Ворота PoP: правильный гейт слышимости, звук «решётка встала», playsound кнопки

Проверка на сцене 9/9 (кнопка 1,8; челюсти 1,2; ворота — в комнате 4,
тайл 1,9) вскрыла три расхождения.

1. Гейт слышимости стоял как «ворота в текущей комнате», а у оригинала
   (play_door_sound_if_visible, seg007:1239) слышны ещё и ворота в комнате
   СЛЕВА, если они в колонке 9; и НЕ слышны в колонке 9 своей комнаты; плюс
   особый случай «уровень 3, комната 2».  Сцена 9/9 — ровно первый пункт,
   поэтому спуск решётки молчал.  Подъём совпадал, потому что звук открытия
   идёт без гейта — эта асимметрия и была подсказкой.

2. Потерян звук 7 «решётка встала»: gate_stop (seg007:05E3) зовётся из трёх
   мест animate_door и каждый раз играет его через гейт.  У нас во всех трёх
   стояло только type = -1.

3. У trigger_button оригинала есть параметр playsound, нулевой в трёх
   местах (вход на уровень, выход Джаффара, зелье «открыть»).  Добавлен.

Щелчок кнопки слышен через раз — это НЕ баг: prio 0x66 против 0x10 у
челюстей, а укус занимает 465 мс из цикла 1229 мс (замерено).  Разбор с
цифрами — sound_plan.md §12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 21:03:16 +03:00
parent a630568a8b
commit c6828c0ad1
7 changed files with 129 additions and 19 deletions
+56 -11
View File
@@ -455,7 +455,8 @@ static void do_trigger_list(uint8_t index, uint8_t button_type)
}
void pop_trigger_button(uint8_t room, uint8_t tilepos,
uint8_t button_type, uint8_t modifier) __banked
uint8_t button_type, uint8_t modifier,
uint8_t snd) __banked
{
uint8_t link_timer = get_doorlink_timer(modifier);
if (link_timer != 0x1F) { /* не заклинено */
@@ -466,8 +467,14 @@ void pop_trigger_button(uint8_t room, uint8_t tilepos,
if (link_timer < 2) {
pop_add_trob(room, tilepos, 1); /* кнопка нажимается */
/* seg007:766: щелчок кнопки — шум, и страж оборачивается
* (флаг обязателен даже без звука, BUG-GUARD-DEAF-1). */
pop_sfx_play(3);
* (флаг обязателен даже без звука, BUG-GUARD-DEAF-1).
*
* Звук НЕ у всех вызовов: у оригинала первый параметр playsound,
* и в трёх местах он нулевой — вход на уровень (seg003:170),
* выход Джаффара (seg002:520) и зелье «открыть» (seg006:1890,
* у нас пока не портировано). Там кнопка нажимается служебно,
* щёлкать нечему. */
if (snd) pop_sfx_play(3);
is_guard_notice = 1;
}
do_trigger_list(modifier, button_type);
@@ -558,6 +565,30 @@ static void animate_leveldoor(uint8_t *m, int8_t *type, uint8_t cur_room)
static const uint8_t gate_close_speeds[9] = {0, 0, 0, 20, 40, 60, 80, 100, 120};
static const int8_t door_delta[3] = {-1, 4, 4};
/* play_door_sound_if_visible (seg007:1239): СЛЫШНА ли решётка.
*
* Правило не «решётка в текущей комнате», как можно подумать, а такое:
* - решётка в комнате СЛЕВА и стоит в колонке 9 — слышна (это тайл вплотную
* к левому краю экрана, игрок фактически видит её створку в шве);
* - решётка в ОТРИСОВАННОЙ комнате и НЕ в колонке 9 — слышна (колонка 9
* уезжает за правый край);
* - особый случай оригинала: уровень 3, комната 2 — слышна всегда.
* Всё остальное молчит, иначе на 13-м уровне скрипели бы решётки со всей
* карты.
*
* Берём вариант ПОД FIX_GATE_SOUNDS (условия через ИЛИ, а не тернарником):
* в SDLPoP этот фикс включён по умолчанию, а сравниваемся мы именно с ним.
* Отличие только в вырожденном случае room_L == cur_room. */
static uint8_t door_audible(uint8_t room, uint8_t tp,
uint8_t room_L, uint8_t cur_room)
{
uint8_t col = TP_COL[tp];
if (pop_current_level == 3 && room == 2) return 1;
if (room == room_L && col == 9) return 1;
if (room == cur_room && col != 9) return 1;
return 0;
}
static void animate_door(uint8_t *m, int8_t *type, uint8_t audible)
{
int8_t anim_type = *type;
@@ -573,20 +604,30 @@ static void animate_door(uint8_t *m, int8_t *type, uint8_t audible)
} else if (mod != 0xFF) {
mod = (uint8_t)(mod + door_delta[anim_type]);
if (anim_type == 0) { /* закрытие */
if (mod == 0) *type = -1; /* gate_stop */
/* seg007:376: скрип закрывающейся решётки — раз в четыре
* шага модификатора и ТОЛЬКО если комната на экране
* (play_door_sound_if_visible), иначе на 13-м уровне
* скрипели бы решётки из соседних комнат. */
else if (mod < 188 && (mod & 3) == 3 && audible) pop_sfx_play(4);
/* seg007:376: скрип закрывающейся решётки — раз в четыре шага
* модификатора; лязг «встала» (7) — в конце. Оба через
* гейт слышимости (gate_stop, seg007:05E3, тоже через него). */
if (mod != 0) {
if (mod < 188 && (mod & 3) == 3 && audible) pop_sfx_play(4);
} else {
*type = -1;
if (audible) pop_sfx_play(7); /* gate_stop */
}
} else { /* открытие */
if (mod >= 188) {
/* Лязг после ОБЫЧНОГО открытия оригинал даёт без гейта
* (seg007:395 — прямой play_sound), а после открытия
* насовсем — через gate_stop, то есть с гейтом. */
if (anim_type < 2) { mod = 238; *type = 0; pop_sfx_play(7); }
else { mod = 0xFF; *type = -1; } /* насовсем */
else {
mod = 0xFF; *type = -1;
if (audible) pop_sfx_play(7);
}
} else if ((mod & 7) == 0) pop_sfx_play(5); /* seg007:386 */
}
} else {
*type = -1;
if (audible) pop_sfx_play(7); /* gate_stop, seg007:404 */
}
}
*m = mod;
@@ -668,6 +709,10 @@ void pop_process_trobs(uint8_t cur_room) __banked
{
uint8_t mod_room = 0; /* 0 = «ещё не брали» (комнаты 1..24) */
uint8_t *mod = 0;
/* Сосед СЛЕВА нужен гейту слышимости решёток (door_audible). Берём один
* раз на проход: pop_room_link маппит страницу уровня, а решёток в
* списке может быть несколько. */
uint8_t room_L = pop_room_link(cur_room, 0 /* L */);
for (i = 0; i < trobs_count; i++) {
uint8_t room = trobs[i].room;
@@ -689,7 +734,7 @@ void pop_process_trobs(uint8_t cur_room) __banked
animate_button(mod[tp], &type); /* mod[tp] = индекс LINKLOC кнопки */
break;
case TILE_GATE:
animate_door(&mod[tp], &type, (uint8_t)(room == cur_room));
animate_door(&mod[tp], &type, door_audible(room, tp, room_L, cur_room));
break;
case TILE_LOOSE:
animate_loose(&mod[tp], &type, room, tp, cur_room);