SprPoP: мёртвое тело больше не приземляется в присед, надпись смерти не залипает
Два независимых фикса, оба проверены в MAME. 1. ПРИЗЕМЛЕНИЕ МЁРТВОГО (симптом «убили Кида, а он сел этажом ниже»). Оригинал спрашивает у приземляющегося, жив ли он (seg005:0173): вся развязка «мягко / средне / разбиться» отведена ЖИВОМУ, телу — своя ветка (добить HP, звук падения насмерть, seq_22). У нас развилки не было, и в глаза это не бросалось только из-за высоты: тело, сброшенное ударом с ОДНОГО ряда, набирает fall_y < 22 — урона нет, «последнее HP» не тратится, ветка «разбился» не выбирается никогда. Труп уходил в мягкое приземление и садился (кадр 109). Цена — один тест байта на вызов land(), то есть на событие касания земли, а не на кадр. Живой путь не изменился ни на операцию. 2. ЗАЛИПАВШАЯ НАДПИСЬ «Press Button to Continue». Счётчик кадров смерти живёт снаружи главного витка и потому переживает возврат на заставку. Ответ игрока кнопкой его обнулял, а выход по таймауту (24 с молчания -> title) уходил мимо сброса. Дальше счётчик оставался израсходованным на всю сессию, и в следующей игре ПЕРВАЯ же смерть мгновенно уводила в title, не показав надписи; лечилось только перезапуском программы. Сброс поставлен на входе в игровой маршрут — закрывает и остальные боковые дороги (выпадение за нижнюю границу, смена уровня). Здесь же довезена связка находок 12/13 аудита: смерть безоружного у обрыва уходит в свою последовательность (seq_81), а прижатие к полу осталось страховкой для прочих веток — снять его целиком не вышло дважды, подробности в комментарии guards.c. Тесты: t_death дополнен обеими сторонами развилки (мёртвый обязан разбиться, живой с той же высоты — сесть без урона), 18 проверок; все 16 наборов host-тестов зелёные. В доску записан CLIMB-VS-GUARD: Кид подтягивается к стражу этажом выше — у нас удар порой смертелен, в оригинале Кид срывается без урона. Цепочка засчитывания удара сверена с оригиналом и совпадает дословно, расходятся входные данные. Лучшая зацепка — ЗВУК: оригинал играет взмах клинка (11) при любом уколе, до всякой проверки попадания, а у нас слышен упор в стену (8) — значит страж не атакует, а сталкивается. Набор звуков проверен и не виноват. Отложено по решению пользователя. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
This commit is contained in:
@@ -999,6 +999,7 @@ uint8_t pop_demo_kid_ai(void) __banked
|
||||
#define FRAME_154_POKING 154
|
||||
#define SEQ_69_ATTACK_WAS_PARRIED 69
|
||||
#define SEQ_74_HIT_BY_SWORD 74
|
||||
#define SEQ_81_PUSHED_OFF_LEDGE 81 /* заколот у обрыва — падает замертво */
|
||||
#define SEQ_85_STABBED_TO_DEATH 85
|
||||
|
||||
/* refractimer (seg002:36) — «отдышка» стража после того, как его ранили. */
|
||||
@@ -1008,10 +1009,40 @@ static const uint8_t REFRACTIMER[NUM_GUARD_SKILLS] =
|
||||
/* hurt_by_sword (seg002): применить попадание к АКТИВНОМУ персонажу.
|
||||
* Без меча в руке любое попадание смертельно; с мечом — минус 1 HP и кадр
|
||||
* «получил удар».
|
||||
* УПРОЩЕНИЕ: ветку «сбит с уступа» (seq_81, когда сзади пусто и до кромки
|
||||
* меньше 4) не портируем — ей нужны тайловые запросы ОТ Char, а pop_map
|
||||
* пока умеет только от Kid. На ровном полу (тайл сзади не пустой) оригинал
|
||||
* идёт ровно нашей веткой. */
|
||||
*
|
||||
* СМЕРТЬ БЕЗОРУЖНОГО БЫВАЕТ ДВУХ ВИДОВ, и выбор между ними делается по
|
||||
* обстановке ПОЗАДИ (seg002): если сзади есть опора или до кромки меньше
|
||||
* четырёх — «заколот на месте» (seq_85); если сзади пусто и от кромки
|
||||
* далеко — «сброшен с уступа» (seq_81), отдельная последовательность,
|
||||
* которая сама отыгрывает падение замертво.
|
||||
*
|
||||
* Вторая ветка появилась 2026-08-31 (docs/sdlpop_audit.md, находка 12):
|
||||
* раньше её не было, потому что считалось, будто тайловые запросы от Char
|
||||
* недоступны — на деле они давно работают, не хватало объявлений.
|
||||
*
|
||||
* Замечание к сцене: падение первым делом убирает меч (start_fall), так
|
||||
* что персонаж, сбитый в пропасть во время боя, к следующему удару уже
|
||||
* безоружен — и попадает сюда же. */
|
||||
/* ПРИЖАТЬ К ПОЛУ СВОЕГО РЯДА — только для ПЕРЕЖИВШЕГО удар.
|
||||
*
|
||||
* В оригинале (seg002, ветка seq_74_hit_by_sword) эти две строки стоят
|
||||
* ВНУТРИ ветки выжившего; смертельные ветки координату не трогают.
|
||||
*
|
||||
* У нас они долго выполнялись безусловно и работали СТРАХОВКОЙ за
|
||||
* отсутствующую ветку «сброшен с уступа»: убитого в воздухе прижимали к
|
||||
* полу, иначе он продолжал падать и выпадал за нижнюю границу, а игра
|
||||
* уходила на рестарт, не показав тела (проверено на живой машине
|
||||
* 2026-08-31 — правка в одиночку ломала смерть).
|
||||
*
|
||||
* Снято ПОСЛЕ появления ветки seq_81: теперь смерть у обрыва отыгрывает
|
||||
* своя последовательность, и страховка больше не нужна. Порядок именно
|
||||
* такой и важен — сперва ветка, потом снятие. */
|
||||
static void hurt_stand_on_floor(void)
|
||||
{
|
||||
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||
Char.fall_y = 0;
|
||||
}
|
||||
|
||||
static void hurt_by_sword(void)
|
||||
{
|
||||
if (Char.alive >= 0) return;
|
||||
@@ -1032,19 +1063,22 @@ static void hurt_by_sword(void)
|
||||
* Костыля «снять бессмертие на время вызова» здесь БОЛЬШЕ НЕТ:
|
||||
* pop_take_hp гасит только урон меньше 100, а тут ровно 100. */
|
||||
pop_take_hp(100);
|
||||
pop_char_set_seq(SEQ_85_STABBED_TO_DEATH);
|
||||
if (pop_tile_behind_char() != 0 || pop_dist_to_edge_weight() < 4)
|
||||
pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* есть опора сзади */
|
||||
else
|
||||
pop_char_set_seq(SEQ_81_PUSHED_OFF_LEDGE); /* сзади обрыв */
|
||||
} else if (Char.charid == CHARID_0_KID && pop_immortal) {
|
||||
/* ЧИТ, уровень 1 и выше: в боевой стойке удары не отнимают HP.
|
||||
* Кадр «получил удар» оставляем — иначе бой перестаёт читаться,
|
||||
* да и оригинал на выживший удар ставит ровно его. */
|
||||
pop_char_set_seq(SEQ_74_HIT_BY_SWORD);
|
||||
hurt_stand_on_floor();
|
||||
} else if (Char.charid != CHARID_4_SKELETON && pop_take_hp(1)) {
|
||||
pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* HP кончились */
|
||||
} else {
|
||||
pop_char_set_seq(SEQ_74_HIT_BY_SWORD);
|
||||
hurt_stand_on_floor();
|
||||
}
|
||||
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||
Char.fall_y = 0;
|
||||
/* seg002:0C1F: у Кида свой звук боли, у соперника свой. */
|
||||
pop_sfx_play((uint8_t)(Char.charid == CHARID_0_KID ? 13 : 12));
|
||||
play_seq();
|
||||
|
||||
Reference in New Issue
Block a user