79ea473910
Первый самостоятельный кусок ИИ стража (план, пункт 2). В оригинале у соперника нет своего диспетчера: autocontrol_* (seg002) выставляет те же глобалы control_*, что и ввод игрока, а дальше исполняется общий control(). Инфраструктура под это встала прошлым коммитом, здесь — сама логика. Порт: - check_can_guard_see_kid (seg003:688) — луч видимости по ряду: стены и верхи дверей рвут его совсем, loose/чомпер/дыра/неподнятые ворота дают «вижу, но не пойду». В guards.c (банк 1); - Opp + loadshad_and_opp (seg006:841) и char_opp_dist (seg006:2135); - autocontrol_guard_inactive (seg002:710) + move_* (seg002:0706..); - ветки control(): control_guard_inactive (seg006:2123) и draw_sword (seg005:945) — соперник уходит сразу в seq_90 en garde; - pop_guard_tick перестроен по play_guard_frame (seg000:1246): окно Char/Opp вокруг ИИ, диспетчера и play_seq. По дороге: - Kid.alive не выставлялся (= 0 = «мёртв» в семантике оригинала), из-за чего луч видимости не мог сработать в принципе — ставим -1 в kid_init; - Kid.room не выставлялась вовсе; условие Kid.room == Guard.room всегда было ложным. Ставим в enter_room (полная модель Kid.room != drawn_room у шва по-прежнему впереди); - pop_tile_at — тайл текущей комнаты наружу из pop_map (луч видимости); - control_x/y/shift открыты в шину: ИИ заполняет оси как есть, без flip_control_x (его «вперёд» уже в системе персонажа). Проверено в MAME (комната 3, страж на tile 17): страж переходит из стойки 166 в 171 stand_with_sword, sword=2, can_guard_see_kid=2; ввод игрока не пострадал. Клинок отдельным спрайтом пока не рисуется — sword.atl содержит только кадры 229..237 (подъём меча Кидом), остальные строки sword_tbl приедут с боёвкой. Бюджет (225 кадров, комната 3): 384 168 – 384 636 тактов, 0.893–0.895 кадра, запас 45 364. Прошлый замер 382 584 – 383 064 — шаг стоил ~1 570. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
97 lines
5.0 KiB
C
97 lines
5.0 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:1052+) — последовательности стража. */
|
||
#define SEQ_55_DRAW_SWORD 55
|
||
#define SEQ_77_GUARD_STAND_INACTIVE 77
|
||
#define SEQ_80_STAND_FLIPPED 80
|
||
#define SEQ_90_EN_GARDE 90
|
||
|
||
/* frameids (types.h:927) и состояния меча (types.h:332). */
|
||
#define FRAME_166_STAND_INACTIVE 166
|
||
#define SWORD_0_SHEATHED 0
|
||
#define SWORD_2_DRAWN 2
|
||
|
||
/* 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 */
|
||
|
||
/* Видит ли страж Кида (порт can_guard_see_kid, seg003:688):
|
||
* 0 — не видит, 1 — видит, но не пойдёт (между ними loose/чомпер/дыра),
|
||
* 2 — видит и пойдёт. Считает pop_check_can_guard_see_kid (банк) раз в
|
||
* кадр ДО логики персонажей; читают ИИ (autocontrol_*) и боёвка. */
|
||
extern int8_t can_guard_see_kid;
|
||
|
||
/* Кид «нашумел» (порт is_guard_notice, seg000:364): заставляет неактивного
|
||
* стража обернуться, даже если Кид у него за спиной. Ставит источник шума,
|
||
* снимает сам ИИ. */
|
||
extern int8_t is_guard_notice;
|
||
|
||
/* Луч видимости стража — раз в кадр, ДО тиков персонажей (в оригинале
|
||
* check_can_guard_see_kid стоит в play_frame перед play_kid_frame). */
|
||
void pop_check_can_guard_see_kid(void) __banked;
|
||
|
||
/* Текущий кадр стража (порт 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
|