L3-CHOMP: чомперы — анимация, отрисовка и смерть в челюстях
Порт SDLPoP: animate_chomper / start_chompers / start_anim_chomper / next_chomper_timing (seg007) -> pop_trob.c; draw_tile_anim + draw_tile_fore, ветка tiles_18_chomper (seg008) -> pop_room.c (низ/кровь/верх, backtable) и pop_bg.c (передний слой); check_chomped_kid / check_chomped_guard / chomped (seg004) -> pop_map.c. Состояние — в room_modif, как у пик и ворот: младшие 7 бит фаза 1..N, старший бит «перемололо кого-то» (кровь остаётся на тайле навсегда). Номер позы chomper_fram1 и передние куски лежат в РЕЗИДЕНТЕ (pop_tile.c): их читают обе половины слоя фона, а const-таблица банка из чужого банка не видна. start_chompers зовётся там же, где в оригинале: SEQ_UP/SEQ_DOWN в play_seq (pop_kid.c), start_fall и land (pop_map.c), вход в комнату (roomtest.c). Поэтому чомперы щёлкают только пока персонаж в ИХ ряду — так в оригинале. check_chomped_guard у оригинала отдельное тело (страж не проходит через check_collisions). У нас та же формула уже есть в get_row_collision_data, поэтому флаги ряда считаются во ВРЕМЕННЫЙ массив: coll_curr/above/below — это кадр Кида, из них move_coll_to_prev берёт прошлые флаги, затирание сломало бы ему бамп. Период смыкания — POP_CHOMPER_SPEED в pop_tune.h (15, как в оригинале). Заодно: устаревшая заглушка pop_fore_set_clip в tests-host была __banked, хотя функция давно переехала в резидент — всплыло при пересборке. Ассеты уже были упакованы (pop_pack_bg.py, 2026-08-07). Банк 6: 20.0 %, банк 7: 39.7 %, банк 3: 53.8 %. tests-host зелёные (5 наборов). Зубья в MAME рисуются; анимация и смерть — на ручной приёмке.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
* нельзя звать из __banked-кода. */
|
||||
#include "pop_bg.h"
|
||||
#include "pop_guard.h" /* is_guard_notice — нажатая кнопка слышна стражу */
|
||||
#include "pop_kid.h" /* Kid.curr_row/alive — условие остановки чомпера */
|
||||
#include "pop_tune.h" /* POP_DBG_GATE_HOLD — отладочное растягивание окна */
|
||||
|
||||
/* Коды тайлов (fg & 0x1F), которые анимируются. */
|
||||
@@ -27,6 +28,7 @@
|
||||
#define TILE_LOOSE 0x0B /* проваливающийся пол (фаза тряски в modif) */
|
||||
#define TILE_SWORD 0x16 /* меч-предмет на полу */
|
||||
#define TILE_LEVELDOOR 0x10 /* левая половина двери уровня (на ней modif) */
|
||||
#define TILE_CHOMP 0x12 /* чомпер (челюсти) */
|
||||
#define TILE_TORCH 0x13
|
||||
#define TILE_TORCH_DEBRIS 0x1E
|
||||
|
||||
@@ -196,6 +198,75 @@ static uint8_t bubble_next_frame(uint8_t curr)
|
||||
return (next >= 8) ? 1 : next;
|
||||
}
|
||||
|
||||
/* animate_chomper (seg007:0448). Модификатор: младшие 7 бит — фаза 1..N,
|
||||
* старший — «перемололо кого-то» (кровь). Фаза крутится по кругу до
|
||||
* POP_CHOMPER_SPEED (у оригинала 15), то есть челюсти смыкаются раз в N
|
||||
* кадров; фаза 2 — момент удара (в оригинале там звук sound_47).
|
||||
*
|
||||
* Остановка (иначе чомперы щёлкали бы всю игру): если Кид ушёл из этой
|
||||
* комнаты, или ушёл из этого РЯДА, или умер не в этом чомпере — trob
|
||||
* снимается, но только после фазы 6, чтобы челюсти успели разомкнуться.
|
||||
* Запускает их обратно start_chompers на смене ряда. Аргументы room/tp
|
||||
* нужны ровно для этой проверки. */
|
||||
static void animate_chomper(uint8_t *m, int8_t *type,
|
||||
uint8_t room, uint8_t tp, uint8_t drawn_room)
|
||||
{
|
||||
uint8_t blood, frame;
|
||||
if (*type >= 0) {
|
||||
blood = (uint8_t)(*m & 0x80);
|
||||
frame = (uint8_t)((*m & 0x7F) + 1);
|
||||
if (frame > POP_CHOMPER_SPEED) frame = 1;
|
||||
*m = (uint8_t)(blood | frame);
|
||||
if ((room != drawn_room || tp / 10 != Kid.curr_row ||
|
||||
(Kid.alive >= 0 && blood == 0)) && frame >= 6)
|
||||
*type = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* next_chomper_timing (seg007:0F9A): 15,12,9,6,13,10,7,14,11,8 по кругу —
|
||||
* чомперы одного ряда стартуют вразнобой, а не хлопают в унисон. */
|
||||
static uint8_t next_chomper_timing(uint8_t timing)
|
||||
{
|
||||
timing -= 3;
|
||||
if (timing < 6) timing += 10;
|
||||
return timing;
|
||||
}
|
||||
|
||||
/* start_anim_chomper (seg007:08C7): запустить, только если стоит (0) или
|
||||
* уже прошёл фазу размыкания (>=6). */
|
||||
static void start_anim_chomper(uint8_t room, uint8_t tp, uint8_t modifier)
|
||||
{
|
||||
uint8_t *mod = pop_trob_modif(room);
|
||||
uint8_t old = (uint8_t)(mod[tp] & 0x7F);
|
||||
if (old == 0 || old >= 6) {
|
||||
mod[tp] = modifier;
|
||||
pop_add_trob(room, tp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* start_chompers (seg007:0F13): все чомперы РЯДА активного персонажа.
|
||||
* Зовётся отовсюду, где персонаж сменил ряд (SEQ_UP/SEQ_DOWN, start_fall,
|
||||
* приземление) и на входе в комнату. */
|
||||
void pop_start_chompers(uint8_t room, int8_t row) __banked
|
||||
{
|
||||
uint8_t timing = 15, col, tp;
|
||||
uint8_t *mod;
|
||||
if ((uint8_t)row >= 3) return; /* вне комнаты — нечего */
|
||||
mod = pop_trob_modif(room);
|
||||
pop_level_access_begin();
|
||||
for (col = 0; col < 10; col++) {
|
||||
tp = (uint8_t)(row * 10 + col);
|
||||
if ((pop_level_tile_raw(room, tp) & 0x1F) == TILE_CHOMP) {
|
||||
uint8_t m = (uint8_t)(mod[tp] & 0x7F);
|
||||
if (m == 0 || m >= 6) {
|
||||
start_anim_chomper(room, tp, (uint8_t)(timing | (mod[tp] & 0x80)));
|
||||
timing = next_chomper_timing(timing);
|
||||
}
|
||||
}
|
||||
}
|
||||
pop_level_access_end();
|
||||
}
|
||||
|
||||
static void animate_spike(uint8_t *m, int8_t *type)
|
||||
{
|
||||
uint8_t mod = *m;
|
||||
@@ -429,6 +500,9 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
case TILE_SPIKE:
|
||||
animate_spike(&mod[tp], &type);
|
||||
break;
|
||||
case TILE_CHOMP:
|
||||
animate_chomper(&mod[tp], &type, room, tp, cur_room);
|
||||
break;
|
||||
case TILE_OPENER:
|
||||
case TILE_CLOSER:
|
||||
animate_button(mod[tp], &type); /* mod[tp] = индекс LINKLOC кнопки */
|
||||
@@ -490,6 +564,13 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
* страницы: живой trob метит каждый кадр по одной. */
|
||||
pop_set_redraw(tp, POP_RD_SPIKE, (uint8_t)(type < 0 ? 2 : 1));
|
||||
}
|
||||
if (room == cur_room && code == TILE_CHOMP) {
|
||||
/* Кадр меняется каждый тик, пока trob жив — как у пик, метим
|
||||
* текущую страницу; последний кадр (челюсти встали) — обе,
|
||||
* иначе на второй странице дабл-буфера застынет предыдущая поза
|
||||
* и чомпер дрожит через кадр. */
|
||||
pop_set_redraw(tp, POP_RD_CHOMP, (uint8_t)(type < 0 ? 2 : 1));
|
||||
}
|
||||
if (room == cur_room && code == TILE_GATE) {
|
||||
/* draw_trob (seg007:01E6), которым заканчивается animate_door:
|
||||
* пока trob жив — решётка едет каждый кадр, помечаем ТЕКУЩУЮ
|
||||
|
||||
Reference in New Issue
Block a user