d014a3f577
Пункт 2 плана закрыт: страж не только замечает Кида, но и идёт к нему и дерётся. Порт по SDLPoP, диспетчер общий — ИИ выставляет те же control_*, что и клавиатура игрока. guards.c (банк 1), порт seg002: - autocontrol_guard_active (737) + kid_in_sight (0A93) + kid_armed (0AC1) + kid_far (09CB); - guard_advance / guard_block / guard_strike с таблицами вероятностей по 12 градациям мастерства (seg002:26..38), бросок prob > prandom(255); - move_2_backward / move_3_up / move_6_shift / move_down_back; - таймеры justblocked / kid_sword_strike / guard_refrac убывают раз в кадр в autocontrol_opponent, как в оригинале. pop_ctrl.c, порт seg005: control_with_sword (964), swordfight (0CDB), sword_strike, parry, forward_with_sword, back_with_sword. Ветвление у Кида и у соперника разное — соперник блокирует только на кадре 152, Кид ещё и по 153 (и тогда последовательность прокручивается сразу). pop_guard.c: guard_skill из данных уровня (12 градаций, вне диапазона -> 3), HP по get_guard_hp (extrastrength[skill] + tbl_guard_hp[уровень]), собственный сид бросков pop_fight_seed — иначе перерисовка стены сбивала бы решения стража. char_opp_dist переехал из банка в pop_kid.c: он нужен по ОБЕ стороны банковой границы — и ИИ, и диспетчеру боёвки. Проверено в MAME (комната 3): страж проходит комнату, встаёт в дистанцию и машет мечом, позы меняются. Урона пока нет — HP-учёт и check_hurt следующим шагом, без них бой не заканчивается. Бюджет В БОЮ (175 кадров): 412 224 – 421 068 = 0.958–0.979 кадра. В покое было 400 800. Запас в худшем кадре ~9 000 — тесно, но в один кадр укладываемся; оптимизация отложена сознательно. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
130 lines
7.5 KiB
C
130 lines
7.5 KiB
C
/*
|
||
* 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 <stdint.h>
|
||
#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) */
|
||
|
||
/* Дистанция до соперника в системе 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);
|
||
|
||
/* Восстановить фон (из теневой копии) на месте ПРЕДЫДУЩЕГО кадра 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
|