Залипший блеск меча: trob_drawn обнулялся, а комната рисовалась по живому modif

Найдено пользователем (2026-08-17, уровень 1 комната 15): при входе в комнату
блеск меча иногда горит несколько секунд вместо одиночной вспышки.  Повторить
не удавалось.

Порядок в enter_room:
  1) pop_trob_room_changed() обнуляет trob_drawn[] для ВСЕХ тайлов
     = «нарисовано без блеска»;
  2) pop_trob_anim_room() задаёт мечу СЛУЧАЙНУЮ фазу счётчика 0..31
     (start_anim_sword, seg007:087C);
  3) pop_room_draw() рисует комнату по ЖИВОМУ modif.

Если случайная фаза попала ровно на 1, комната рисуется С БЛЕСКОМ (draw_tile
берёт кадр (modif==1)+10), а trob_drawn говорит «блеска нет».  На следующем
кадре animate_sword уводит счётчик с 1, признак снова 0 — и СОВПАДАЕТ со
стоячим trob_drawn, поэтому перерисовка не помечается.  Блеск остаётся
запечённым в фон.

Оба наблюдённых числа сходятся:
  вероятность   ровно 1/32 на вход в комнату — отсюда «редко»;
  длительность  animate_sword считает ВНИЗ и на нуле берёт новый период
                0x28..0x67, то есть до 103 логических кадров ~ 6 секунд.

Фикс: trob_drawn для меча инициализируется тем, что реально нарисует комната
(mod == 1), а не нулём.  Одно присваивание на вход в комнату — в кадре ноль.

ПОДТВЕРЖДЕНО АРТЕФАКТОМ, а не рассуждением: временная сборка с подменой фазы
на mod[i] = 1 давала баг на КАЖДОМ входе в комнату 15 (пользователь
подтвердил), с фиксом — ни на одном.  Временный форсаж снят.

У КНОПКИ той же дыры нет, хотя механизм общий (оба используют trob_drawn):
её признак — ДЛИТЕЛЬНОЕ состояние (нажата/нет), рассогласование само
исправляется на первой же смене.  У блеска признак — одиночная вспышка в
ОДНОМ кадре из 40..103, и пропущенная смена стоит целого периода.  Записано в
комментарии, чтобы их не «унифицировали» обратно.

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

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 17:47:26 +03:00
parent 6a91b9c688
commit e4e9836f29
+21
View File
@@ -177,6 +177,27 @@ void pop_trob_anim_room(uint8_t room) __banked
} else if (code == TILE_SWORD) {
/* start_anim_sword (seg007:087C): случайная фаза счётчика блеска. */
mod[i] = (uint8_t)(trob_prandom(0xFF) & 0x1F);
/* trob_drawn обязан описывать то, что НАРИСУЕТ полная отрисовка
* комнаты, а она идёт ПОСЛЕ этой функции и берёт ЖИВОЙ modif.
* Обнулять его (как делает pop_trob_room_changed для всех тайлов)
* тут нельзя: если случайная фаза попала ровно на 1, комната
* рисуется С БЛЕСКОМ, а trob_drawn говорит «блеска нет» — и на
* следующем кадре счётчик уходит с 1, sig снова 0, СОВПАДАЕТ со
* стоячим trob_drawn, и перерисовка НЕ помечается. Блеск остаётся
* запечённым в фон до конца периода: animate_sword считает вниз и
* выдаёт новый период 0x28..0x67, то есть до 103 логических кадров
* ≈ 6 секунд. Вероятность ровно 1/32 на вход в комнату — отсюда
* «редко воспроизводится» (найдено пользователем 2026-08-17,
* уровень 1 комната 15). Проверено ПРИНУДИТЕЛЬНО: со временной
* подменой фазы на mod[i] = 1 баг ловится на КАЖДОМ входе в
* комнату, а с этой строкой не ловится ни на одном.
*
* У КНОПКИ той же дыры нет, хотя механизм общий: её признак —
* ДЛИТЕЛЬНОЕ состояние (нажата/нет), и рассогласование само
* исправляется на первой же смене. У блеска признак — одиночная
* вспышка в ОДНОМ кадре из 40..103, и пропущенная смена стоит
* целого периода. */
trob_drawn[i] = (uint8_t)(mod[i] == 1);
pop_add_trob(room, i, 1);
}
}