PoP roomtest: дверь уровня — створка, лестница и анимация открытия

Комната 9: портал на уровень 2 рисовался чёрным проёмом — не был
портирован draw_leveldoor (seg008:1D29).  Дверь рисуется при обработке
ПРАВОЙ половины (tile_left = 0x10), все куски со сдвигом +8 px:
  99  низ лестницы (всегда),
  144 марш лестницы за створкой (когда створка тронулась),
  33  слайс створки — повторяется вниз с шагом 4 px до y = ybottom-modif
      (modif 0 = закрыто на всю высоту, 43 = открыто, остаётся кромка),
  34  верх коробки.

Анимация: animate_leveldoor (seg007:05F1) — type 0..2 открытие (modif++
до 43), type>=3 быстрое закрытие со скоростями {0,5,17,99}.  Кнопка
заводит trob через trigger_1 (seg007:0999): дверь открывается ОДИН раз,
при modif != 0 кнопка уже ничего не делает.

Перерисовка створки — pop_leveldoor_redraw: draw_tile правой половины С
ЗАПЕЧКОЙ в ОЗУ-копию (банк TRANSPARENT), иначе kid_heal возвращал бы из
фона закрытую створку.  Куски двери непрозрачные, wipe не нужен.

Спрайты 33/34/99/144 не попадали в атлас: сбор идёт прогоном
render_room.py, а он draw_leveldoor не реализует — добавлены явным
набором LEVELDOOR_ENV_IDS (как STUCK_ENV_IDS для нажатой кнопки).

