Files
Sprinter-SDCC/applications/PoP/roomtest/pop_redraw.c
T
snark13 7ae691b070 Fore потолка поверх падающей плиты: в разборе пометок не было ряда -1
Найдено пользователем (2026-08-17, уровень 1 комната 6): падающая плита
(-1,5) перекрывает собой кромку потолка (-1,6), чего физически быть не может.

Дыра архитектурная: pop_fore_needed обходил только ряды 0..2, ряда -1 в
переднем слое не было вовсе.

Сверено с оригиналом.  redraw_needed_tiles (seg008:1B06) обходит ряды 2,1,0, а
ПОТОМ отдельным проходом ряд 2 комнаты сверху (redraw_needed_above), и его
draw_tile_fore кладёт куски в FOREtable.  Падающая плита идёт в MIDtable
(draw_mobs).  draw_tables рисует back -> mid -> fore (seg008:1373), поэтому у
оригинала кромка потолка оказывается поверх плиты сама собой.

Порт:

- pop_fore_needed: проход по ряду -1 добавлен и идёт ПОСЛЕДНИМ, как в
  оригинале.  Свой набор пометок (rdfa/rdfa_pending) — как и у самих
  перерисовок ряда -1 (rda_*), это отдельный проход, а не 11-я колонка;
- новый лист pop_ceil_fore_tile_b (pop_bg.c) — тот же redraw_needed_above,
  что уже рисовался над персонажем (ceil_over_kid_tile), плюс окно клипа
  ровно на полосу столбца и ov_mark (полоса идёт банком без тени, на второй
  странице её восстановит pop_fore_heal);
- mob_mark_neighbour помечает ряд -1, пока кусок достаёт до кромки.  Кромка
  живёт в трёх верхних строках поля (dby = 2 при клипе по POP_YOFF), спрайт
  куска занимает mob_y-16 .. mob_y, отсюда условие mob_y <= 18 — три кадра
  после отрыва (y = 2, 5, 11 при ускорении 3).  Помечаются ОБА столбца,
  которые кусок накрывает по x (mob_x .. mob_x+62 = col и col+1); именно
  поэтому страдал сосед.

По бюджету работа появляется только в эти три кадра на кусок и попадает в
циановую фазу, где сейчас запас (270 тыс. из 400 тыс.).  Замер на ур.13 —
следующим шагом.

8 наборов tests-host зелёные.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:17:04 +03:00

139 lines
6.3 KiB
C

