Ворота 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:
@@ -579,3 +579,65 @@ play_next_sound() seg000:1304 — раз в кадр решает, зап
|
||||
|
||||
Цена: резидент +~250 Б (таблицы + логика), куча ужалась с 347 до 134 Б —
|
||||
довод в пользу давно назревшей реорганизации базовой памяти.
|
||||
|
||||
## 12. Ворота: гейт слышимости и «решётка встала» (2026-08-20)
|
||||
|
||||
Проверка на сцене, которую предложил пользователь — уровень 9, комната 9:
|
||||
кнопка (1,8), челюсти (1,2), а ворота **в комнате 4, тайл (1,9)**, то есть
|
||||
в комнате СЛЕВА. Нашлись три расхождения сразу.
|
||||
|
||||
### 12.1 Гейт слышимости был неверный
|
||||
|
||||
У нас стояло `audible = (room == cur_room)`. У оригинала
|
||||
(`play_door_sound_if_visible`, seg007:1239) правило другое:
|
||||
|
||||
- ворота в комнате СЛЕВА и в колонке 9 — СЛЫШНЫ (створка видна в шве);
|
||||
- ворота в отрисованной комнате и НЕ в колонке 9 — слышны;
|
||||
- особый случай: уровень 3, комната 2 — слышны всегда.
|
||||
|
||||
Сцена 9/9 попадает ровно в первый пункт, поэтому спуск решётки у нас
|
||||
молчал. Подъём при этом совпадал с оригиналом — потому что звук открытия
|
||||
(5) идёт БЕЗ гейта (seg007:386, прямой `play_sound`). Эта асимметрия и была
|
||||
подсказкой.
|
||||
|
||||
Взят вариант под `FIX_GATE_SOUNDS` (условия через ИЛИ): в config.h SDLPoP
|
||||
он определён безусловно.
|
||||
|
||||
### 12.2 Потерян звук «решётка встала» (7)
|
||||
|
||||
`gate_stop()` (seg007:05E3) зовётся из ТРЁХ мест `animate_door` и каждый раз
|
||||
играет звук 7 через гейт слышимости: конец закрытия, открытие насовсем и
|
||||
ветка «уже 0xFF». У нас во всех трёх стояло только `*type = -1` без звука.
|
||||
Добавлено. Лязг после ОБЫЧНОГО открытия (seg007:395) остаётся без гейта —
|
||||
там оригинал зовёт `play_sound` напрямую.
|
||||
|
||||
### 12.3 Кнопка: у оригинала есть параметр playsound
|
||||
|
||||
`trigger_button(playsound, ...)` — в трёх местах он нулевой: вход на уровень
|
||||
(seg003:170), выход Джаффара (seg002:520) и зелье «открыть» (seg006:1890, у
|
||||
нас не портировано). Мы играли щелчок всегда. Добавлен параметр `snd`.
|
||||
|
||||
### 12.4 Почему щелчок кнопки слышно через раз — это НЕ баг
|
||||
|
||||
Бюджет сцены 9/9 (длительности после пересчёта на 10 937,5 Гц):
|
||||
|
||||
| звук | длительность | prio |
|
||||
|---|---:|---:|
|
||||
| челюсти (47) | 465 мс | 0x10 |
|
||||
| решётка вниз (4) | 97 мс | 0x32 |
|
||||
| решётка вверх (5) | 123 мс | 0x37 |
|
||||
| решётка встала (7) | 75 мс | 0x30 |
|
||||
| кнопка (3) | 106 мс | 0x66 |
|
||||
|
||||
Цикл челюстей — 15 кадров = 1229 мс (замерено брейкпоинтом на номинации:
|
||||
25 805 000 тактов между укусами). Значит укус занимает 465 мс, пауза 764 мс.
|
||||
Кнопка (prio 0x66) перебить челюсти не может (0x66 > 0x10), поэтому слышна
|
||||
только если нажатие попало в паузу — примерно в 6 случаях из 10.
|
||||
Подтверждено пользователем на живой сцене.
|
||||
|
||||
### 12.5 Грабли сцены
|
||||
|
||||
Если игра стартует ПРЯМО в комнате с челюстями, они не заводятся сами:
|
||||
нужно сходить Кидом на левую кнопку и вернуться. Это поведение оригинала
|
||||
(trob челюстей создаётся событием), а не наш дефект — учитывать при
|
||||
постановке автотестов.
|
||||
|
||||
@@ -406,7 +406,8 @@ void pop_jaffar_exit(void) __banked
|
||||
m = pop_trob_modif(JAFFAR_EXIT_ROOM);
|
||||
pop_trigger_button(JAFFAR_EXIT_ROOM, JAFFAR_EXIT_TILEPOS,
|
||||
pop_level_tile(JAFFAR_EXIT_ROOM, JAFFAR_EXIT_TILEPOS),
|
||||
m ? m[JAFFAR_EXIT_TILEPOS] : 0);
|
||||
m ? m[JAFFAR_EXIT_TILEPOS] : 0,
|
||||
0 /* seg002:520 — playsound=0 */);
|
||||
}
|
||||
|
||||
/* on_guard_killed (seg006:1927) — соперник только что потерял последнее HP.
|
||||
|
||||
@@ -880,7 +880,7 @@ static uint8_t check_grab_run_jump(void)
|
||||
if (grab_col >= 0 && grab_col <= 9 && grab_row >= 0 && grab_row <= 2) {
|
||||
uint8_t tp = (uint8_t)(grab_row * 10 + grab_col);
|
||||
if (grab_tile == TILE_OPENER || grab_tile == TILE_CLOSER)
|
||||
pop_trigger_button(g_room, tp, grab_tile, pop_trob_modif(g_room)[tp]);
|
||||
pop_trigger_button(g_room, tp, grab_tile, pop_trob_modif(g_room)[tp], 1);
|
||||
else if (grab_tile == TILE_LOOSE) {
|
||||
is_guard_notice = 1;
|
||||
make_loose_fall(tp, 1);
|
||||
@@ -2318,7 +2318,7 @@ static void died_on_button(uint8_t room, uint8_t tp, uint8_t code)
|
||||
if (tp % 10 < 9)
|
||||
pop_set_redraw((uint8_t)(tp + 1), POP_RD_FLOOR, 2);
|
||||
}
|
||||
pop_trigger_button(room, tp, button_type, modifier);
|
||||
pop_trigger_button(room, tp, button_type, modifier, 1);
|
||||
}
|
||||
|
||||
/* check_press (seg006:0EC8, упрощ.): Kid стоит на loose → make_loose_fall;
|
||||
@@ -2373,7 +2373,7 @@ static void check_press(void)
|
||||
/* seg006:1707 — ЖИВОЙ жмёт кнопку на 5 кадров, МЁРТВЫЙ ломает её
|
||||
* насовсем. `alive < 0` = жив (соглашение оригинала). */
|
||||
if (Char.alive < 0)
|
||||
pop_trigger_button(btn_room, tp, t, pop_trob_modif(btn_room)[tp]);
|
||||
pop_trigger_button(btn_room, tp, t, pop_trob_modif(btn_room)[tp], 1);
|
||||
else
|
||||
died_on_button(btn_room, tp, t);
|
||||
}
|
||||
@@ -2861,7 +2861,7 @@ void pop_loose_tick(void) __banked
|
||||
if (tt == TILE_OPENER || tt == TILE_CLOSER)
|
||||
pop_trigger_button(g_room, pos,
|
||||
(uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt),
|
||||
pop_trob_modif(g_room)[pos]);
|
||||
pop_trob_modif(g_room)[pos], 1);
|
||||
/* На факеле — отдельный тайл «факел с щебнем» (seg007:1067). */
|
||||
g_fg[pos] = (uint8_t)((tt == TILE_TORCH || tt == TILE_TORCH_DEBRIS)
|
||||
? TILE_TORCH_DEBRIS : TILE_DEBRIS);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -47,7 +47,8 @@ void pop_process_trobs(uint8_t cur_room) __banked;
|
||||
* room/tilepos — кнопка; button_type — её тайл-код; modifier — её modif
|
||||
* (= индекс в LINKLOC). Звать из check_press (pop_map). */
|
||||
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;
|
||||
|
||||
/* Есть ли активные trob в комнате room? Для перерисовки ворот соседа через
|
||||
* шов (roomtest не дёргает шов, пока сосед не анимируется). */
|
||||
|
||||
@@ -562,7 +562,7 @@ int main(void)
|
||||
if (tt == TILE_OPENER || tt == TILE_CLOSER)
|
||||
pop_trigger_button(lroom, lpos,
|
||||
(uint8_t)(tt == TILE_OPENER ? TILE_DEBRIS : tt),
|
||||
pop_trob_modif(lroom)[lpos]);
|
||||
pop_trob_modif(lroom)[lpos], 1);
|
||||
/* На факеле остаётся отдельный тайл «факел с щебнем»
|
||||
* (seg007:1067), а не обычный щебень. */
|
||||
pop_level_set_tile(lroom, lpos,
|
||||
|
||||
@@ -163,7 +163,8 @@ void pop_start_level(void) __banked
|
||||
* col2, row0 её же типом и модификатором (в оригинале и то и другое
|
||||
* берётся из curr_* после get_tile). */
|
||||
uint8_t tp = 2; /* row0*10 + col2 */
|
||||
pop_trigger_button(5, tp, pop_level_tile(5, tp), pop_trob_modif(5)[tp]);
|
||||
pop_trigger_button(5, tp, pop_level_tile(5, tp), pop_trob_modif(5)[tp],
|
||||
0 /* seg003:170 — playsound=0 */);
|
||||
seq = SEQ_FALL;
|
||||
} else if (entry_pose == ENTRY_POSE_RUN) {
|
||||
seq = SEQ_RUN;
|
||||
|
||||
Reference in New Issue
Block a user