L9-INVERT: состояние переворота, зелье типа 4 и pop_flip_screen

Шаг II.1 плана.  pop_upside/pop_upside_dirty (порт upside_down и
need_redraw_because_flipped), ветка case 4 в proc_get_object (только тоггл —
ни вспышки, ни урона, как в оригинале), сброс стартом уровня и смертью Кида.

pop_flip_screen (банк 8) переворачивает уже нарисованное двумя копиями
акселератора: VFLIP в скрытую страницу, показ, DIRECT во вторую; затем
инвалидация слотов персонажей, метки фона и полосы HP.

Чит-клавиша U — порт seg000:0793 (в оригинале переворот тоже на чите): без
неё эффект не проверить, чит-телепорт до зелья в комнате 7 не дотягивается.

Проверено в MAME на уровне 9: комната переворачивается целиком за кадр,
повторное нажатие возвращает.  Персонажи и факелы пока не зеркалятся —
это шаги II.3/II.4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 16:48:08 +03:00
parent 7fe4c25ad1
commit b1ef48bd16
7 changed files with 144 additions and 5 deletions
+53
View File
@@ -19,6 +19,8 @@
#include "pop_guard.h"
#include "pop_state.h"
#include "pop_geom.h"
#include "pop_cdraw.h" /* pop_cd — инвалидация слотов после переворота */
#include <sprite.h> /* gfx_copy_page */
#include "pop_tune.h"
#define SEQ_STAND 2
@@ -73,6 +75,11 @@ void pop_start_level(void) __banked
pop_leveldoor_open = 0; /* seg003:97 — дверь прошлого уровня не в счёт */
pop_feather = 0; /* seg003:189 — перо не переживает старт уровня */
/* upside_down тоже сбрасывается стартом уровня (seg003:38/188). Если
* картинка была перевёрнута, отрисовка комнаты ниже уже пойдёт обычной
* стороной, поэтому переворачивать ничего не надо — только снять флаги. */
pop_upside = 0;
pop_upside_dirty = 0;
entry_pose = pop_level_entry_pose(pop_current_level);
start_room = pop_level_start_room();
pos = pop_level_start_pos();
@@ -247,3 +254,49 @@ void pop_dbg_room_label(uint8_t room) __banked
for (i = 0; i < (uint8_t)(room % 10); i++) bar(130 + i * 5, 0, 131 + i * 5, 5);
room_label_redraw--;
}
/* ---- Переворот уже нарисованного (зелье инверсии, уровень 9) --------- *
*
* Порт need_redraw_because_flipped (seg000:15E9 + 939/946). Оригинал
* держит готовый offscreen и переворачивает его на выводе; у нас страниц
* ровно две, рабочей третьей нет, поэтому переворачиваем то, что уже
* нарисовано, — построчной копией акселератора (gfx_copy_page).
*
* Почему это законно и почему heal после не ломается: accel ЧИТАЕТ
* ОЗУ-копию, значит копируется ЧИСТЫЙ ФОН без персонажей, а пишет банком
* 0x50 — и в видео, и в копию приёмника. После двух проходов обе страницы
* дабл-буфера содержат перевёрнутый фон в обеих плоскостях, и heal
* восстанавливает уже его (memory accel_port_y_between_triggers,
* регресс — tests/pageflip).
*
* Порядок: перевернуть в СКРЫТУЮ, показать её, скопировать оттуда во
* вторую (теперь скрытую) — прямой копией. Так игрок видит результат
* сразу, а не после второго прохода.
*
* Переворачиваем ТОЛЬКО игровое поле (POP_PLAYFIELD_H): полоса HP лежит
* ниже него и в оригинале тоже не переворачивается. */
void pop_flip_screen(void) __banked
{
gfx_rect_t area;
uint8_t back = (uint8_t)(gfx_get_visible_page() ^ 1);
area.x = 0; area.y = POP_YOFF;
area.w = 320; area.h = POP_PLAYFIELD_H;
gfx_set_draw_page(back);
gfx_copy_page(&area, GFX_COPY_VFLIP); /* источник — видимая страница */
gfx_set_visible_page(back);
gfx_set_draw_page((uint8_t)(back ^ 1));
gfx_copy_page(&area, GFX_COPY_DIRECT); /* вторую страницу — прямой копией */
/* Всё, что рисуется поверх фона, надо положить заново: прошлые
* прямоугольники персонажей относятся к СТАРОЙ (неперевёрнутой)
* картинке, и heal по ним стёр бы не то место. */
pop_cd[POP_CH_KID].valid[0] = pop_cd[POP_CH_KID].valid[1] = 0;
pop_cd[POP_CH_KID].ovalid[0] = pop_cd[POP_CH_KID].ovalid[1] = 0;
pop_cd[POP_CH_OPP].valid[0] = pop_cd[POP_CH_OPP].valid[1] = 0;
pop_cd[POP_CH_OPP].ovalid[0] = pop_cd[POP_CH_OPP].ovalid[1] = 0;
POP_CD_TOUCH_ALL(); /* фон трогали целиком */
pop_hp_invalidate(); /* полосу HP перерисовать */
}