/*
* pop_redraw.c — пометки перерисовки тайлов и их разбор. См. pop_redraw.h.
*
* Живёт в W1/W2 (НЕ в резиденте W3): пометки ставит логика, которая должна
* оставаться достижимой из будущих __banked-модулей. Сам разбор зовёт
* pop_bg (резидент W3) — это законно, потому что разбор дёргает только
* главный цикл.
*/
#include <stdint.h>
#include "pop_redraw.h"
#include "pop_bg.h"
#include "_pop_bg.h" /* pop_bake_slot_reset — тот же банк, без трамплина */
#define NTILES 30
#define NCOLS 10
static uint8_t rd_kind[NTILES], rd_cnt[NTILES];
static uint8_t rda_kind[NCOLS], rda_cnt[NCOLS];
/* Сколько всего непустых пометок: без него проход по 40 ячейкам крутился бы
* КАЖДЫЙ кадр впустую (замер 2026-07-27 на прежнем rest-цикле: ~12 % кадра). */
static uint8_t rd_pending;
/* Пометки ПЕРЕДНЕГО слоя — однокадровые, отдельно от rd_*: те живут по
* страницам дабл-буфера (запекают фон), а эти транзиентны. */
static uint8_t rdf[NTILES];
static uint8_t rdf_pending;
/* Передний слой РЯДА −1 (полоса кладки у потолка). Отдельным набором, как и
* сами перерисовки ряда −1 (rda_*): у оригинала это вообще другой проход —
* redraw_needed_tiles сначала обходит ряды 2,1,0, а ПОТОМ ряд 2 комнаты сверху
* (seg008:1B06), и его draw_tile_fore кладёт куски в foretable, то есть поверх
* midtable, где лежат падающие плиты, и поверх объектов. */
static uint8_t rdfa[NCOLS];
static uint8_t rdfa_pending;
void pop_redraw_reset(void) __banked
{
uint8_t i;
for (i = 0; i < NTILES; i++) { rd_kind[i] = 0; rd_cnt[i] = 0; }
for (i = 0; i < NCOLS; i++) { rda_kind[i] = 0; rda_cnt[i] = 0; }
rd_pending = 0;
for (i = 0; i < NTILES; i++) rdf[i] = 0;
for (i = 0; i < NCOLS; i++) rdfa[i] = 0;
pop_fore_pass_end();
rdf_pending = 0;
rdfa_pending = 0;
}
void pop_set_redraw_fore(uint8_t tilepos) __banked
{
if (tilepos >= NTILES) return;
if (!rdf[tilepos]) rdf_pending++;
rdf[tilepos] = 1;
}
/* То же для полосы кладки У ПОТОЛКА (ряд −1). Ставит падающий кусок, когда
* его спрайт достаёт до кромки потолка: иначе кусок накрывает эту кромку
* собой, чего быть не может (найдено пользователем 2026-08-17, ур.1 к.6 —
* плита (−1,5) перекрывала потолок (−1,6)). */
void pop_set_redraw_fore_above(uint8_t col) __banked
{
if (col >= NCOLS) return;
if (!rdfa[col]) rdfa_pending++;
rdfa[col] = 1;
}
/* Разбор пометок переднего слоя: тайлы обходятся в том же порядке, что и
* фон, и рисуются ПОСЛЕ всех объектов кадра — как foretable у оригинала
* (draw_tables, seg008:1373: back -> mid -> fore). */
void pop_fore_needed(void) __banked
{
uint8_t i, row = 0, col = 0;
if (!rdf_pending && !rdfa_pending) return;
pop_fore_pass_begin();
for (i = 0; i < NTILES; i++) {
if (rdf[i]) { rdf[i] = 0; pop_fore_tile_b(row, col); }
if (++col == 10) { col = 0; row++; }
}
rdf_pending = 0;
/* Ряд −1 — ПОСЛЕДНИМ, как в redraw_needed_tiles (seg008:1B06). */
if (rdfa_pending) {
for (i = 0; i < NCOLS; i++)
if (rdfa[i]) { rdfa[i] = 0; pop_ceil_fore_tile_b(i); }
rdfa_pending = 0;
}
}
void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked
{
if (tilepos >= NTILES) return;
if (!rd_cnt[tilepos]) rd_pending++;
else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */
rd_kind[tilepos] = kind;
if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages;
}
void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked
{
if (col >= NCOLS) return;
if (!rda_cnt[col]) rd_pending++;
else pop_bake_slot_reset_above(col); /* перепометка — копия запечки не годится */
rda_kind[col] = kind;
if (pages > rda_cnt[col]) rda_cnt[col] = pages;
}
/* redraw_needed (seg008:0178): пройти пометки и нарисовать. row/col ведём
* бегущими счётчиками, а не через i/10 и i%10 — у Z80 нет деления, и SDCC
* выносит __divuchar/__moduchar в вершину цикла как общее подвыражение
* аргументов (30*2 делений за кадр даже когда рисовать нечего). */
void pop_redraw_needed(void) __banked
{
uint8_t i, row = 0, col = 0, left = 0;
if (!rd_pending) return;
for (i = 0; i < NTILES; i++) {
if (rd_cnt[i]) {
switch (rd_kind[i]) {
case POP_RD_SPIKE: pop_spike_redraw(row, col); break;
case POP_RD_LOOSE: pop_loose_shake_draw(row, col); break;
case POP_RD_LOOSE_GONE: pop_loose_bake_empty(row, col); break;
case POP_RD_FLOOR: pop_floor_bake(row, col); break;
case POP_RD_LEVELDOOR: pop_leveldoor_redraw(row, col); break;
case POP_RD_GATE: pop_gate_redraw(row, col); break;
case POP_RD_CHOMP: pop_chomp_redraw(row, col); break;
default: break;
}
if (--rd_cnt[i]) left++;
else rd_kind[i] = 0;
}
if (++col == 10) { col = 0; row++; }
}
for (i = 0; i < NCOLS; i++) {
if (rda_cnt[i]) {
if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i);
else pop_ceil_bake_empty(i);
if (--rda_cnt[i]) left++;
else rda_kind[i] = 0;
}
}
rd_pending = left;
}