8bbc6b4d07
Пункт 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>
74 lines
3.7 KiB
C
74 lines
3.7 KiB
C
/*
|
||
* pop_guard.h — СОСТОЯНИЕ стража и точки входа в его логику.
|
||
*
|
||
* Разделение по окнам (docs/layout_plan_v2.md §4, подтверждено пробником
|
||
* tests/w3bankgfx):
|
||
* - СОСТОЯНИЕ живёт здесь, в W1/W2. Писучие статики __banked-модуля
|
||
* линкуются В СТРАНИЦУ БАНКА и снаружи не читаются, поэтому банк —
|
||
* только код;
|
||
* - ЛОГИКА (ИИ, боёвка) — в guards.c, собираемом как --bank 1=guards.c;
|
||
* - ОТРИСОВКА стража — в pop_gdraw.c, РЕЗИДЕНТЕ W3 (--w3): её зовёт
|
||
* только главный цикл, а из банка резидент недостижим. Так код
|
||
* отрисовки не занимает дефицитные W1/W2 (куча).
|
||
*
|
||
* Поля char_type — как в оригинале (types.h:302), чтобы порт seg005/seg006
|
||
* ложился один в один.
|
||
*/
|
||
#ifndef POP_GUARD_H
|
||
#define POP_GUARD_H
|
||
|
||
#include <stdint.h>
|
||
#include "kid_data.h" /* kframe */
|
||
#include "pop_char.h"
|
||
|
||
/* seqids (types.h:1070) — последовательности стража. */
|
||
#define SEQ_77_GUARD_STAND_INACTIVE 77
|
||
|
||
/* charids (types.h:320) — нужны хотя бы для «скелет не убивается читом». */
|
||
#define CHARID_0_KID 0
|
||
#define CHARID_1_SHADOW 1
|
||
#define CHARID_2_GUARD 2
|
||
#define CHARID_4_SKELETON 4
|
||
#define CHARID_5_PRINCESS 5
|
||
|
||
/* Тип персонажа — в pop_char.h (общий с Кидом и Char). */
|
||
|
||
extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */
|
||
extern uint8_t guardhp_curr, guardhp_max;
|
||
extern int8_t guardhp_delta; /* <0 = в этом кадре отняли HP */
|
||
|
||
/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2,
|
||
* читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */
|
||
extern kframe pop_gframe;
|
||
void pop_guard_load_frame(void);
|
||
|
||
/* Есть ли живой страж в отрисованной комнате (для главного цикла). */
|
||
uint8_t pop_guard_present(void);
|
||
|
||
/* Атласы стража (GUARD\\g0..g4.atl) — грузить ДО initgraph, как Kid.
|
||
* 0 — OK, -1 — ошибка (тогда страж просто не рисуется). */
|
||
int pop_guard_load(void) __banked;
|
||
void pop_guard_free(void) __banked;
|
||
|
||
/* Вход в комнату: поднять стража из данных уровня (порт enter_guard,
|
||
* seg002:0112) либо убрать его, если в комнате стража нет. */
|
||
void pop_guard_enter(uint8_t room);
|
||
|
||
/* Нарисовать текущий кадр стража на back-странице (банк GFX_BANK_SPRITE,
|
||
* как kid_draw) и стереть прошлый (heal). Реализация — pop_gdraw.c
|
||
* (резидент W3), поэтому звать ТОЛЬКО из главного цикла, не из банка. */
|
||
void pop_guard_heal(void) __banked;
|
||
void pop_guard_draw(void) __banked;
|
||
|
||
/* Убрать стража со сцены (смена комнаты / инициализация). */
|
||
void pop_guard_reset(void);
|
||
|
||
/* Чит «убить стража» (seg000:786, K): скелета не берёт. Вызывать только
|
||
* при включённых читах — проверку делает вызывающий. */
|
||
void pop_guard_kill(void);
|
||
|
||
/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */
|
||
void pop_guard_tick(void) __banked;
|
||
|
||
#endif
|