diff --git a/applications/PoP/roomtest/bug_list.md b/applications/PoP/roomtest/bug_list.md index a4f7ebd..8ef936a 100644 --- a/applications/PoP/roomtest/bug_list.md +++ b/applications/PoP/roomtest/bug_list.md @@ -451,7 +451,7 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ | 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» | | 13 | недостижима в игре | | | 14 | | | -| 15 | | | +| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` | | 16 | | | | 17 | | | | 18 | недостижима в игре | | diff --git a/applications/PoP/roomtest/hang_variants.png b/applications/PoP/roomtest/hang_variants.png new file mode 100644 index 0000000..d4c177c Binary files /dev/null and b/applications/PoP/roomtest/hang_variants.png differ diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index bd6371a..f214448 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -583,6 +583,12 @@ static void draw_tile(int row, int col) if (sf > 0 && sf < 10 && SPIKES_FRAM_LEFT[sf]) env_b(SPIKES_FRAM_LEFT[sf], x, dmy - 2); } + /* Меч-предмет на полу (тот же draw_tile_anim, case tiles_22_sword): + * chtab_1 (наш pot-атлас), id = (modif==1)+10, низ на draw_main_y-3. + * В оригинале это midtable (рисуется вместе с объектами) — у нас статикой + * в фоне: пока меч лежит, он не анимируется, а Kid и так поверх фона. */ + if (row >= 0 && code == 22) + pot_b((uint8_t)((tile_mod(row, col) == 1) + 10), x, dmy - 3); /* --- draw_tile_fore (seg008.c:690) --- */ if (code == 20) { diff --git a/applications/PoP/roomtest/pop_ctrl.c b/applications/PoP/roomtest/pop_ctrl.c index 10bdc6e..9e435ae 100644 --- a/applications/PoP/roomtest/pop_ctrl.c +++ b/applications/PoP/roomtest/pop_ctrl.c @@ -51,6 +51,7 @@ #define SEQ_49_STAND_UP_FROM_CROUCH 49 #define SEQ_50_CROUCH 50 #define SEQ_79_CROUCH_HOP 79 +#define SEQ_91_PICKUP_SWORD 91 /* поднять меч (pickupsword) */ #define SEQ_STEP_BASE 28 /* +distance(1..14) = seq 29..42 шаг-N */ #define SEQ_39_UNSAFE_STEP 39 /* шаг за кромку (distance 11) */ #define SEQ_44_STEP_ON_EDGE 44 @@ -104,6 +105,22 @@ static void standing_jump(void) seqtbl_offset_char(SEQ_3_STANDING_JUMP); } +/* check_get_item (seg005:061F): решение по тайлам принимает pop_map, здесь + * только исполнение. Триггер в оригинале — Shift (control_shift2) в стойке + * или в приседе: первый раз Kid приседает к предмету, второй — поднимает. + * Возврат 1 = управление обработано (caller делает return). */ +static uint8_t get_item_action(void) +{ + uint8_t a = pop_get_item_action(); + if (a == 1) { crouch(); return 1; } + if (a == 2) { + control_shift2 = CONTROL_IGNORE; /* do_pickup: без авто-повтора */ + seqtbl_offset_char(SEQ_91_PICKUP_SWORD); + return 1; + } + return 0; +} + static void run_jump(void) { /* Оригинал выравнивает Kid по краю пола (нужны tile-запросы) — это @@ -191,6 +208,8 @@ static void safe_step(void) /* ---- control_* обработчики состояний ------------------------------ */ static void control_crouched(void) { + /* Shift в приседе — поднять предмет (seg005 control_crouched). */ + if (control_shift2 == CONTROL_HELD && get_item_action()) return; if (control_y != CONTROL_HELD_DOWN) { seqtbl_offset_char(SEQ_49_STAND_UP_FROM_CROUCH); } else if (control_forward == CONTROL_HELD) { @@ -201,6 +220,11 @@ static void control_crouched(void) static void control_standing(void) { + /* Shift в стойке над предметом — присесть к нему (seg005:0358, первая + * строка control_standing). Идёт ДО остальных shift-веток. */ + if (control_shift2 == CONTROL_HELD && control_shift == CONTROL_HELD && + get_item_action()) + return; /* Kid charid_0, без меча: остаётся нижняя часть control_standing. */ if (control_shift == CONTROL_HELD) { if (control_backward == CONTROL_HELD) back_pressed(); diff --git a/applications/PoP/roomtest/pop_kid.c b/applications/PoP/roomtest/pop_kid.c index c725e94..261dab7 100644 --- a/applications/PoP/roomtest/pop_kid.c +++ b/applications/PoP/roomtest/pop_kid.c @@ -125,7 +125,10 @@ void play_seq(void) break; case 0xF1: /* END_LEVEL */ break; - case 0xF3: /* GET_ITEM — потребить 1 байт */ + case 0xF3: /* GET_ITEM: аргумент 1 — применить эффект предмета + * (порт play_seq -> proc_get_object, seg006) */ + if (SEQ(Kid.curr_seq) == 1) + pop_proc_get_object(); Kid.curr_seq++; break; case 0xF6: /* DIE nop */ diff --git a/applications/PoP/roomtest/pop_map.c b/applications/PoP/roomtest/pop_map.c index 819e92d..0f1a9ca 100644 --- a/applications/PoP/roomtest/pop_map.c +++ b/applications/PoP/roomtest/pop_map.c @@ -69,6 +69,9 @@ #define SEQ_73_CLIMB_UP_TO_GATE 73 /* подъём к закрытой калитке */ #define SEQ_51_SPIKED 51 /* напороться на пики (смерть) */ +#define TILE_FLOOR 1 +#define FRAME_109_CROUCH 109 + /* Кадры анимации для check_spiked (seg006:0658). */ #define FRAME_7_RUN 7 #define FRAME_26_STANDJUMP_LAND 26 @@ -763,6 +766,65 @@ uint8_t pop_down_action(void) return 0; } +/* ---- Предметы: меч (seg005 check_get_item/get_item + seg006 do_pickup/ + * proc_get_object) --------------------------------------------------- * + * pop_have_sword — порт глобала have_sword: «меч у Кида». Боевой режим + * (стойка с мечом, бой) НЕ реализован и здесь не нужен: сиквенс подъёма + * seq_91 сам заканчивается убиранием меча в ножны (кадры 230..240 sheathe) + * и возвратом в обычную стойку — как в оригинале до встречи со стражем. */ +uint8_t pop_have_sword; +uint8_t pop_item_taken; /* 0 = нет; иначе tilepos+1 поднятого */ +static int8_t pickup_obj_type; /* -1 = меч (порт pickup_obj_type) */ + +/* do_pickup (seg006:1671): стереть предмет с карты (тайл → пол) и запомнить + * тип; сам эффект применяет опкод GET_ITEM внутри сиквенса. */ +static void do_pickup(int8_t obj_type, uint8_t tilepos) +{ + pickup_obj_type = obj_type; + g_fg[tilepos] = TILE_FLOOR; + pop_item_taken = (uint8_t)(tilepos + 1); +} + +/* proc_get_object (seg006:16CB): эффект поднятого предмета. Зовётся из + * play_seq по опкоду SEQ_GET_ITEM с аргументом 1 (pop_kid). */ +void pop_proc_get_object(void) +{ + if (pickup_obj_type == -1) + pop_have_sword = 1; /* в оригинале have_sword = -1 */ + pickup_obj_type = 0; /* зелья (типы 1..6) не портированы */ +} + +/* check_get_item + get_item (seg005:061F/073E). Возврат: 0 — предмета нет + * (управление идёт обычной веткой); 1 — присесть (caller зовёт crouch()); + * 2 — поднять меч (caller ставит seq_91). */ +uint8_t pop_get_item_action(void) +{ + uint8_t t; + int8_t icol; + t = get_tile_at_char(); + if (t == TILE_POTION || t == TILE_SWORD) { /* стоим НА предмете */ + if (!tile_is_floor(get_tile_behind_char())) return 0; + Kid.x = (uint8_t)char_dx_forward(-TILE_SIZEX); /* отступить на тайл */ + determine_col(); + } + t = get_tile_infrontof_char(); /* заодно ставит g_infrontx */ + if (t != TILE_POTION && t != TILE_SWORD) return 0; + icol = g_infrontx; + if (icol < 0 || icol > 9) return 0; /* предмет за швом — не наш */ + if (Kid.frame != FRAME_109_CROUCH) { + /* Ещё стоим: подровняться к предмету и присесть. */ + int d = pop_edge_distance(); /* ставит g_edge_type */ + if (g_edge_type != EDGE_FLOOR) + Kid.x = (uint8_t)char_dx_forward((int8_t)d); + if (Kid.direction >= 0) + Kid.x = (uint8_t)char_dx_forward((int8_t)((t == TILE_POTION) - 2)); + return 1; + } + if (t != TILE_SWORD) return 0; /* питьё зелий не портировано */ + do_pickup(-1, (uint8_t)(Kid.curr_row * 10 + icol)); + return 2; +} + /* ---- Диспетчер ----------------------------------------------------- */ static void check_on_floor(void) { diff --git a/applications/PoP/roomtest/pop_map.h b/applications/PoP/roomtest/pop_map.h index 5b0a5e2..317d4ae 100644 --- a/applications/PoP/roomtest/pop_map.h +++ b/applications/PoP/roomtest/pop_map.h @@ -37,6 +37,15 @@ extern uint8_t pop_kid_dead; extern uint8_t hitp_curr; void pop_kid_hp_reset(void); +/* Предметы (seg005 check_get_item/get_item, seg006 do_pickup/proc_get_object). + * pop_have_sword — «меч у Кида» (порт have_sword). pop_item_taken — 0 или + * tilepos+1 только что поднятого предмета: вызывающий обязан запечь тайл + * (он стал полом) на ОБЕИХ страницах и записать override комнаты. */ +extern uint8_t pop_have_sword; +extern uint8_t pop_item_taken; +uint8_t pop_get_item_action(void); /* 0 нет / 1 присесть / 2 поднять меч */ +void pop_proc_get_object(void); /* эффект — из play_seq (опкод GET_ITEM) */ + /* Уход из комнаты ВБОК/ВВЕРХ: 0=нет; 1=left,2=right,3=up (= side+1 для * pop_room_link). Координаты Kid уже репроецированы (x∓140 / y+189). * Приложение переключает комнату по соответствующей связи и снимает флаг. */ diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index 967d085..4f91d1b 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -85,9 +85,13 @@ static uint8_t room_label_redraw; * в docs/gates_spikes_plan.md, P0.) */ #define TILE_DEBRIS 0x0E #define TILE_EMPTY 0x00 +#define TILE_FLOOR 0x01 #define OVR_MAX 8 static uint8_t ovr_room[OVR_MAX], ovr_pos[OVR_MAX], ovr_tile[OVR_MAX], ovr_cnt; +/* Поднятый предмет (меч): tilepos и счётчик «запечь пол» по страницам. */ +static uint8_t item_pos, item_bake; + static void add_tile_override(uint8_t room, uint8_t pos, uint8_t tile) { uint8_t i; @@ -378,6 +382,16 @@ int main(void) add_tile_override(cur_room, (uint8_t)(pop_debris_at - 1), TILE_DEBRIS); pop_debris_at = 0; } + if (pop_item_taken) { /* предмет поднят → тайл стал полом */ + item_pos = (uint8_t)(pop_item_taken - 1); + item_bake = 2; /* запечь на ОБЕИХ страницах */ + add_tile_override(cur_room, item_pos, TILE_FLOOR); + pop_item_taken = 0; + } + if (item_bake) { /* стереть спрайт меча из фона страницы */ + pop_floor_bake(item_pos / 10, item_pos % 10); + item_bake--; + } io_border = 4; /* зеленый */ update_kid_render_dx(); /* straddle: смещение спрайта, если kid_room≠cur_room */ kid_draw(); /* нарисовать новый кадр (детект выхода за поле) */