PoP roomtest: интерпретатор последовательностей стал общим (Char) — стражи

Пункт 1 плана стражей.  play_seq был прибит к Киду, поэтому страж стоял на
захардкоженном кадре 166.  Теперь как в оригинале: интерпретатор работает
с АКТИВНЫМ персонажем Char, а вокруг стоят loadkid/savekid и
loadshad/saveshad (порт seg006:809..825).

Почему копия, а не указатель: так в оригинале, и на Z80 это быстрее —
горячий цикл обращается к глобалу абсолютной адресацией, а 16-байтовое
копирование платится один раз на переключение персонажа, тогда как
указатель дал бы индексную адресацию в каждом обращении.

Сопутствующее:
- kid_t и pop_char_t слиты в один pop_char_t (pop_char.h): в оригинале
  char_type один на всех, и без этого общий интерпретатор невозможен.
  Kid получил поля room/charid/sword/alive — они и так нужны боёвке;
- load_frame выбирает таблицу кадров по Char.charid (у стража своя,
  frame_tbl_guard с индексом frame + add_frame − 149, seg006:0293);
- cur_frame разведён на два кэша: страж тикает ПОСЛЕ Кида, и без этого
  kid_draw брал бы кадр стража.  savekid/saveshad раскладывают кадр по
  своему персонажу;
- kid_set_seq пишет ИМЕННО Kid (его зовут pop_ctrl/pop_map вне окна Char,
  иначе loadkid затёр бы), для окна Char добавлен pop_char_set_seq —
  порт seqtbl_offset_char;
- в pop_map 9 голых play_seq() заменены на pop_kid_play() (load+play+save);
- страж входит в комнату через seq_77_guard_stand_inactive (seg002:0208),
  а не через прибитый кадр.

Проверено в MAME: Kid бегает и упирается в стену как прежде, в комнате 12
плиты проваливаются со щебнем (правка задела 9 вызовов play_seq в
физике), страж в комнате 3 рисуется в той же позе, но теперь
curr_seq=0x19A9 и charid=2 — кадр получен прокруткой последовательности,
а не константой.