Проверено в MAME (комната 9): закрытая дверь = решётка как в оригинале;
после нажатия кнопки (0,0) створка едет вверх, открывая лестницу.
Вход в дверь (кадры 217..228 + переход на уровень 2) НЕ делался.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 18:05:57 +03:00
parent fb495ace42
commit 63a25530f5
72 changed files with 90 additions and 2 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -445,7 +445,7 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
| 6 | | |
| 7 | | |
| 8 | | |
| 9 | | |
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
| 10 | | |
| 11 | | |
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» |
+39
View File
@@ -464,6 +464,32 @@ static uint8_t get_loose_frame(uint8_t m)
return m;
}
/* draw_leveldoor (seg008:1D29) — дверь уровня. Рисуется при обработке
* ПРАВОЙ половины (tile_left = 0x10), все куски со сдвигом +8px (xh+1):
* 99 низ лестницы (всегда),
* 144 марш лестницы за створкой (когда створка приподнята, modif != 0),
* 33 слайс створки — повторяется вниз с шагом 4px, пока не дойдёт до
* y = ybottom modif: modif 0 = закрыто (створка на всю высоту),
* 43 = открыто (остаётся только верхняя кромка),
* 34 верх коробки двери.
* Куски непрозрачные (blitters_0_no_transp) — как в оригинале. */
static void draw_leveldoor(uint8_t modif, int xh, int dmy)
{
int x = (xh + 1) * 8;
int ybottom = dmy - 13;
int ylo = ybottom - (int)(modif & 3) - 48; /* leveldoor_ybottom */
int y = ybottom - (int)modif;
env_b(99, x, ybottom);
if (modif)
env_b(144, x, ybottom - 4); /* лестница (комната ≠ стартовой) */
for (;;) {
env_b(33, x, ylo);
if (y > ylo) ylo += 4;
else break;
}
env_b(34, x, dmy - 64);
}
/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
static void draw_tile(int row, int col)
{
@@ -539,6 +565,8 @@ static void draw_tile(int row, int col)
uint8_t sf = (lmod & 0x80) ? 5 : lmod;
if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
env_b(SPIKES_FRAM_RIGHT[sf], x, dmy - 7);
} else if (lcode == 16) { /* дверь уровня */
draw_leveldoor(lmod, xh, dmy);
}
/* Пламя факела (seg008:560, chtab_1 id modifier+1) в фон НЕ запекаем:
* оно анимируется каждый кадр, а heal вернул бы запечённый кадр из-под
@@ -769,6 +797,17 @@ void pop_floor_bake(int row, int col)
void pop_button_redraw(int row, int col) { pop_floor_bake(row, col); }
/* Перерисовать ПРАВУЮ половину двери уровня (в её ячейке рисуется створка,
* см. draw_leveldoor) на back-странице С ЗАПЕЧКОЙ в ОЗУ-копию: створка едет,
* а heal обязан возвращать её ТЕКУЩЕЕ положение, а не запечённое закрытое.
* Куски двери непрозрачные — wipe не нужен. */
void pop_leveldoor_redraw(int row, int col)
{
gfx_set_bank(GFX_BANK_TRANSPARENT);
draw_tile(row, col);
gfx_set_bank(GFX_BANK_NORMAL);
}
/* Запечь ПУСТОТУ на месте упавшего loose (текущая страница). Порт
* redraw_at_cur_mob (seg007:132C): перерисовать сам тайл (теперь empty) И
* тайл СПРАВА (его левая грань зависит от нашего кода: пока был loose —
+4
View File
@@ -102,6 +102,10 @@ void pop_room_redraw_seam_left(void);
void pop_floor_bake(int row, int col);
void pop_button_redraw(int row, int col); /* = pop_floor_bake (кнопка) */
/* Дверь уровня: перерисовать её ПРАВУЮ половину (row, col = правая) с
* запечкой — створка анимируется (pop_trob animate_leveldoor). */
void pop_leveldoor_redraw(int row, int col);
/* Падающий кусок loose (mob, seg007 move_loose): спавн при падении тайла
* (row,col) и покадровое продвижение+отрисовка на back-странице. tick
* зовётся каждый кадр (внутри pop_loose_tick); клип низа делает clip. */
+40
View File
@@ -15,6 +15,7 @@
#define TILE_OPENER 0x0F
#define TILE_POTION 0x0A
#define TILE_SWORD 0x16 /* меч-предмет на полу */
#define TILE_LEVELDOOR 0x10 /* левая половина двери уровня (на ней modif) */
#define TILE_TORCH 0x13
#define TILE_TORCH_DEBRIS 0x1E
@@ -240,6 +241,13 @@ static int8_t trigger_1(uint8_t target_type, uint8_t room, uint8_t tilepos, uint
{
if (target_type == TILE_GATE)
return trigger_gate(room, tilepos, button_type);
if (target_type == TILE_LEVELDOOR) {
/* trigger_1 (seg007:0999): дверь уровня открывается ОДИН раз — если
* створка уже тронулась (modif != 0), кнопка ничего не делает. */
uint8_t *m = pop_trob_modif(room);
if (m && m[tilepos] == 0) return 1;
return -1;
}
return -1;
}
@@ -282,6 +290,27 @@ static void animate_button(uint8_t idx, int8_t *type)
}
}
/* animate_leveldoor (seg007:05F1): створка двери уровня. type 0..2 —
* открытие (modif 0 -> 43, по пикселю за кадр), type >= 3 — быстрое
* закрытие со скоростями {0,5,17,99} (после выхода из уровня). Готово ->
* type = -1. */
static const uint8_t leveldoor_close_speeds[5] = {0, 5, 17, 99, 0};
static void animate_leveldoor(uint8_t *m, int8_t *type)
{
if (*type < 0) return;
if (*type >= 3) {
uint8_t sp;
if (*type < 7) (*type)++;
sp = leveldoor_close_speeds[*type - 3];
if (sp >= *m) { *m = 0; *type = -1; }
else *m = (uint8_t)(*m - sp);
} else {
if (*m < 43) (*m)++;
if (*m >= 43) { *m = 43; *type = -1; }
}
}
/* animate_door (seg007:0522): openness по типу анимации. *m — modif ворот,
* *type — 0 закрытие / 1 открытие / 2 насовсем / 3..8 быстрое закрытие. */
static const uint8_t gate_close_speeds[9] = {0, 0, 0, 20, 40, 60, 80, 100, 120};
@@ -384,6 +413,9 @@ void pop_process_trobs(uint8_t cur_room)
case TILE_GATE:
animate_door(&mod[tp], &type);
break;
case TILE_LEVELDOOR:
animate_leveldoor(&mod[tp], &type);
break;
case TILE_TORCH:
case TILE_TORCH_DEBRIS:
/* animate_torch (seg007:03C1): кадр пламени; анимация вечная —
@@ -439,6 +471,14 @@ void pop_process_trobs(uint8_t cur_room)
* его уже никто не положит: ставим отложенный редрой. */
if (type < 0) { spike_rest[tp] = 1; rest_pending++; }
}
if (room == cur_room && code == TILE_LEVELDOOR) {
/* Створка едет каждый кадр — перерисовываем ПРАВУЮ половину (там
* её рисует draw_tile_anim_right) С ЗАПЕЧКОЙ в ОЗУ-копию: иначе
* kid_heal вернул бы из фона закрытую створку. Куски двери
* непрозрачны, поэтому wipe не нужен. */
uint8_t trow = tp / 10, tcol = tp % 10;
if (tcol + 1 < 10) pop_leveldoor_redraw(trow, tcol + 1);
}
if (room == cur_room && code == TILE_SWORD) {
/* МЕЧ — редрой только на смене видимого кадра (блеск = modif==1):
* спрайт запечён в фон (draw_tile), поэтому перерисовываем тайл
+6 -1
View File
@@ -83,6 +83,10 @@ STUCK_ENV_IDS = {35, 36}
# curr_tile==15 && tile_left==0 && level_type==0 → id 148 вместо базы 147).
# render_room этой ветки не знает, поэтому добавляем явно.
OPENER_NOFLOOR_ENV_IDS = {148}
# Дверь уровня (draw_leveldoor, seg008:1D29): решётка створки (33 — слайс 4px,
# 34 — верх), лестница за ней (99 — низ, 144 — марш). render_room.py
# draw_leveldoor не реализует, поэтому в автосбор эти id не попадают.
LEVELDOOR_ENV_IDS = {33, 34, 99, 144}
# ---- chtab_1 (flame/sword/potion), база ресурса 150, папка PRINCE ----
# Зелье (seg008 draw_tile_fore/draw_tile_anim): склянка id 12 (малая) / 13
@@ -355,7 +359,8 @@ def main():
| GATE_ANIM_ENV_IDS # + кадры открытия ворот
| TORCH_ENV_IDS # + основание факела
| STUCK_ENV_IDS # + нажатая кнопка-closer
| OPENER_NOFLOOR_ENV_IDS) # + opener без пола слева
| OPENER_NOFLOOR_ENV_IDS # + opener без пола слева
| LEVELDOOR_ENV_IDS) # + дверь уровня (створка/лестница)
wall_ids = sorted(id for ch, id in used if ch == R.CHTAB_WALL)
env_bg = [i for i in env_ids if i not in FORE_ENV_IDS]
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B