L4-MIRROR шаги 1-2: зеркало в атласе + постановка тайла по открытию двери
Задача заведена активной на доске по решению пользователя (palace был
отложен 2026-08-04, но уровень 4 уже гоняется в MAME и зеркало —
единственное, что мешает пройти его сюжетно). Механика целиком сверена по
SDLPoP, таблица соответствий в TASKS_OPEN.
Шаг 1 — АТЛАС. tile_table[0x0D] = база 75, фронт 77 (наша таблица
совпадает с SDLPoP байт в байт). Тайла 13 НЕТ НИ В ОДНОМ уровне
статически: перебор всех 15 res200N.bin даёт ноль попаданий (санити
разбора: ур.1 без чомпера, ур.3 с 18, ур.4 с паласными 25..29). Значит
render_room эти id не увидит и на месте зеркала был бы чёрный провал —
грабли memory pop_atlas_dynamic_ids. Добавлены MIRROR_ENV_IDS = {75,77} в
pop_pack_bg.py, 77 ещё и в FORE_ENV_IDS. Оба набора переупакованы:
fore 17 -> 18 спрайтов, все страницы EMM в пределах 16 КБ.
Шаг 2 — ПОСТАНОВКА. place_mirror() в pop_trob.c по переходу
pop_leveldoor_open 0/2 -> 1 (условие оригинала, seg007:0457 — иначе тайл
ставился бы заново каждый кадр открытой двери). Пишет тайл 13 в комнату 4,
колонку 4, ряд 0; если комната уже на экране — POP_RD_FLOOR на обе
страницы. Банк 6 3450 -> 3518.
НЕ ПРОВЕРЕНО В MAME: нужно нажать плиту выхода на уровне 4 и дойти до
комнаты 4. Отдельный вопрос к проверке — не устареет ли g_fg коллизии,
если игрок окажется в комнате 4 в момент постановки.
Дальше по плану: 4 (прыжок сквозь + рождение тени), 5 (клип тени),
3 (отражение — косметика, самое дорогое).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
||||
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
||||
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||
| 4 | [L4-MIRROR](#l4-mirror) | **АКТИВНА с 2026-08-10 по решению пользователя**: зеркало уровня 4 + тень | прохождение ур. 4 |
|
||||
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3** (игра быстрее на треть). Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
|
||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
@@ -720,6 +721,59 @@ loose-плиту (7, кол 4, ряд 0).
|
||||
|
||||
---
|
||||
|
||||
### <a id="l4-mirror"></a>L4-MIRROR. Зеркало уровня 4 и тень — **АКТИВНА с 2026-08-10**
|
||||
|
||||
Приоритет переставлен пользователем: palace-уровни были отложены решением
|
||||
2026-08-04, но уровень 4 уже гоняется в MAME, и зеркало — единственное, что
|
||||
мешает пройти его сюжетно.
|
||||
|
||||
**Как это работает в оригинале** (SDLPoP, всё сверено по коду):
|
||||
|
||||
| что | где | суть |
|
||||
|---|---|---|
|
||||
| постановка тайла | `animate_leveldoor`, seg007:0457 | дверь выхода ДОРИСОВАЛА открытие (`modif >= 43`, `leveldoor_open` 0/2 -> 1) -> в комнату 4, колонку 4, ряд 0 пишется `tiles_13_mirror` |
|
||||
| отражение | `check_mirror`, seg003:0798 | пока Кид на тайле зеркала — каждый кадр рисуется зеркальная копия: `Char.x = (xpos<<1) - Char.x`, `direction` инвертируется, копия идёт в objtable типом 4 с клипом `left = (curr_col<<5)+9`, `top = y_clip[row+1]` |
|
||||
| прыжок сквозь | seg004:0240 | засчитывается ТОЛЬКО `run-jump` СПРАВА НАЛЕВО (`frame 39..43`, `direction < 0`) -> `modif = 0x56` (разбитое), `jumped_through_mirror = -1` |
|
||||
| рождение тени | `jump_through_mirror`, seg003:080A | Кид копируется в слот Guard как `charid_1_shadow`; `guardhp = hitp_max`, у Кида `hitp_curr = 1` |
|
||||
| поведение тени | `autocontrol_shadow_level4`, seg002:1131 | в зеркальной комнате при `x < 80` исчезает (`clear_char`), иначе бежит вперёд. Боя нет |
|
||||
| клип тени | seg008:1699 | `obj_clip_left = 137 + (mirror_column-4)*32` — тень видна только СПРАВА от зеркала |
|
||||
| коллизия | `wall_type`, seg006:1632 | зеркало = 2 «стена слева» — **уже портировано** (`pop_map.c`), как и спецкейс `can_climb_up` |
|
||||
|
||||
**Шаги.**
|
||||
|
||||
1. **АТЛАС — СДЕЛАНО 2026-08-10.** `tile_table[0x0D]` = база 75, фронт 77
|
||||
(наша таблица совпадает с SDLPoP такт в такт). Тайла 13 **нет ни в одном
|
||||
уровне статически** — проверено перебором всех 15 `res200N.bin`, ноль
|
||||
попаданий; санити-проверка разбора: ур.1 без чомпера, ур.3 с 18, ур.4 с
|
||||
паласными 25..29. Значит `render_room` эти id не увидит и на месте
|
||||
зеркала был бы чёрный провал (memory `pop_atlas_dynamic_ids`). Добавлены
|
||||
`MIRROR_ENV_IDS = {75, 77}` в `pop_pack_bg.py`, 77 — ещё и в
|
||||
`FORE_ENV_IDS`. Оба набора переупакованы: fore 17 -> 18 спрайтов, все
|
||||
страницы EMM в пределах 16 КБ.
|
||||
2. **ПОСТАНОВКА ТАЙЛА — СДЕЛАНО 2026-08-10, НЕ ПРОВЕРЕНО В MAME.**
|
||||
`place_mirror()` в `pop_trob.c`, вызывается из `animate_leveldoor` по
|
||||
переходу `pop_leveldoor_open` 0/2 -> 1 (условие оригинала — иначе тайл
|
||||
ставился бы заново каждый кадр открытой двери). Если комната зеркала уже
|
||||
на экране, ставится `POP_RD_FLOOR` на обе страницы.
|
||||
**Что проверить:** нажать плиту выхода на ур. 4, дойти до комнаты 4 —
|
||||
зеркало должно быть нарисовано. Отдельно посмотреть, не устарел ли
|
||||
`g_fg` коллизии, если игрок окажется в комнате 4 в момент постановки.
|
||||
3. **ОТРАЖЕНИЕ.** Самый дорогой шаг: это ЧЕТВЁРТЫЙ рисуемый Char помимо
|
||||
Кида и соперника, со своим клипом. Отрисовка у нас уже общая
|
||||
(`pop_cdraw.c`, слоты — memory `pop_char_draw_unified`), вопрос в числе
|
||||
слотов и в том, что отражение меняется вместе с Кидом каждый кадр, то
|
||||
есть `pop_char_skip_mask` на нём не сработает. ЧИСТО КОСМЕТИКА —
|
||||
делается последним.
|
||||
4. **ПРЫЖОК СКВОЗЬ + РОЖДЕНИЕ ТЕНИ.** Слот Guard уже умеет держать
|
||||
не-стража (скелет ур. 3), `charid_1_shadow` встаёт рядом.
|
||||
`autocontrol_shadow_level4` — десяток строк.
|
||||
5. **КЛИП ТЕНИ** (seg008:1699).
|
||||
|
||||
Порядок: 1 -> 2 -> 4 -> 5 -> 3. После 4 и 5 уровень уже проходится, потому
|
||||
что сюжетно достаточно прыгнуть сквозь зеркало.
|
||||
|
||||
---
|
||||
|
||||
## P1 — берётся в любой момент
|
||||
|
||||
### <a id="l1-speed"></a>L1-SPEED. Игра идёт быстрее оригинала (найдено 2026-08-01)
|
||||
|
||||
@@ -409,7 +409,37 @@ static void animate_button(uint8_t idx, int8_t *type)
|
||||
* 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)
|
||||
/* ---- Спецсобытие уровня 4: ПОЯВЛЕНИЕ ЗЕРКАЛА ------------------------ *
|
||||
*
|
||||
* seg007:0457, хвост animate_leveldoor: когда дверь выхода ДОРИСОВАЛА
|
||||
* открытие и leveldoor_open переходит 0/2 -> 1, оригинал ставит тайл
|
||||
* зеркала прямо в данные комнаты:
|
||||
*
|
||||
* get_tile(mirror_room, mirror_column, mirror_row);
|
||||
* curr_room_tiles[curr_tilepos] = tiles_13_mirror;
|
||||
*
|
||||
* Ставится он в ЧУЖУЮ комнату (4), а не в ту, где дверь. В статике уровня
|
||||
* зеркала НЕТ НИ В ОДНОМ файле (проверено перебором всех 15 res200N.bin),
|
||||
* поэтому его спрайты 75/77 пришлось добавить в атлас явным списком —
|
||||
* MIRROR_ENV_IDS в pop_pack_bg.py (грабли memory pop_atlas_dynamic_ids). */
|
||||
#define MIRROR_LEVEL 4
|
||||
#define MIRROR_ROOM 4
|
||||
#define MIRROR_TILEPOS 4 /* row 0, col 4 */
|
||||
#define TILE_MIRROR 0x0D
|
||||
|
||||
static void place_mirror(uint8_t cur_room)
|
||||
{
|
||||
if (pop_current_level != MIRROR_LEVEL) return;
|
||||
pop_level_set_tile(MIRROR_ROOM, MIRROR_TILEPOS, TILE_MIRROR);
|
||||
/* Комната зеркала обычно НЕ та, где дверь выхода, — игрок войдёт в неё
|
||||
* позже и она отрисуется целиком. Но если она уже на экране, тайл надо
|
||||
* вернуть: зеркало ДОБАВЛЯЕТ графику поверх пустого места, поэтому
|
||||
* хватает обычной пометки (wipe пола + полный draw_tile). */
|
||||
if (cur_room == MIRROR_ROOM)
|
||||
pop_set_redraw(MIRROR_TILEPOS, POP_RD_FLOOR, 2);
|
||||
}
|
||||
|
||||
static void animate_leveldoor(uint8_t *m, int8_t *type, uint8_t cur_room)
|
||||
{
|
||||
if (*type < 0) return;
|
||||
if (*type >= 3) {
|
||||
@@ -420,7 +450,17 @@ static void animate_leveldoor(uint8_t *m, int8_t *type)
|
||||
else *m = (uint8_t)(*m - sp);
|
||||
} else {
|
||||
if (*m < 43) (*m)++;
|
||||
if (*m >= 43) { *m = 43; *type = -1; pop_leveldoor_open = 1; }
|
||||
if (*m >= 43) {
|
||||
*m = 43; *type = -1;
|
||||
/* Условие оригинала — переход ИЗ 0 или 2, то есть ровно один раз
|
||||
* за открытие; иначе зеркало ставилось бы заново каждый кадр,
|
||||
* пока дверь стоит открытой. */
|
||||
if (pop_leveldoor_open == 0 || pop_leveldoor_open == 2) {
|
||||
pop_leveldoor_open = 1;
|
||||
place_mirror(cur_room);
|
||||
}
|
||||
pop_leveldoor_open = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +578,7 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
animate_door(&mod[tp], &type);
|
||||
break;
|
||||
case TILE_LEVELDOOR:
|
||||
animate_leveldoor(&mod[tp], &type);
|
||||
animate_leveldoor(&mod[tp], &type, cur_room);
|
||||
break;
|
||||
case TILE_TORCH:
|
||||
case TILE_TORCH_DEBRIS:
|
||||
|
||||
Reference in New Issue
Block a user