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:
@@ -90,6 +90,7 @@
|
||||
#define SEQ_68_CLIMB_DOWN 68 /* спуск через зацеп (не приседание) */
|
||||
#define SEQ_73_CLIMB_UP_TO_GATE 73 /* подъём к закрытой калитке */
|
||||
#define SEQ_51_SPIKED 51 /* напороться на пики (смерть) */
|
||||
#define SEQ_54_CHOMPED 54 /* попасть в челюсти чомпера (смерть) */
|
||||
#define SEQ_22_CRUSHED 22 /* разбиться при падении (смерть) */
|
||||
#define SEQ_63_ACTIVE_AFTER_FALL 63 /* стойка С МЕЧОМ после приземления */
|
||||
#define SEQ_65_BUMP_FWD_SWORD 65 /* стража прижали к стене (с мечом) */
|
||||
@@ -99,6 +100,7 @@
|
||||
|
||||
#define TILE_FLOOR 1
|
||||
#define FRAME_109_CROUCH 109
|
||||
#define FRAME_178_CHOMPED 178
|
||||
|
||||
/* Кадры анимации для check_spiked (seg006:0658). */
|
||||
#define FRAME_7_RUN 7
|
||||
@@ -521,6 +523,7 @@ static void land(void)
|
||||
/* к краю пола — чуть назад (как оригинал) */
|
||||
if (!tile_is_floor(get_tile_infrontof_char()) && distance_to_edge_weight() < 3)
|
||||
Char.x = (uint8_t)char_dx_forward(-3);
|
||||
pop_start_chompers(Char.room, Char.curr_row); /* seg005:0148 */
|
||||
/* Урон от падения (seg005:173) — ветвление по ПЕРСОНАЖУ, а не только по
|
||||
* высоте. Порт построчно:
|
||||
* 1 этаж (fall_y < 22): не-Кид (charid >= guard) ИЛИ меч вынут →
|
||||
@@ -575,7 +578,7 @@ static void start_fall(void)
|
||||
* control_with_sword разбирает кадры падения как боевые. */
|
||||
Char.sword = SWORD_0_SHEATHED;
|
||||
inc_curr_row();
|
||||
/* start_chompers() — чомперов ещё нет (L3-CHOMP). */
|
||||
pop_start_chompers(Char.room, Char.curr_row); /* seg006:08C1 */
|
||||
/* seg006:1044 start_fall: frame 9 -> seq_7, 13 -> seq_19 (лишний dx(1)) */
|
||||
if (frame == 13) seq = SEQ_19_FALL;
|
||||
else if (frame == 26) seq = SEQ_18_FALL_STANDJUMP;
|
||||
@@ -2107,6 +2110,61 @@ static void check_spike_below(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* chomped (seg004:07BF): персонажа перемололо. Тайл помечается кровью
|
||||
* (0x80 в modif), персонаж выравнивается по колонке чомпера, теряет все HP
|
||||
* и уходит в seq_54. Условие «кадр ещё не 178» — чтобы не перезапускать
|
||||
* последовательность каждый кадр, пока труп лежит в челюстях.
|
||||
*
|
||||
* Кровь пишется ДАЖЕ у скелета: SDLPoP чинит это опцией
|
||||
* fix_skeleton_chomper_blood, но в ВАНИЛИ она есть, и мы держим ваниль. */
|
||||
static void chomped(uint8_t tilepos, int8_t col)
|
||||
{
|
||||
pop_trob_modif(g_room)[tilepos] |= 0x80;
|
||||
if (Char.frame == FRAME_178_CHOMPED) return;
|
||||
Char.x = (uint8_t)(pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX);
|
||||
Char.x = (uint8_t)char_dx_forward(7 - !Char.direction);
|
||||
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||
pop_take_hp(100); /* (TODO: sound_46_chomped) */
|
||||
pop_char_set_seq(SEQ_54_CHOMPED);
|
||||
play_seq();
|
||||
if (Char.charid == CHARID_0_KID) pop_kid_dead = 1;
|
||||
}
|
||||
|
||||
/* check_chomped_kid (seg004:076B): по колонкам ряда — там, где персонаж
|
||||
* перекрывает тайл ОБЕИМИ гранями (у оригинала curr_row_coll_flags == 0xFF,
|
||||
* у нас оба бита) и этот тайл — СОМКНУТЫЙ чомпер (modif & 0x7F == 2),
|
||||
* персонажа перемалывает. */
|
||||
static void check_chomped_flags(const uint8_t *flags)
|
||||
{
|
||||
int8_t row = Char.curr_row, col;
|
||||
uint8_t tp;
|
||||
if (row < 0 || row > 2) return;
|
||||
for (col = 0; col <= 9; col++) {
|
||||
if (flags[COLL_IDX(col)] != 3) continue;
|
||||
if (get_tile(col, row) != TILE_CHOMPER) continue;
|
||||
tp = (uint8_t)(row * 10 + col);
|
||||
if ((pop_trob_modif(g_room)[tp] & 0x7F) == 2) chomped(tp, col);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_chomped_kid(void) { check_chomped_flags(coll_curr); }
|
||||
|
||||
/* check_chomped_guard (seg004:0989) — у оригинала ОТДЕЛЬНОЕ тело: страж не
|
||||
* проходит через check_collisions, поэтому там перекрытие считается прямо по
|
||||
* координатам граней (check_chomped_here). У нас та же формула уже живёт в
|
||||
* get_row_collision_data, поэтому считаем флаги ряда во ВРЕМЕННЫЙ массив и
|
||||
* идём общим телом. Именно во временный: coll_curr/above/below — это кадр
|
||||
* КИДА, из них move_coll_to_prev берёт «прошлые» флаги на следующем кадре, и
|
||||
* затирание сломало бы ему бамп. */
|
||||
static void check_chomped_guard(void)
|
||||
{
|
||||
uint8_t flags[COLL_N];
|
||||
if (Char.curr_row < 0 || Char.curr_row > 2) return;
|
||||
set_char_collision();
|
||||
get_row_collision_data(Char.curr_row, flags);
|
||||
check_chomped_flags(flags);
|
||||
}
|
||||
|
||||
/* ---- Кадровая физика: одна и та же над Char, два входа --------------- *
|
||||
* Оригинал держит физику ОДНИМ набором функций над Char, а кто именно в
|
||||
* Char — решают окна loadkid/savekid и loadshad/saveshad (seg006:809).
|
||||
@@ -2126,6 +2184,7 @@ static void kid_phys(void)
|
||||
check_press(); /* seg006: стойка/пробой loose → make_loose_fall */
|
||||
check_spike_below(); /* seg006: над колонкой с пиками → выдвинуть пики */
|
||||
check_spiked(); /* seg006: напоролся на вредные пики → смерть */
|
||||
check_chomped_kid(); /* seg004: перемололо в сомкнутых челюстях → смерть */
|
||||
if (pop_kid_dead) return; /* только что умер — без leave/knock в этот кадр */
|
||||
pop_check_knock(); /* seg003: сотрясение приземления → тряска loose в ряду */
|
||||
/* exit_room_timer (seg002 exit_room): 2 кадра ПОСЛЕ входа в комнату любой
|
||||
@@ -2144,7 +2203,7 @@ static void kid_phys(void)
|
||||
/* play_guard_frame (seg000:1252..1266). Чего у стража НЕТ по сравнению с
|
||||
* Кидом: bump_into_opponent, check_collisions/check_bumped (у него свой
|
||||
* check_guard_bumped), check_gate_push, check_knock и весь уход из комнаты.
|
||||
* check_chomped_guard появится вместе с чомперами (L3-CHOMP). */
|
||||
* check_chomped_guard — то же тело, что у Кида (у нас check_chomped). */
|
||||
static void guard_phys(void)
|
||||
{
|
||||
fall_accel();
|
||||
@@ -2155,6 +2214,7 @@ static void guard_phys(void)
|
||||
check_press(); /* страж тоже давит кнопки и проваливает loose */
|
||||
check_spike_below();
|
||||
check_spiked();
|
||||
check_chomped_guard();/* seg000:1265 */
|
||||
}
|
||||
|
||||
/* Физика КИДА — своё окно Char: главный цикл зовёт нас вне окон, а физика
|
||||
|
||||
Reference in New Issue
Block a user