Бюджет (комната 3, 150 кадров): 380 292..381 282 тактов против
370 140..371 160 до правки, то есть +10 150 (+2.7 %).  Основное — не
копии Char, а то, что страж теперь реально крутит интерпретатор каждый
кадр, а раньше стоял замороженным.  0.887 кадра, запас 48 718.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 10:11:10 +03:00
parent 565ba98852
commit 8bbc6b4d07
7 changed files with 158 additions and 73 deletions
+4
View File
@@ -14,9 +14,13 @@
* seg004 fight/collision) добавляются сюда. * seg004 fight/collision) добавляются сюда.
*/ */
#include "pop_guard.h" #include "pop_guard.h"
#include "pop_kid.h"
void pop_guard_tick(void) __banked void pop_guard_tick(void) __banked
{ {
if (Guard.charid == 0) return; /* стража в комнате нет */ if (Guard.charid == 0) return; /* стража в комнате нет */
/* Анимация: та же seqtbl, что у Кида, но своя таблица кадров —
* выбирается по Char.charid внутри load_frame (seg006:0293). */
pop_guard_play();
/* TODO: порт control_guard/fight (seg005/seg004) — состояние в Guard. */ /* TODO: порт control_guard/fight (seg005/seg004) — состояние в Guard. */
} }
+32
View File
@@ -0,0 +1,32 @@
/*
* pop_char.h — состояние ПЕРСОНАЖА, общее для Кида, стража и активной
* копии Char. Порт char_type (types.h:302).
*
* Зачем отдельным заголовком: интерпретатор последовательностей в
* оригинале один на всех (работает с Char, а вокруг стоят
* loadkid/savekid и loadshad/saveshad, seg006:809). Пока у Кида был свой
* усечённый тип, а у стража свой, общий интерпретатор был невозможен.
* Поля и их порядок — как в char_type, чтобы порт seg005/seg006 ложился
* один в один.
*/
#ifndef POP_CHAR_H
#define POP_CHAR_H
#include <stdint.h>
typedef struct {
uint8_t frame;
uint8_t x, y;
int8_t direction; /* -1 влево, 0 вправо */
int8_t curr_col, curr_row;
uint8_t action;
int8_t fall_x, fall_y;
uint8_t room;
uint8_t repeat;
uint8_t charid; /* 0 = Kid, 2 = страж (см. CHARID_* в pop_guard.h) */
uint8_t sword;
int8_t alive; /* -1 = жив (как в оригинале), 0 = убит */
uint16_t curr_seq; /* адрес в seqtbl (база SEQTBL_BASE) */
} pop_char_t;
#endif
+8 -7
View File
@@ -9,7 +9,7 @@
#include "pop_guard.h" #include "pop_guard.h"
#include "pop_geom.h" #include "pop_geom.h"
#include "pop_level.h" #include "pop_level.h"
#include "pop_kid.h" /* pop_kid_data_frame — таблицы в EMM-странице */ #include "pop_kid.h" /* Char/play_seq — интерпретатор общий */
#include "kid_data.h" #include "kid_data.h"
pop_char_t Guard; pop_char_t Guard;
@@ -80,12 +80,13 @@ void pop_guard_enter(uint8_t room)
Guard.alive = -1; Guard.alive = -1;
Guard.action = 1; /* actions_1_run_jump */ Guard.action = 1; /* actions_1_run_jump */
Guard.fall_x = Guard.fall_y = 0; Guard.fall_x = Guard.fall_y = 0;
/* seq_77_guard_stand_inactive даёт кадр 166 (frame_166_stand_inactive,
* seqtbl.c:266). Интерпретатор последовательностей для стража придёт
* вместе с ИИ (следующая фаза) — сейчас ставим кадр напрямую, чтобы
* страж просто СТОЯЛ и было что рисовать/убивать читом. */
Guard.frame = 166;
guardhp_max = guardhp_curr = 3; /* get_guard_hp: skill 0, уровень 1 */ guardhp_max = guardhp_curr = 3; /* get_guard_hp: skill 0, уровень 1 */
guardhp_delta = 0; guardhp_delta = 0;
pop_guard_load_frame(); /* enter_guard (seg002:0208): seqtbl_offset_char(seq_77_guard_stand_inactive).
* Последовательность крутит общий интерпретатор (pop_guard_play), а
* таблицу кадров он выбирает по Char.charid — у стража своя. */
pop_loadshad();
pop_char_set_seq(SEQ_77_GUARD_STAND_INACTIVE);
pop_saveshad();
pop_guard_play(); /* до первого кадра */
} }
+5 -14
View File
@@ -19,6 +19,10 @@
#include <stdint.h> #include <stdint.h>
#include "kid_data.h" /* kframe */ #include "kid_data.h" /* kframe */
#include "pop_char.h"
/* seqids (types.h:1070) — последовательности стража. */
#define SEQ_77_GUARD_STAND_INACTIVE 77
/* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */ /* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */
#define CHARID_0_KID 0 #define CHARID_0_KID 0
@@ -27,20 +31,7 @@
#define CHARID_4_SKELETON 4 #define CHARID_4_SKELETON 4
#define CHARID_5_PRINCESS 5 #define CHARID_5_PRINCESS 5
typedef struct { /* Тип персонажа — в pop_char.h (общий с Кидом и Char). */
uint8_t frame;
uint8_t x, y;
int8_t direction;
int8_t curr_col, curr_row;
uint8_t action;
int8_t fall_x, fall_y;
uint8_t room;
uint8_t repeat;
uint8_t charid;
uint8_t sword;
int8_t alive; /* -1 = жив (как в оригинале), 0 = убит */
uint16_t curr_seq;
} pop_char_t;
extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */ extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */
extern uint8_t guardhp_curr, guardhp_max; extern uint8_t guardhp_curr, guardhp_max;
+81 -29
View File
@@ -10,12 +10,13 @@
#include <gfx.h> #include <gfx.h>
#include <sprite.h> #include <sprite.h>
#include "pop_kid.h" #include "pop_kid.h"
#include "pop_guard.h" /* Guard/pop_gframe/CHARID_* — интерпретатор общий */
#include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ #include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */
#include "pop_map.h" /* pop_clip_char_top (clip_char) */ #include "pop_map.h" /* pop_clip_char_top (clip_char) */
#include "pop_geom.h" #include "pop_geom.h"
#include "kid_data.h" #include "kid_data.h"
kid_t Kid; pop_char_t Kid;
/* Флаг сотрясения (seg006 knock): ставится seqtbl-командой KNOCK_UP/DOWN /* Флаг сотрясения (seg006 knock): ставится seqtbl-командой KNOCK_UP/DOWN
* в play_seq, потребляется pop_check_knock (pop_map) — трясёт loose-полы. */ * в play_seq, потребляется pop_check_knock (pop_map) — трясёт loose-полы. */
@@ -98,6 +99,16 @@ static const uint8_t *kd_frame_ptr(uint8_t i)
off += (uint16_t)(KD_DATA_OFF + KID_BIN_FRAMES_OFF); off += (uint16_t)(KD_DATA_OFF + KID_BIN_FRAMES_OFF);
return (const uint8_t *)off; return (const uint8_t *)off;
} }
/* То же для таблицы кадров СТРАЖА (frame_tbl_guard дописана в ту же
* страницу данных, см. pop_extract_kid_data.py). */
static const uint8_t *kd_gframe_ptr(uint8_t i)
{
uint16_t off = (uint16_t)i;
off += (uint16_t)(off << 2);
off += (uint16_t)(KD_DATA_OFF + KID_BIN_GFRAMES_OFF);
return (const uint8_t *)off;
}
#define KD_SEQ(cs) (*((const uint8_t *)(KD_DATA_OFF + KID_BIN_SEQTBL_OFF) + \ #define KD_SEQ(cs) (*((const uint8_t *)(KD_DATA_OFF + KID_BIN_SEQTBL_OFF) + \
(uint16_t)((cs) - SEQTBL_BASE))) (uint16_t)((cs) - SEQTBL_BASE)))
/* Байт seqtbl по адресу оригинала (база SEQTBL_BASE); ТОЛЬКО под замапленной /* Байт seqtbl по адресу оригинала (база SEQTBL_BASE); ТОЛЬКО под замапленной
@@ -106,8 +117,25 @@ static const uint8_t *kd_frame_ptr(uint8_t i)
static uint8_t kdat_blk, kdat_page, kdat_ok; static uint8_t kdat_blk, kdat_page, kdat_ok;
/* Текущий кадр (порт глобала cur_frame, seg006): 5 байт вместо таблицы. */ /* Текущий кадр (порт глобала cur_frame, seg006): 5 байт вместо таблицы.
* Заполняется load_frame для АКТИВНОГО персонажа (Char), поэтому на
* выходе из play_seq его надо сохранить в кэш своего персонажа: страж
* тикает ПОСЛЕ Кида, и без разделения kid_draw взял бы кадр стража. */
static kframe cur_frame; static kframe cur_frame;
static kframe kid_frame; /* кадр Kid для kid_draw */
/* Char (seg006): АКТИВНЫЙ персонаж. Интерпретатор последовательностей в
* оригинале работает не с Kid и не с Guard, а с этой копией, а вокруг
* стоят loadkid/savekid и loadshad/saveshad (seg006:809..825). Копия, а
* не указатель, — и так же, как в оригинале, и быстрее на Z80: обращения
* в горячем цикле идут абсолютной адресацией глобала, а 16-байтовое
* копирование платится один раз на переключение персонажа. */
pop_char_t Char;
void pop_loadkid(void) { Char = Kid; }
void pop_savekid(void) { Kid = Char; kid_frame = cur_frame; }
void pop_loadshad(void) { Char = Guard; }
void pop_saveshad(void) { Guard = Char; pop_gframe = cur_frame; }
int pop_kid_data_load(const char *path) int pop_kid_data_load(const char *path)
{ {
@@ -166,7 +194,19 @@ void pop_kid_data_frame(uint16_t tbl_off, uint8_t idx, uint8_t *out5)
* cur_frame. Звать ТОЛЬКО когда страница данных замаплена в W0. */ * cur_frame. Звать ТОЛЬКО когда страница данных замаплена в W0. */
static void load_frame(void) static void load_frame(void)
{ {
const uint8_t *f = kd_frame_ptr(Kid.frame); /* Таблица кадров зависит от персонажа (seg006:0293): у Kid своя, у
* стража frame_tbl_guard с индексом frame + add_frame 149, где
* add_frame = 70 для кадров 102..106 (seg006:535). */
const uint8_t *f;
if (Char.charid == CHARID_2_GUARD) {
int16_t idx = (int16_t)Char.frame;
if (idx >= 102 && idx < 107) idx += 70;
idx -= 149;
if (idx < 0 || idx >= KID_NGFRAMES) { cur_frame.image = 255; return; }
f = kd_gframe_ptr((uint8_t)idx);
} else {
f = kd_frame_ptr(Char.frame);
}
cur_frame.image = f[0]; cur_frame.image = f[0];
cur_frame.dx = (int8_t)f[1]; cur_frame.dx = (int8_t)f[1];
cur_frame.dy = (int8_t)f[2]; cur_frame.dy = (int8_t)f[2];
@@ -176,8 +216,8 @@ static void load_frame(void)
static uint8_t char_dx_forward(int8_t dx) static uint8_t char_dx_forward(int8_t dx)
{ {
if (Kid.direction < 0) dx = (int8_t)-dx; if (Char.direction < 0) dx = (int8_t)-dx;
return (uint8_t)(Kid.x + dx); return (uint8_t)(Char.x + dx);
} }
/* Аксессоры к текущему кадру (для pop_map): читают cur_frame, НЕ таблицу — /* Аксессоры к текущему кадру (для pop_map): читают cur_frame, НЕ таблицу —
@@ -189,6 +229,11 @@ uint8_t kid_cur_flags(void) { return cur_frame.flags; }
* pop_ctrl/pop_map зовут ЕЁ, а не дублируют kid_seq_off/kid_seqtbl). */ * pop_ctrl/pop_map зовут ЕЁ, а не дублируют kid_seq_off/kid_seqtbl). */
void kid_set_seq(uint8_t seq_id) { Kid.curr_seq = kid_seq_off[seq_id]; } void kid_set_seq(uint8_t seq_id) { Kid.curr_seq = kid_seq_off[seq_id]; }
/* То же для АКТИВНОГО персонажа — внутри окна Char (порт
* seqtbl_offset_char, seg006). Нужен стражу: его последовательность
* ставится, когда Char уже загружен из Guard. */
void pop_char_set_seq(uint8_t seq_id) { Char.curr_seq = kid_seq_off[seq_id]; }
/* play_seq (seg006.c:570): крутит опкоды до КАДРА (байт < 0xF0). /* play_seq (seg006.c:570): крутит опкоды до КАДРА (байт < 0xF0).
* Страница данных маппится в W0 на ВЕСЬ разбор — один map/unmap за вызов * Страница данных маппится в W0 на ВЕСЬ разбор — один map/unmap за вызов
* (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце — * (в тике, до отрисовки: конфликта с атласом в W0 нет). В конце —
@@ -198,42 +243,42 @@ void play_seq(void)
if (!kdat_ok) return; if (!kdat_ok) return;
gfx_w0_map(kdat_page); gfx_w0_map(kdat_page);
for (;;) { for (;;) {
uint8_t cmd = SEQ(Kid.curr_seq); uint8_t cmd = SEQ(Char.curr_seq);
Kid.curr_seq++; Char.curr_seq++;
switch (cmd) { switch (cmd) {
case 0xFB: /* DX */ case 0xFB: /* DX */
Kid.x = char_dx_forward((int8_t)SEQ(Kid.curr_seq)); Char.x = char_dx_forward((int8_t)SEQ(Char.curr_seq));
Kid.curr_seq++; Char.curr_seq++;
break; break;
case 0xFA: /* DY */ case 0xFA: /* DY */
Kid.y = (uint8_t)(Kid.y + (int8_t)SEQ(Kid.curr_seq)); Char.y = (uint8_t)(Char.y + (int8_t)SEQ(Char.curr_seq));
Kid.curr_seq++; Char.curr_seq++;
break; break;
case 0xFE: /* FLIP */ case 0xFE: /* FLIP */
Kid.direction = (int8_t)~Kid.direction; Char.direction = (int8_t)~Char.direction;
break; break;
case 0xF7: /* JMP_IF_FEATHER: нет пера -> пропустить адрес */ case 0xF7: /* JMP_IF_FEATHER: нет пера -> пропустить адрес */
Kid.curr_seq += 2; Char.curr_seq += 2;
break; break;
case 0xFF: { /* JMP */ case 0xFF: { /* JMP */
uint16_t a = (uint16_t)(SEQ(Kid.curr_seq) | uint16_t a = (uint16_t)(SEQ(Char.curr_seq) |
((uint16_t)SEQ((uint16_t)(Kid.curr_seq + 1)) << 8)); ((uint16_t)SEQ((uint16_t)(Char.curr_seq + 1)) << 8));
Kid.curr_seq = a; Char.curr_seq = a;
break; break;
} }
case 0xFD: /* UP */ case 0xFD: /* UP */
Kid.curr_row--; Char.curr_row--;
break; break;
case 0xFC: /* DOWN */ case 0xFC: /* DOWN */
Kid.curr_row++; Char.curr_row++;
break; break;
case 0xF9: /* ACTION */ case 0xF9: /* ACTION */
Kid.action = SEQ(Kid.curr_seq); Char.action = SEQ(Char.curr_seq);
Kid.curr_seq++; Char.curr_seq++;
break; break;
case 0xF8: /* SET_FALL */ case 0xF8: /* SET_FALL */
Kid.fall_x = (int8_t)SEQ(Kid.curr_seq); Kid.curr_seq++; Char.fall_x = (int8_t)SEQ(Char.curr_seq); Char.curr_seq++;
Kid.fall_y = (int8_t)SEQ(Kid.curr_seq); Kid.curr_seq++; Char.fall_y = (int8_t)SEQ(Char.curr_seq); Char.curr_seq++;
break; break;
case 0xF5: /* KNOCK_UP: сотрясение трясёт loose РЯДОМ ВЫШЕ (check_knock) */ case 0xF5: /* KNOCK_UP: сотрясение трясёт loose РЯДОМ ВЫШЕ (check_knock) */
knock = 1; knock = 1;
@@ -242,20 +287,20 @@ void play_seq(void)
knock = -1; knock = -1;
break; break;
case 0xF2: /* SOUND — потребить 1 байт */ case 0xF2: /* SOUND — потребить 1 байт */
Kid.curr_seq++; Char.curr_seq++;
break; break;
case 0xF1: /* END_LEVEL */ case 0xF1: /* END_LEVEL */
break; break;
case 0xF3: /* GET_ITEM: аргумент 1 — применить эффект предмета case 0xF3: /* GET_ITEM: аргумент 1 — применить эффект предмета
* (порт play_seq -> proc_get_object, seg006) */ * (порт play_seq -> proc_get_object, seg006) */
if (SEQ(Kid.curr_seq) == 1) if (SEQ(Char.curr_seq) == 1)
pop_proc_get_object(); pop_proc_get_object();
Kid.curr_seq++; Char.curr_seq++;
break; break;
case 0xF6: /* DIE nop */ case 0xF6: /* DIE nop */
break; break;
default: /* КАДР */ default: /* КАДР */
Kid.frame = cmd; Char.frame = cmd;
load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */ load_frame(); /* cur_frame := таблица[frame] (страница ещё в W0) */
gfx_w0_unmap(); gfx_w0_unmap();
return; return;
@@ -263,6 +308,12 @@ void play_seq(void)
} }
} }
/* Прокрутить последовательность КОНКРЕТНОГО персонажа (порт связки
* loadkid/play_seq/savekid, seg006). Звать вместо голого play_seq: тот
* работает с Char и сам по себе не знает, чей это персонаж. */
void pop_kid_play(void) { pop_loadkid(); play_seq(); pop_savekid(); }
void pop_guard_play(void) { pop_loadshad(); play_seq(); pop_saveshad(); }
void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir) void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir)
{ {
Kid.curr_col = col; Kid.curr_col = col;
@@ -272,13 +323,14 @@ void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir)
Kid.y = (uint8_t)pop_y_land[row + 1]; Kid.y = (uint8_t)pop_y_land[row + 1];
Kid.fall_x = Kid.fall_y = 0; Kid.fall_x = Kid.fall_y = 0;
Kid.repeat = 0; Kid.repeat = 0;
Kid.charid = 0; /* charid_0_kid: своя таблица кадров */
Kid.curr_seq = kid_seq_off[seq_id]; Kid.curr_seq = kid_seq_off[seq_id];
play_seq(); /* до первого кадра */ pop_kid_play(); /* до первого кадра */
} }
void kid_tick(void) void kid_tick(void)
{ {
play_seq(); pop_kid_play();
} }
/* Отрисовка текущего кадра: obj_x=(char_x+dx)*2-116, obj_y=char_y+dy /* Отрисовка текущего кадра: obj_x=(char_x+dx)*2-116, obj_y=char_y+dy
@@ -387,7 +439,7 @@ void kid_heal(void)
void kid_draw(void) void kid_draw(void)
{ {
kframe fr = cur_frame; /* load_frame уже положил кадр (play_seq) */ kframe fr = kid_frame; /* кадр Kid, сохранённый savekid */
int fwd, obj_x, obj_y, top; int fwd, obj_x, obj_y, top;
uint8_t page, idx; uint8_t page, idx;
const uint8_t *img; const uint8_t *img;
+19 -14
View File
@@ -11,20 +11,10 @@
#define POP_KID_H #define POP_KID_H
#include <stdint.h> #include <stdint.h>
#include "pop_char.h"
/* Состояние персонажа (подмножество char_type, types.h). */ /* Состояние персонажа — общий тип для Кида, стража и Char (pop_char.h). */
typedef struct { extern pop_char_t Kid;
uint8_t frame;
uint8_t x, y;
int8_t direction; /* -1 влево, 0 вправо */
int8_t curr_col, curr_row;
uint8_t action;
int8_t fall_x, fall_y;
uint8_t repeat;
uint16_t curr_seq; /* адрес в seqtbl (база SEQTBL_BASE) */
} kid_t;
extern kid_t Kid;
/* Флаг сотрясения (seg006 knock): +1 = KNOCK_UP (ряд выше), -1 = KNOCK_DOWN /* Флаг сотрясения (seg006 knock): +1 = KNOCK_UP (ряд выше), -1 = KNOCK_DOWN
* (свой ряд). Ставит play_seq, читает/сбрасывает pop_check_knock. */ * (свой ряд). Ставит play_seq, читает/сбрасывает pop_check_knock. */
@@ -63,7 +53,22 @@ void kid_tick(void);
/* play_seq напрямую (для pop_map start_fall — переход в fall-seq в тот же /* play_seq напрямую (для pop_map start_fall — переход в fall-seq в тот же
* physics-шаг, как оригинал). */ * physics-шаг, как оригинал). */
void play_seq(void); void play_seq(void); /* работает с Char — см. pop_kid_play ниже */
/* Активный персонаж (порт Char, seg006) и его загрузка/выгрузка.
* Интерпретатор последовательностей общий для Kid и стража: у них общая
* seqtbl, но РАЗНЫЕ таблицы кадров (load_frame выбирает по Char.charid). */
extern pop_char_t Char;
void pop_loadkid(void);
void pop_savekid(void);
void pop_loadshad(void);
void pop_saveshad(void);
/* Прокрутить последовательность персонажа целиком (load + play_seq +
* save). Звать вместо голого play_seq. */
void pop_char_set_seq(uint8_t seq_id); /* seq активного персонажа */
void pop_kid_play(void);
void pop_guard_play(void);
/* Поля текущего кадра kid_frames[Kid.frame] (pop_map, без дублирования /* Поля текущего кадра kid_frames[Kid.frame] (pop_map, без дублирования
* таблицы во втором TU). */ * таблицы во втором TU). */
+9 -9
View File
@@ -442,7 +442,7 @@ static void land(void)
if (deadly || take_hp(1)) { /* 3+ этажа или последнее HP */ if (deadly || take_hp(1)) { /* 3+ этажа или последнее HP */
take_hp(100); take_hp(100);
kid_set_seq(SEQ_22_CRUSHED); kid_set_seq(SEQ_22_CRUSHED);
play_seq(); pop_kid_play();
determine_col(); determine_col();
pop_kid_dead = 1; pop_kid_dead = 1;
return; return;
@@ -451,7 +451,7 @@ static void land(void)
} }
Kid.fall_x = Kid.fall_y = 0; Kid.fall_x = Kid.fall_y = 0;
kid_set_seq(seq); kid_set_seq(seq);
play_seq(); pop_kid_play();
determine_col(); determine_col();
} }
@@ -465,7 +465,7 @@ static void start_fall(void)
else if (frame == 44) seq = SEQ_21_FALL_RUNJUMP; else if (frame == 44) seq = SEQ_21_FALL_RUNJUMP;
else seq = SEQ_7_FALL; /* frame 9 + stand/step/crouch */ else seq = SEQ_7_FALL; /* frame 9 + stand/step/crouch */
kid_set_seq(seq); kid_set_seq(seq);
play_seq(); pop_kid_play();
determine_col(); determine_col();
/* Проверки «упал в стену» — ТОЛЬКО пока персонаж В комнате (curr_row<=2). /* Проверки «упал в стену» — ТОЛЬКО пока персонаж В комнате (curr_row<=2).
* Ниже нижнего ряда (curr_row=3 после inc — падение сквозь дыру loose) * Ниже нижнего ряда (curr_row=3 после inc — падение сквозь дыру loose)
@@ -531,7 +531,7 @@ static void check_grab(void)
Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1];
Kid.fall_y = 0; Kid.fall_y = 0;
kid_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR); kid_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR);
play_seq(); pop_kid_play();
determine_col(); determine_col();
grab_timer = 12; /* (TODO: sound grab) */ grab_timer = 12; /* (TODO: sound grab) */
} }
@@ -886,7 +886,7 @@ static void bumped_fall(void)
Kid.fall_x = 0; Kid.fall_x = 0;
} else { } else {
kid_set_seq(SEQ_45_BUMPFALL); /* (TODO: bumped_sound) */ kid_set_seq(SEQ_45_BUMPFALL); /* (TODO: bumped_sound) */
play_seq(); pop_kid_play();
} }
} }
@@ -920,7 +920,7 @@ static void bumped_floor(void)
kid_set_seq(SEQ_46_HARDBUMP); kid_set_seq(SEQ_46_HARDBUMP);
else else
kid_set_seq(SEQ_47_BUMP); /* (TODO: bumped_sound) */ kid_set_seq(SEQ_47_BUMP); /* (TODO: bumped_sound) */
play_seq(); pop_kid_play();
} }
/* check_bumped (seg004): если передний край ЗАШЁЛ за грань стены — выровнять /* check_bumped (seg004): если передний край ЗАШЁЛ за грань стены — выровнять
@@ -1157,14 +1157,14 @@ static void fell_on_your_head(void)
pop_kid_hurt = 1; /* hitp_delta<0 → draw_hurt_splash */ pop_kid_hurt = 1; /* hitp_delta<0 → draw_hurt_splash */
if (take_hp(1)) { if (take_hp(1)) {
kid_set_seq(SEQ_22_CRUSHED); kid_set_seq(SEQ_22_CRUSHED);
play_seq(); pop_kid_play();
determine_col(); determine_col();
pop_kid_dead = 1; pop_kid_dead = 1;
} else if (frame != FRAME_109_CROUCH) { } else if (frame != FRAME_109_CROUCH) {
if (get_tile_behind_char() == TILE_EMPTY) if (get_tile_behind_char() == TILE_EMPTY)
Kid.x = (uint8_t)char_dx_forward(-2); Kid.x = (uint8_t)char_dx_forward(-2);
kid_set_seq(SEQ_52_LOOSE_FELL_ON_KID); kid_set_seq(SEQ_52_LOOSE_FELL_ON_KID);
play_seq(); pop_kid_play();
determine_col(); determine_col();
} }
} }
@@ -1430,7 +1430,7 @@ static void spiked(uint8_t tilepos)
Kid.fall_y = 0; Kid.fall_y = 0;
take_hp(100); /* (TODO: sound_48_spiked) */ take_hp(100); /* (TODO: sound_48_spiked) */
kid_set_seq(SEQ_51_SPIKED); kid_set_seq(SEQ_51_SPIKED);
play_seq(); pop_kid_play();
determine_col(); determine_col();
pop_kid_dead = 1; pop_kid_dead = 1;
} }