L2: переход между уровнями и игра уровня 2
Порт levels_plan.md §2 (шаг 1) — машинерия смены уровня целиком. - Номер уровня стал состоянием: pop_current_level (порт current_level), pop_next_level больше не флаг, а НОМЕР; главный цикл срабатывает по расхождению next != current (порт play_level_2, seg003:0386). - pop_level_load_num(n): LEVELS\res20NN.bin (fallback a:\), старая EMM-страница отпускается только после успешной загрузки новой. На диск кладутся все 15 уровней (34 КБ). - Потабличные различия (data.h:840..848) — таблицы по 16 в pop_level.c: tbl_entry_pose, tbl_guard_hp (HP стража ушло из хардкода 3), tbl_guard_type (−1 = стражей на уровне нет), tbl_level_type. - find_start_level_door (seg003:02E6): на уровне 2 стартовый тайл — правая половина двери уровня, без modif=43 + add_trob(...,3) Кид материализуется внутри глухой створки. Тип 3 = дверь захлопывается за спиной, как в оригинале. - HP через уровень: hitp_beg_lev (seg003) — рестарт уровня откатывает HP к нему, пройденный уровень подтягивает его к hitp_max. Заодно большая склянка add_life (тип зелья 2: +1 к потолку HP до 10) — на уровне 2 она есть, комната 20. - have_sword = level >= 2 (play_level, seg003:106). - Чит Shift+L — следующий уровень (seg000:698). Проверено в MAME: уровень 1 → Shift+L → уровень 2 (комната 5, новые для нас тайлы 8/9 «большая колонна» на месте, дверь захлопнута, HP 3) → влево в комнату 4 со стражем → Shift+L → уровень 3. Вход в стартовую дверь уровня 2 по Up не срабатывает — как и должно (start_room-гард). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,10 +90,7 @@ static void kbd_idle(void)
|
||||
#define ROOM_1 1
|
||||
#define LINK_DOWN 3 /* сторона links: 0=L,1=R,2=U,3=D */
|
||||
|
||||
/* Поза входа на уровне (tbl_entry_pose, data.h:848): у уровня 1 — падение
|
||||
* внутрь плюс нажатие кнопки. Уровень пока один, поэтому константа; когда
|
||||
* появятся уровни 2+ (levels_plan.md), это станет таблицей в pop_level. */
|
||||
#define LEVEL_ENTRY_POSE ENTRY_POSE_FALL
|
||||
#define FIRST_LEVEL 1 /* с какого уровня начинается игра */
|
||||
|
||||
#define RESPAWN_DELAY 400 /* авто-респавн (фолбэк), если не нажали ↑ */
|
||||
#define RESPAWN_SETTLE 20 /* тиков до приёма ↑ (труп успел лечь) */
|
||||
@@ -337,16 +334,38 @@ static void enter_room(uint8_t room)
|
||||
* это оно захлопывает решётку за спиной в первой комнате;
|
||||
* 2 (уровень 13) — вбегание (seq_84_run);
|
||||
* иначе — разворот на месте (seq_5_turn).
|
||||
* Таблица индексируется НОМЕРОМ УРОВНЯ, поэтому появится в pop_level, когда
|
||||
* дойдём до уровней 2+ (levels_plan.md); пока уровень один. */
|
||||
* Таблица индексируется НОМЕРОМ УРОВНЯ — pop_level_entry_pose(). */
|
||||
#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] */
|
||||
#define TILE_LEVELDOOR_LEFT 16 /* tiles_16_level_door_left */
|
||||
|
||||
static void pop_start_level(uint8_t entry_pose)
|
||||
/* find_start_level_door (seg003:02E6): дверь уровня В СТАРТОВОЙ КОМНАТЕ —
|
||||
* это дверь, через которую Кид ВОШЁЛ, поэтому она открыта полностью
|
||||
* (modif = 43) и заводится trob'ом типа 3 (start_level_door, seg007:0D72).
|
||||
* Без этого на уровне 2 Кид материализуется внутри глухой закрытой створки:
|
||||
* его стартовый тайл (комната 5, ряд 1, колонка 3) — правая половина
|
||||
* той самой двери. */
|
||||
static void find_start_level_door(uint8_t room)
|
||||
{
|
||||
uint8_t *m = pop_trob_modif(room);
|
||||
uint8_t tp;
|
||||
if (!m) return;
|
||||
/* Батч-доступ к странице уровня тут не нужен: функция отрабатывает раз
|
||||
* за уровень, а pop_add_trob сам лезет в W0. */
|
||||
for (tp = 0; tp < 30; tp++) {
|
||||
if (pop_level_tile(room, tp) != TILE_LEVELDOOR_LEFT) continue;
|
||||
m[tp] = 43; /* створка поднята полностью */
|
||||
pop_add_trob(room, tp, 3); /* ...и тут же поедет ВНИЗ (type>=3) */
|
||||
}
|
||||
}
|
||||
|
||||
static void pop_start_level(void)
|
||||
{
|
||||
uint8_t entry_pose = pop_level_entry_pose(pop_current_level);
|
||||
uint8_t start_room = pop_level_start_room();
|
||||
uint8_t pos = pop_level_start_pos();
|
||||
int8_t dir = (int8_t)~pop_level_start_dir();
|
||||
uint8_t seq = SEQ_TURN;
|
||||
@@ -375,10 +394,17 @@ static void pop_start_level(uint8_t entry_pose)
|
||||
seq = SEQ_RUN;
|
||||
}
|
||||
|
||||
enter_room(pop_level_start_room());
|
||||
/* Тоже ДО отрисовки комнаты: дверь, через которую вошли, обязана быть
|
||||
* открыта уже в первом кадре (в оригинале find_start_level_door стоит
|
||||
* между do_startpos и draw_level_first). */
|
||||
find_start_level_door(start_room);
|
||||
|
||||
enter_room(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 */
|
||||
/* play_level (seg003:106): have_sword = level >= have_sword_from_level
|
||||
* (=2) — начиная со второго уровня меч у Кида уже есть. */
|
||||
pop_have_sword = (uint8_t)(pop_current_level >= 2);
|
||||
pop_kid_hp_reset();
|
||||
}
|
||||
|
||||
@@ -392,7 +418,7 @@ int main(void)
|
||||
#ifdef ROOMNAV
|
||||
uint8_t nav_prev = 0;
|
||||
#endif
|
||||
uint8_t cheat_prev = 0, immo_prev = 0, sword_prev = 0;
|
||||
uint8_t cheat_prev = 0, immo_prev = 0, sword_prev = 0, nlvl_prev = 0;
|
||||
|
||||
if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */
|
||||
puts("pop_bg_load failed");
|
||||
@@ -410,11 +436,12 @@ int main(void)
|
||||
puts("kid atlas load failed");
|
||||
return 1;
|
||||
}
|
||||
if (pop_level_load("LEVELS\\res2001.bin") != 0 && /* сырой уровень 1 в EMM-страницу */
|
||||
pop_level_load("a:\\res2001.bin") != 0) {
|
||||
if (pop_level_load_num(FIRST_LEVEL) != 0) { /* сырой уровень в EMM-страницу */
|
||||
puts("level load failed");
|
||||
return 1;
|
||||
}
|
||||
pop_next_level = pop_current_level; /* current_level = next_level (load_lev_spr) */
|
||||
pop_kid_hp_newgame(); /* init_game: hitp_beg_lev = start_hitp */
|
||||
|
||||
initgraph();
|
||||
gfx_set_visible_page(0);
|
||||
@@ -431,7 +458,7 @@ int main(void)
|
||||
/* Старт — ПО ДАННЫМ УРОВНЯ (комната/тайл/направление/поза входа), см.
|
||||
* pop_start_level. Управление: ←/→ бег, ↓ присед, ↑+→ прыжок с места,
|
||||
* Shift шаг, ESC — выход. Карта коллизии задана внутри enter_room. */
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
pop_start_level();
|
||||
gfx_set_draw_page(0);
|
||||
kid_draw();
|
||||
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
|
||||
@@ -472,16 +499,27 @@ int main(void)
|
||||
* I — бессмертие Кида (наш отладочный, в оригинале его нет:
|
||||
* иначе боёвку проверять получается ровно три раза);
|
||||
* S — выдать меч, не бегая за ним в комнату 15. Штатно меч
|
||||
* берётся с пола (pop_have_sword ставит подбор). */
|
||||
* берётся с пола (pop_have_sword ставит подбор);
|
||||
* Shift+L — следующий уровень (seg000:698, ветка next_level =
|
||||
* current_level + 1). Единственный вменяемый способ
|
||||
* тестировать уровни 2..15. NB: L без Shift — отладочный
|
||||
* «осторожный шаг вправо» (pop_ctrl KBD_DBG_STEPR); с
|
||||
* Shift шаг тоже пройдёт, но уровень тут же сменится, так
|
||||
* что конфликта на практике нет. */
|
||||
uint8_t k = kbd_raw_down(KBD_CHEAT_KILL);
|
||||
uint8_t i = kbd_raw_down(KBD_CHEAT_IMMO);
|
||||
uint8_t s = kbd_raw_down(KBD_CHEAT_SWORD);
|
||||
uint8_t nl = (uint8_t)(kbd_raw_down(KBD_CHEAT_NEXTLVL) &&
|
||||
(kbd_raw_down(KBD_LSHIFT) || kbd_raw_down(KBD_RSHIFT)));
|
||||
if (pop_cheats && k && !cheat_prev) pop_guard_kill();
|
||||
if (pop_cheats && i && !immo_prev) pop_immortal = !pop_immortal;
|
||||
if (pop_cheats && s && !sword_prev) pop_have_sword = 1;
|
||||
if (pop_cheats && nl && !nlvl_prev && pop_current_level < POP_LAST_LEVEL)
|
||||
pop_next_level = (uint8_t)(pop_current_level + 1);
|
||||
cheat_prev = k;
|
||||
immo_prev = i;
|
||||
sword_prev = s;
|
||||
nlvl_prev = nl;
|
||||
}
|
||||
if (frozen) {
|
||||
PROF(0); /* чёрный: ждём vsync */
|
||||
@@ -545,7 +583,7 @@ int main(void)
|
||||
enter_room(down); /* координаты Kid уже репроецированы pop_map */
|
||||
gfx_set_draw_page(back); /* вернуть текущую back для kid_draw */
|
||||
} else { /* выпал из уровня (нет комнаты снизу) — рестарт */
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
pop_start_level();
|
||||
gfx_set_draw_page(back);
|
||||
}
|
||||
} else if (pop_leave_dir) { /* L3: уход вбок/вверх (координаты уже репроецированы) */
|
||||
@@ -581,15 +619,25 @@ 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);
|
||||
if (pop_next_level != pop_current_level) {
|
||||
/* Порт play_level_2 (seg003:0386..0410) + play_level (seg003:81):
|
||||
* цикл уровня кончается, когда next_level разошёлся с
|
||||
* current_level (END_LEVEL после seq_70 в двери уровня либо чит
|
||||
* Shift+L), и play_level грузит следующий.
|
||||
*
|
||||
* HP переносится: hitp_beg_lev = hitp_max ДО загрузки — так
|
||||
* поднятая на уровне «жизнь» (большая склянка) переезжает
|
||||
* дальше, а урон — нет (do_startpos дольёт до потолка). */
|
||||
hitp_beg_lev = hitp_max;
|
||||
if (pop_level_load_num(pop_next_level) != 0) {
|
||||
pop_next_level = pop_current_level; /* файла нет — остаёмся */
|
||||
} else {
|
||||
/* Всё состояние прошлого уровня сносит сам pop_start_level:
|
||||
* pop_trob_reset обнуляет room_seen (модификаторы комнат
|
||||
* перечитаются из НОВОЙ страницы), тайлы/стражи приезжают
|
||||
* из неё же, loose чистит enter_room. */
|
||||
pop_start_level();
|
||||
}
|
||||
gfx_set_draw_page(back);
|
||||
}
|
||||
if (pop_kid_dead) { /* напоролся на пики: труп лежит; респавн по ↑ */
|
||||
@@ -597,7 +645,7 @@ int main(void)
|
||||
if (dead_frames > RESPAWN_SETTLE &&
|
||||
(kbd_raw_down(KBD_UP) || dead_frames > RESPAWN_DELAY)) {
|
||||
dead_frames = 0;
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
pop_start_level();
|
||||
gfx_set_draw_page(back);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user