/* * pop_kid.h — движок анимации/движения Kid (Фаза K1): интерпретатор * seqtbl (play_seq) + frame_table. Порт SDLPoP seg006.c. Данные — * kid_data.h (pop_extract_kid_data.py). * * K1: только проигрывание последовательностей кадров с авторскими * смещениями (движение = SEQ_DX/DY + frame.dx/dy). Управление (K2) и * коллизия карты (K3) — отдельно. */ #ifndef POP_KID_H #define POP_KID_H #include #include "pop_char.h" #include "kid_data.h" /* kframe — тип кадра для pop_sword_draw */ #include /* atlas_t — набор спрайтов Кида (см. kidp ниже) */ /* Состояние персонажа — общий тип для Кида, стража и Char (pop_char.h). */ extern pop_char_t Kid; /* Флаг сотрясения (seg006 knock): +1 = KNOCK_UP (ряд выше), -1 = KNOCK_DOWN * (свой ряд). Ставит play_seq, читает/сбрасывает pop_check_knock. */ extern int8_t knock; /* Порт next_level (seg000): END_LEVEL в play_seq инкрементит — «уровень * пройден». Читает и обнуляет главный цикл. Пока уровень один, поэтому * там рестарт того же уровня; загрузка следующего — levels_plan.md. */ extern uint8_t pop_next_level; /* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph. * 0 — OK, -1 — ошибка. */ int pop_kid_load(uint8_t npages) __banked; /* Данные анимации (таблица кадров + seqtbl) — отдельная EMM-страница * (kid.ani): в _CODE окна W1/W2 они занимали 3.5 КБ. Грузить ДО * первого kid_init/play_seq. 0 — OK, -1 — ошибка (без них Kid не оживёт). * Страница маппится в W0 внутри play_seq, раз за тик. */ int pop_kid_data_load(const char *path) __banked; /* Прочитать кадр из таблицы страницы данных: tbl_off — KID_BIN_FRAMES_OFF * (Kid) или KID_BIN_GFRAMES_OFF (страж, своя таблица frame_tbl_guard). * Маппит страницу сама; для горячего цикла есть cur_frame. */ void pop_kid_data_frame(uint16_t tbl_off, uint8_t idx, uint8_t *out5); void pop_kid_data_free(void) __banked; /* Атлас chtab_0 «меч в руке» (sword.atl) — спрайт клинка поверх Kid в кадрах * подъёма меча/убирания в ножны (add_sword_to_objtable, seg006:1798). * Грузить ДО initgraph, как и страницы Kid. 0 — OK, -1 — ошибка (тогда * кадры рисуются без клинка). */ /* Зеркальная (вертикально) страница меча — для зелья инверсии. */ int pop_sword_take(const atlas_t *a) __banked; int pop_sword_vflip_take(const atlas_t *m) __banked; void pop_kid_free(void) __banked; /* Сама отрисовка клинка (pop_sword_draw) и блит спрайта chtab_2 по id * (pop_kid_img_blit) переехали в pop_kdraw.c — БАНК 4, к единственному * вызывающему (pop_cdraw.c). Их объявления — в _pop_kdraw.h. */ /* Инициализировать Kid: последовательность seq_id, клетка col/row, * направление dir (-1/0). Ставит x/y по x_bump/y_land. */ void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir); /* Один логический тик: play_seq() — продвинуть последовательность до * следующего кадра (применить SEQ_DX/DY/ACTION/...). */ void kid_tick(void); /* play_seq напрямую (для pop_map start_fall — переход в fall-seq в тот же * physics-шаг, как оригинал). */ void play_seq(void); /* работает с Char — см. pop_kid_play ниже */ /* Активный персонаж (порт Char, seg006) и его загрузка/выгрузка. * Интерпретатор последовательностей общий для Kid и стража: у них общая * seqtbl, но РАЗНЫЕ таблицы кадров (load_frame выбирает по Char.charid). */ extern pop_char_t Char; /* Соперник активного персонажа (порт Opp): ставится вместе с Char через * pop_loadshad_and_opp. Читают ИИ стража и боёвка (char_opp_dist). */ extern pop_char_t Opp; void pop_loadkid(void); void pop_savekid(void); void pop_savekid_state(void); /* Kid = Char, кадр не трогаем */ void pop_loadshad(void); void pop_saveshad(void); void pop_loadshad_and_opp(void); /* Char = Guard, Opp = Kid (seg006:841) */ void pop_saveshad_and_opp(void); /* Guard = Char, Kid = Opp */ void pop_loadkid_and_opp(void); /* Char = Kid, Opp = Guard */ void pop_savekid_and_opp(void); /* Kid = Char, Guard = Opp */ /* Дистанция до соперника в системе Char: >0 впереди, <0 за спиной, * 999 — разные комнаты (порт char_opp_dist, seg006:2135). */ int16_t pop_char_opp_dist(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); /* load_fram_det_col (seg006:0144) — перезагрузить cur_frame под АКТИВНОГО * персонажа (Char) и пересчитать его колонку. Звать сразу после pop_loadkid/ * pop_loadshad и ДО диспетчера control(), как это делают play_kid_frame / * play_guard_frame (seg000:1209/1246): cur_frame один на всех, и без этого * геометрия одного персонажа считается по кадру другого. */ void pop_load_fram_det_col(void); /* Только load_frame (seg006:0293), БЕЗ determine_col. Нужен там, где * оригинал зовёт голый load_frame и пересчёт колонки по x был бы вреден — * например в jump_through_mirror, где ось отражения берётся из curr_col. */ void pop_load_frame(void); /* Какая таблица кадров у персонажа (seg006:0293): 1 — стража, 0 — Кида. * Нужна не только load_frame, но и отрисовке — набор атласов обязан * совпадать с таблицей (тень ходит кадрами и спрайтами КИДА, а дерётся * кадрами стража). */ uint8_t pop_frame_tbl_is_guard(int8_t charid, uint8_t frame); /* Поля текущего кадра kid_frames[Kid.frame] (pop_map, без дублирования * таблицы во втором TU). */ int8_t kid_cur_dx(void); int8_t kid_cur_dy(void); uint8_t kid_cur_flags(void); /* Сменить последовательность (Kid.curr_seq = kid_seq_off[seq_id]) — единая * точка для pop_ctrl/pop_map (иначе каждый дублировал бы kid_seqtbl ~2.3КБ). */ void kid_set_seq(uint8_t seq_id); /* Атласы Кида (chtab_2) и его текущий кадр — набор слота POP_CH_KID общей * отрисовки персонажей (pop_cdraw.c). Наружу отданы как данные, а не через * аксессоры: читаются раз в кадр из другого банка, где вызов стоил бы * дороже самого чтения (данные в _DATA видны всем без трамплина). */ extern atlas_t kidp[]; extern uint8_t kid_npages; extern kframe kid_frame; #endif