PoP roomtest: полный порт bumped (seg004) — прижатие Y к полу + hardbump

Комната 5, прыжок в решётку: Kid оставался стоять на 6 пикселей выше
пола и без приземления-приседания.  Шесть пикселей — это dy(-6) кадра
frame_25_standing_jump_10: удар обрывал standjump ровно между кадрами
25 и 26, парный dy(+6) не выполнялся.

От оригинального bumped() у нас был портирован только хвост (выровнять
X к грани + seq_47_bump).  Добавлены недостающие ветки:

- bumped()      — исход удара выбирается по тайлу, НА КОТОРОМ персонаж
                  оказался после отжатия (сквозь стену/верх двери это
                  соседняя клетка, для ворот/зеркала — сама клетка);
- bumped_floor  — прижимает Y к y_land, ветка fall_y>=22 (только отжать
                  на 5) и выбор сиквенса по кадру: 24/25/40..42/102..106
                  -> seq_46_hardbump (отскок с приседанием), иначе 47;
- bumped_fall   — удар выше пола на 15+ px (беззнаковое сравнение
                  оригинала) или не над полом: seq_45, в свободном
                  падении — только гашение fall_x.

Гард «уже в отскоке — не рестартить» (наш, от edge-триггера коллизий)
перенесён внутрь bumped_floor: выравнивание X/Y идёт всегда, рестарта
анимации нет.  Проверено в MAME.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 14:23:10 +03:00
parent fc69316c2c
commit 517d225091
2 changed files with 71 additions and 9 deletions
+1 -1
View File
@@ -431,7 +431,7 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
| 2 | | | | 2 | | |
| 3 | | | | 3 | | |
| 4 | | | | 4 | | |
| 5 | | | | 5 | пофикшено | прыжок в решётку: Kid оставался стоять на 6 px ВЫШЕ пола и без приземления-приседания — от `bumped()` (seg004) был портирован только хвост (`seq_47`), не хватало `bumped_floor` (прижатие Y к полу + `seq_46_hardbump` на кадрах прыжка 24/25/40..42/102..106) и `bumped_fall` |
| 6 | | | | 6 | | |
| 7 | | | | 7 | | |
| 8 | | | | 8 | | |
+70 -8
View File
@@ -50,6 +50,8 @@
#define SEQ_19_FALL 19 /* stepfall2 (run frames 9/13) */ #define SEQ_19_FALL 19 /* stepfall2 (run frames 9/13) */
#define SEQ_20_MEDIUM_LAND 20 #define SEQ_20_MEDIUM_LAND 20
#define SEQ_21_FALL_RUNJUMP 21 #define SEQ_21_FALL_RUNJUMP 21
#define SEQ_45_BUMPFALL 45 /* удар В ВОЗДУХЕ: отскок и падение */
#define SEQ_46_HARDBUMP 46 /* удар после прыжка: отскок + ПРИСЕДАНИЕ */
#define SEQ_47_BUMP 47 /* удар в стену: dx(-4) отскок + 50-52 + stand */ #define SEQ_47_BUMP 47 /* удар в стену: dx(-4) отскок + 50-52 + stand */
/* K4 — зацеп/вис/подтягивание/спуск */ /* K4 — зацеп/вис/подтягивание/спуск */
#define SEQ_8_GRAB_STRAIGHT 8 /* прыжок вверх и зацеп (прямо) */ #define SEQ_8_GRAB_STRAIGHT 8 /* прыжок вверх и зацеп (прямо) */
@@ -762,11 +764,58 @@ static void check_action(void)
} }
} }
/* bumped_fall (seg004:0522): удар, когда под персонажем НЕ пол ИЛИ он высоко
* над полом — отскок назад и падение (seq_45). В свободном падении сиквенс
* не меняем, только гасим горизонтальную составляющую. */
static void bumped_fall(void)
{
Kid.x = (uint8_t)char_dx_forward(-4);
if (Kid.action == ACT_FREEFALL) {
Kid.fall_x = 0;
} else {
kid_set_seq(SEQ_45_BUMPFALL); /* (TODO: bumped_sound) */
play_seq();
}
}
/* bumped_floor (seg004:053B): удар о стену, когда под персонажем ПОЛ.
* Портируется целиком — раньше у нас был только хвост (seq_47), из-за чего
* терялись ДВЕ вещи: прижатие Y к полу и «жёсткий» вариант удара. Симптом
* (комната 5, прыжок в решётку): standjump бампится на кадре 25 — том самом,
* где сиквенс уже сделал dy(−6), а парного dy(+6) ещё не было, — и Kid
* оставался стоять на 6 пикселей ВЫШЕ пола, без приземления-приседания. */
static void bumped_floor(void)
{
uint8_t frame;
/* Оригинал сравнивает БЕЗЗНАКОВО: если персонаж НИЖЕ уровня пола,
* разность заворачивается в большое число и тоже даёт «падать». */
if ((uint16_t)(y_land[Kid.curr_row + 1] - (int16_t)Kid.y) >= 15) {
bumped_fall(); /* высоко над полом — в воздухе */
return;
}
Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; /* ПРИЖАТЬ к полу */
if (Kid.fall_y >= 22) { /* влетел быстро — только отжать */
Kid.x = (uint8_t)char_dx_forward(-5);
return;
}
Kid.fall_y = 0;
if (Kid.action == ACT_BUMPED) return; /* уже в отскоке — не рестартить */
frame = Kid.frame;
/* Кадры прыжков и начала падения — «жёсткий» удар с приседанием
* (24/25 = standjump, 40..42 = runjump, 102..106 = start_fall). */
if (frame == 24 || frame == 25 || (frame >= 40 && frame < 43) ||
(frame >= 102 && frame < 107))
kid_set_seq(SEQ_46_HARDBUMP);
else
kid_set_seq(SEQ_47_BUMP); /* (TODO: bumped_sound) */
play_seq();
}
/* check_bumped (seg004): если передний край ЗАШЁЛ за грань стены — выровнять /* check_bumped (seg004): если передний край ЗАШЁЛ за грань стены — выровнять
* к грани И проиграть отскок seq_47_bump (dx4 назад + кадры 50-52 + stand), * к грани и отдать исход в bumped_floor/bumped_fall (порт bumped, seg004).
* как в оригинале (bumped_floor). Это даёт «шаг назад со звуком» при беге/ * Это даёт «шаг назад со звуком» при беге/шаге в стену вместо падения. Уже
* шаге в стену вместо падения. Уже в отскоке (action bumped) — только * в отскоке (action bumped) — только выравниваем, без рестарта сиквенса.
* выравниваем, без рестарта. В падении/висе не трогаем. */ * В падении/висе не трогаем. */
static void check_bumped(void) static void check_bumped(void)
{ {
uint8_t t, wt; int8_t tcol; int d; uint8_t t, wt; int8_t tcol; int d;
@@ -842,10 +891,23 @@ static void check_bumped(void)
if (d >= 0) return; /* ещё не зашёл за грань */ if (d >= 0) return; /* ещё не зашёл за грань */
Kid.x = (uint8_t)char_dx_forward((int8_t)d); /* край -> грань */ Kid.x = (uint8_t)char_dx_forward((int8_t)d); /* край -> грань */
determine_col(); determine_col();
if (Kid.action == ACT_BUMPED) return; /* уже в отскоке — не рестартить */ /* Хвост bumped (seg004:04E4): чем КОНЧИТСЯ удар, решает тайл, на котором
Kid.fall_y = 0; * персонаж оказался после отжатия от грани. Сквозь стену/верх двери он
kid_set_seq(SEQ_47_BUMP); /* отскок (TODO: bumped_sound) */ * стоит на СОСЕДНЕЙ клетке (в сторону толчка), для прочих препятствий
play_seq(); * (ворота, зеркало, кусаки) — на клетке самого препятствия (у ворот пол
* есть, поэтому tile_is_floor их считает полом). */
{
uint8_t ct = t;
if (Kid.direction < 0) { /* смотрит влево => толчок ВПРАВО */
if (ct == TILE_DOORTOP || ct == TILE_DOORTOP_FLOOR || ct == TILE_WALL)
ct = get_tile((int8_t)(tcol + 1), Kid.curr_row);
} else { /* смотрит вправо => толчок ВЛЕВО */
if (ct == TILE_WALL)
ct = get_tile((int8_t)(tcol - 1), Kid.curr_row);
}
if (tile_is_floor(ct)) bumped_floor();
else bumped_fall();
}
determine_col(); determine_col();
} }