Уровень 5: тень не дерётся, кнопка в шве, узор паласа после зелья

Три бага одной отладочной сессии, все проверены в MAME пользователем.

1. SHADOW-FIGHT-L5. Кид доставал меч, а тень вставала в боевую стойку и
   рисовалась спрайтами стража.  В pop_check_can_guard_see_kid первым
   множителем стояло `Guard.charid != 0`, тогда как оригинал (seg003:702)
   пишет `Guard.charid != charid_1_shadow || current_level == 12`: тень
   боевая только на 12-м уровне.  Оба симптома шли из одной ветки
   control_standing (seg005:352).  Возвращён и пропущенный множитель
   `Guard.direction != dir_56_none`.

2. SEAM-BUTTON-STALE. Кнопка в шве не меняла вид ни при нажатии, ни при
   отжатии.  Сигнатура редроя шва сравнивала room_modif, а у кнопки modif —
   это индекс LINKLOC, константа уровня: нажатие живёт в doorlinks2, и в
   сигнатуру не приходило никогда.  seam_row_sig подмешивает бит нажатости.
   Оригинал шов в этом случае не перерисовывает вовсе — расхождение
   осознанное, записано как D-2 в docs/impl_diff.md.

3. BUG-POTION-STRIPE. Синяя лента паласа пропадала на месте выпитого зелья
   и не возвращалась даже после перезахода в комнату.  У оригинала
   curr_room_tiles — сама таблица уровня, у нас fg живёт в двух местах, и
   страницу правил главный цикл уже после pop_process_trobs.  В этот зазор
   trob зелья крутил фазу пузырька поверх обнулённого модификатора:
   bubble_next_frame(0) = 1, а для пола в паласе modif 1 означает «узор не
   рисовать» (seg008:499).  do_pickup теперь правит страницу уровня сразу.
   Тем же лечится меч: animate_sword делал `--mod[tp]` из нуля.

tests-host 5/5 (добавлена заглушка pop_level_set_tile), size-check чист.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 21:24:15 +03:00
parent 36a60f54ba
commit abe4a83d38
7 changed files with 235 additions and 6 deletions
+28 -6
View File
@@ -236,6 +236,24 @@ static uint8_t guard_over_kid(void)
* — изменение приходило в m[19], сигнатура его не видела, редрой не
* заказывался, и открытые ворота оставались нарисованными закрытыми
* (BUG-GATE-SEAM-ROW1). */
/* Кнопка в шве (SEAM-BUTTON-STALE). Сигнатурой служил ТОЛЬКО modif, а у
* кнопки modif — это индекс LINKLOC, то есть константа уровня: нажатие живёт
* в doorlinks2 (get_doorlink_timer, seg007:0BB6). Изменение в сигнатуру не
* приходило, редрой не заказывался — и шов оставался нарисованным в том
* состоянии, в каком был на входе в комнату: кнопка (1,9) комнаты 11, она же
* (1,−1) комнаты 24, залипала нажатой (или отжатой), хотя связь срабатывала.
* Видимой её делает draw_tile: нажатый opener рисуется как ПОЛ (seg008
* get_tile_to_draw), а у пола есть правая грань, у кнопки — нет.
* Поэтому в сигнатуру ряда подмешиваем бит «кнопка нажата». */
static uint8_t seam_row_sig(const uint8_t *m, const uint8_t *lfg, uint8_t r)
{
uint8_t code = (uint8_t)(lfg[r] & 0x1F);
uint8_t mod = m[9 + r * 10];
if (code == 0x0F || code == 0x06) /* opener / closer */
return (uint8_t)((pop_doorlink2(mod) & 0x1F) > 1);
return mod;
}
static uint8_t seam_sig[3]; /* последняя нарисованная openness гейта в шве */
static uint8_t seam_rows; /* маска рядов, ждущих редроя (переживает 2 кадра) */
static uint8_t seam_redraw; /* оставшиеся кадры редроя (обе страницы) */
@@ -346,10 +364,12 @@ static void enter_room_side(uint8_t room, uint8_t side)
* pop_room_load заполнил lcol_bg/rcol_bg статикой уровня — перекрываем. */
{
uint8_t lft = pop_room_link(room, 0), rgt = pop_room_link(room, 1);
if (lft) { uint8_t *m = pop_trob_modif(lft);
lcol_bg[0] = m[9]; lcol_bg[1] = m[19]; lcol_bg[2] = m[29];
/* шов рисуется в обе страницы ниже — синхр. сигнатуру */
seam_sig[0] = m[9]; seam_sig[1] = m[19]; seam_sig[2] = m[29]; }
if (lft) { uint8_t *m = pop_trob_modif(lft), i;
for (i = 0; i < 3; i++) {
lcol_bg[i] = m[9 + i * 10];
/* шов рисуется в обе страницы ниже — синхр. сигнатуру */
seam_sig[i] = seam_row_sig(m, lcol_fg, i);
} }
if (rgt) { uint8_t *m = pop_trob_modif(rgt);
rcol_bg[0] = m[0]; rcol_bg[1] = m[10]; rcol_bg[2] = m[20]; }
}
@@ -792,7 +812,7 @@ int main(void)
/* Все ТРИ ряда колонки 9: ворота в шве бывают в любом из них
* (BUG-GATE-SEAM-ROW1, см. объявление seam_sig). */
for (i = 0; i < 3; i++) {
uint8_t g = m[9 + i * 10], prev = seam_sig[i];
uint8_t g = seam_row_sig(m, lcol_fg, i), prev = seam_sig[i];
seam_sig[i] = g; /* СНАЧАЛА сохранить (обход SDCC z80 sub-store */
/* бага: `if(g!=s)s=g` эмитит store РАЗНОСТИ g-s, */
/* не g — sdcc_z80_cmp_store_a_bug) */
@@ -906,7 +926,9 @@ int main(void)
if (pop_item_taken) { /* предмет поднят → тайл стал полом */
item_pos = (uint8_t)(pop_item_taken - 1);
item_bake = 2; /* запечь на ОБЕИХ страницах */
pop_level_set_tile(cur_room, item_pos, TILE_FLOOR);
/* Тайл в странице уровня ставит САМА do_pickup (pop_map.c): здесь
* это было бы слишком поздно — между подбором и этим местом
* успевает пройти pop_process_trobs (BUG-POTION-STRIPE). */
pop_add_trob(cur_room, item_pos, -1); /* меч убран — блеска больше нет */
pop_item_taken = 0;
}