/* * 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 */ /* Состояние персонажа — общий тип для Кида, стража и 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; /* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph. * 0 — OK, -1 — ошибка. */ int pop_kid_load(uint8_t npages); /* Данные анимации (таблица кадров + seqtbl) — отдельная EMM-страница * (kid_data.bin): в _CODE окна W1/W2 они занимали 3.5 КБ. Грузить ДО * первого kid_init/play_seq. 0 — OK, -1 — ошибка (без них Kid не оживёт). * Страница маппится в W0 внутри play_seq, раз за тик. */ int pop_kid_data_load(const char *path); /* Прочитать кадр из таблицы страницы данных: 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); /* Атлас chtab_0 «меч в руке» (sword.atl) — спрайт клинка поверх Kid в кадрах * подъёма меча/убирания в ножны (add_sword_to_objtable, seg006:1798). * Грузить ДО initgraph, как и страницы Kid. 0 — OK, -1 — ошибка (тогда * кадры рисуются без клинка). */ int pop_sword_load(void); void pop_kid_free(void); /* Нарисовать клинок chtab_0 поверх персонажа (порт add_sword_to_objtable, * seg006:1798) — общая точка для Кида и стража: меч в оригинале ОДИН на * всех. obj_x/obj_y — та же опорная точка, что у спрайта персонажа. * Возвращает 1 и прямоугольник клинка (для heal), 0 — клинка нет. * Страницу W0 оставляет замапленной: вызывающий делает gfx_w0_unmap. */ uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr, int obj_x, int obj_y, int *rx, int *ry, uint16_t *rw, uint16_t *rh); /* Инициализировать 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); /* Поля текущего кадра kid_frames[Kid.frame] (pop_map, без дублирования * таблицы во втором TU). */ int8_t kid_cur_dx(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); /* Рендер-смещение Kid по ЛОГИЧЕСКОЙ X (порт xpos_in_drawn_room, seg004): * когда комната Kid ≠ отрисованной (straddle у шва), спрайт рисуется со * сдвигом ∓140 (левый сосед −140 / правый +140), при этом Kid.x/коллизия * остаются в кадре комнаты Kid. 0 — комнаты совпадают (обычный случай). * Влияет на kid_draw и метрики футпринта (fore) — heal берёт запомненный * прямоугольник, поэтому согласован автоматически. */ void pop_kid_set_render_dx(int dx); /* Нарисовать текущий кадр Kid (банк GFX_BANK_SPRITE — видео-ОЗУ, без * теневой копии; запоминает прямоугольник для kid_heal). */ void kid_draw(void); /* draw_hurt_splash (seg006:2003): «брызги» урона (image 218) поверх Kid — * звать СРАЗУ после kid_draw в кадре, когда HP убавилось (pop_kid_hurt). */ void kid_draw_splash(void); /* Блит спрайта chtab_2 по id, левым верхним углом в (x,y) — для полосы HP * (id 216 полное деление / 217 пустое). */ void pop_kid_img_blit(uint8_t image, int x, int y); /* Восстановить фон (из теневой копии) на месте ПРЕДЫДУЩЕГО кадра Kid — * вместо перерисовки всего экрана. Звать ПЕРЕД kid_tick/kid_draw. */ void kid_heal(void); /* Метрики последнего kid_draw для футпринта fore-over-Kid (pop_bg * pop_fore_over_kid): obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ * спрайта, w/h — размер кадра. */ int kid_fp_obj_x(void); int kid_fp_obj_y(void); uint16_t kid_fp_width(void); uint16_t kid_fp_height(void); #endif