PoP roomtest: каркас стражей в БАНКЕ + режим читов (K — убить стража)
Раскладка (по подтверждённой пробником модели, tests/w3bankgfx): - roomtest переведён на MEMORY=huge: та же small-раскладка резидента (CODE в W1, DATA за ним) плюс банки кода в W3; - guards.c собирается как --bank 1=guards.c — там будет ИИ и боёвка; - СОСТОЯНИЕ стража живёт в W1/W2 (pop_guard.c): писучие статики __banked-модуля линкуются в страницу банка и снаружи не читаются, так что банк — только код; - поля pop_char_t повторяют char_type оригинала (types.h:302), чтобы порт seg005/seg006 ложился один в один. Режим читов (порт cheats_enabled, seg000:111): глобальный флаг pop_cheats, на время разработки включается в main. Реализован один чит — K (kill guard, seg000:786): скелета не берёт, живому стражу ставит guardhp_delta = -guardhp_curr и alive = 0. Обработка по фронту нажатия. Остальные читы оригинала не портированы. Проверено в MAME: приложение в huge-раскладке стартует, комната рисуется, Kid бегает — то есть банкованный pop_guard_tick() зовётся каждый кадр через трамплин и корректно возвращается; K не роняет приложение (стража в комнате пока нет). Банк занят на 6 Б из 16384. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,14 +5,14 @@ PROJ_ROOT := $(abspath $(CURDIR)/../../..)
|
|||||||
EXAMPLE := roomtest
|
EXAMPLE := roomtest
|
||||||
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе
|
||||||
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge).
|
||||||
MEMORY ?= small
|
MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи)
|
||||||
# --w3 берёт ОДИН файл на флаг. В W3-резиденте держим ТОЛЬКО pop_bg —
|
# --w3 берёт ОДИН файл на флаг. В W3-резиденте держим ТОЛЬКО pop_bg —
|
||||||
# отрисовку тайлов: она большая и её зовёт лишь главный цикл. Всё
|
# отрисовку тайлов: она большая и её зовёт лишь главный цикл. Всё
|
||||||
# остальное живёт в W1/W2, чтобы оставаться достижимым из будущих
|
# остальное живёт в W1/W2, чтобы оставаться достижимым из будущих
|
||||||
# __banked-модулей (стражи): из банка резидентная страница W3 не видна —
|
# __banked-модулей (стражи): из банка резидентная страница W3 не видна —
|
||||||
# ни напрямую, ни транзитивно. См. docs/layout_plan_v2.md §2 (R2), §4.
|
# ни напрямую, ни транзитивно. См. docs/layout_plan_v2.md §2 (R2), §4.
|
||||||
EXTRA_FLAGS ?= --gfx 256 --w3 pop_bg.c
|
EXTRA_FLAGS ?= --gfx 256 --w3 pop_bg.c --bank 1=guards.c
|
||||||
EXTRA_SRCS := pop_kid.c pop_ctrl.c pop_level.c pop_map.c pop_trob.c pop_redraw.c pop_geom.c
|
EXTRA_SRCS := pop_kid.c pop_ctrl.c pop_level.c pop_map.c pop_trob.c pop_redraw.c pop_geom.c pop_guard.c
|
||||||
|
|
||||||
BG_DIR := $(CURDIR)/../poc/res/bg
|
BG_DIR := $(CURDIR)/../poc/res/bg
|
||||||
KID_DIR := $(CURDIR)/../poc/res/kid
|
KID_DIR := $(CURDIR)/../poc/res/kid
|
||||||
@@ -42,4 +42,4 @@ $(KID_DATA): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py
|
|||||||
kid_data.h $(KID_BIN): $(TC)/pop_extract_kid_data.py
|
kid_data.h $(KID_BIN): $(TC)/pop_extract_kid_data.py
|
||||||
cd $(TC) && python3 pop_extract_kid_data.py
|
cd $(TC) && python3 pop_extract_kid_data.py
|
||||||
|
|
||||||
$(EXAMPLE).exe: pop_bg.c pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA)
|
$(EXAMPLE).exe: guards.c pop_guard.c pop_guard.h pop_cheat.h pop_bg.c pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* guards.c — ЛОГИКА стража, собирается в БАНК (--bank 1=guards.c, окно W3).
|
||||||
|
*
|
||||||
|
* Правила банка (проверено пробником tests/w3bankgfx):
|
||||||
|
* - состояние держим в W1/W2 (pop_guard.c): статики банка линкуются в
|
||||||
|
* страницу банка и снаружи не читаются;
|
||||||
|
* - звать можно W1/W2-код (pop_map, pop_kid, pop_geom, libc, примитивы
|
||||||
|
* libbgi) — скобка _bgi_begin/_bgi_end возвращает страницу БАНКА;
|
||||||
|
* - звать НЕЛЬЗЯ резидент W3 (pop_bg) — ни напрямую, ни транзитивно:
|
||||||
|
* пока исполняется банк, резидентной страницы в адресном пространстве
|
||||||
|
* нет. Поэтому фон/окклюзию за стража рисует главный цикл.
|
||||||
|
*
|
||||||
|
* Сейчас — каркас: тик есть, ИИ и боёвка (порт seg005 control_guard*,
|
||||||
|
* seg004 fight/collision) добавляются сюда.
|
||||||
|
*/
|
||||||
|
#include "pop_guard.h"
|
||||||
|
|
||||||
|
void pop_guard_tick(void) __banked
|
||||||
|
{
|
||||||
|
if (Guard.charid == 0) return; /* стража в комнате нет */
|
||||||
|
/* TODO: порт control_guard/fight (seg005/seg004) — состояние в Guard. */
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* pop_cheat.h — режим читов (порт cheats_enabled, seg000:111).
|
||||||
|
*
|
||||||
|
* В оригинале включается ключом командной строки megahit/debug и меняет
|
||||||
|
* набор доступных клавиш. У нас конфигурации/UI пока нет, поэтому это
|
||||||
|
* просто глобальный флаг: main ставит его при старте (на время разработки —
|
||||||
|
* ВКЛЮЧЁН). Клавиши читов обрабатывает главный цикл.
|
||||||
|
*
|
||||||
|
* Реализовано: K — убить стража (seg000:786). Остальные читы оригинала
|
||||||
|
* (время, перо, воскрешение, переход по комнатам) пока не портированы.
|
||||||
|
*/
|
||||||
|
#ifndef POP_CHEAT_H
|
||||||
|
#define POP_CHEAT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern uint8_t pop_cheats; /* 0 = выключены, иначе включены */
|
||||||
|
|
||||||
|
#define KBD_CHEAT_KILL 0x42 /* K (PS/2 set 2) — kill guard */
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* pop_guard.c — состояние стража (W1/W2). См. pop_guard.h.
|
||||||
|
* Логика — в банке (guards.c), отрисовка — из главного цикла.
|
||||||
|
*/
|
||||||
|
#include "pop_guard.h"
|
||||||
|
|
||||||
|
pop_char_t Guard;
|
||||||
|
uint8_t guardhp_curr, guardhp_max;
|
||||||
|
int8_t guardhp_delta;
|
||||||
|
|
||||||
|
uint8_t pop_guard_present(void)
|
||||||
|
{
|
||||||
|
return (uint8_t)(Guard.charid != 0 && Guard.alive < 0 && guardhp_curr != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_guard_reset(void)
|
||||||
|
{
|
||||||
|
Guard.charid = 0;
|
||||||
|
Guard.alive = 0;
|
||||||
|
Guard.direction = 0;
|
||||||
|
guardhp_curr = 0;
|
||||||
|
guardhp_delta = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kill guard cheat (seg000:786):
|
||||||
|
* if (Guard.charid != charid_4_skeleton) {
|
||||||
|
* guardhp_delta = -guardhp_curr;
|
||||||
|
* Guard.alive = 0;
|
||||||
|
* }
|
||||||
|
* Скелета чит не убивает — он и в оригинале бессмертен. */
|
||||||
|
void pop_guard_kill(void)
|
||||||
|
{
|
||||||
|
if (Guard.charid == CHARID_4_SKELETON) return;
|
||||||
|
if (!pop_guard_present()) return;
|
||||||
|
guardhp_delta = (int8_t)-guardhp_curr;
|
||||||
|
guardhp_curr = 0;
|
||||||
|
Guard.alive = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* pop_guard.h — СОСТОЯНИЕ стража и точки входа в его логику.
|
||||||
|
*
|
||||||
|
* Разделение по окнам (docs/layout_plan_v2.md §4, подтверждено пробником
|
||||||
|
* tests/w3bankgfx):
|
||||||
|
* - СОСТОЯНИЕ живёт здесь, в W1/W2. Писучие статики __banked-модуля
|
||||||
|
* линкуются В СТРАНИЦУ БАНКА и снаружи не читаются, поэтому банк —
|
||||||
|
* только код;
|
||||||
|
* - ЛОГИКА (ИИ, боёвка) — в guards.c, собираемом как --bank 1=guards.c;
|
||||||
|
* - ОТРИСОВКА стража — из W1/W2 (рядом с kid_draw): из банка примитивы
|
||||||
|
* libbgi звать можно, но резидент W3 (pop_bg) — нельзя, поэтому слой
|
||||||
|
* фона/окклюзии за стража делает главный цикл.
|
||||||
|
*
|
||||||
|
* Поля char_type — как в оригинале (types.h:302), чтобы порт seg005/seg006
|
||||||
|
* ложился один в один.
|
||||||
|
*/
|
||||||
|
#ifndef POP_GUARD_H
|
||||||
|
#define POP_GUARD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t frame;
|
||||||
|
uint8_t x, y;
|
||||||
|
int8_t direction;
|
||||||
|
int8_t curr_col, curr_row;
|
||||||
|
uint8_t action;
|
||||||
|
int8_t fall_x, fall_y;
|
||||||
|
uint8_t room;
|
||||||
|
uint8_t repeat;
|
||||||
|
uint8_t charid;
|
||||||
|
uint8_t sword;
|
||||||
|
int8_t alive; /* -1 = жив (как в оригинале), 0 = убит */
|
||||||
|
uint16_t curr_seq;
|
||||||
|
} pop_char_t;
|
||||||
|
|
||||||
|
extern pop_char_t Guard; /* страж ТЕКУЩЕЙ комнаты (как в оригинале) */
|
||||||
|
extern uint8_t guardhp_curr, guardhp_max;
|
||||||
|
extern int8_t guardhp_delta; /* <0 = в этом кадре отняли HP */
|
||||||
|
|
||||||
|
/* Есть ли живой страж в отрисованной комнате (для главного цикла). */
|
||||||
|
uint8_t pop_guard_present(void);
|
||||||
|
|
||||||
|
/* Убрать стража со сцены (смена комнаты / инициализация). */
|
||||||
|
void pop_guard_reset(void);
|
||||||
|
|
||||||
|
/* Чит «убить стража» (seg000:786, K): скелета не берёт. Вызывать только
|
||||||
|
* при включённых читах — проверку делает вызывающий. */
|
||||||
|
void pop_guard_kill(void);
|
||||||
|
|
||||||
|
/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */
|
||||||
|
void pop_guard_tick(void) __banked;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -16,9 +16,17 @@
|
|||||||
#include "pop_level.h"
|
#include "pop_level.h"
|
||||||
#include "pop_trob.h"
|
#include "pop_trob.h"
|
||||||
#include "pop_redraw.h"
|
#include "pop_redraw.h"
|
||||||
|
#include "pop_guard.h"
|
||||||
|
#include "pop_cheat.h"
|
||||||
|
|
||||||
__sfr __at (0xFE) io_border;
|
__sfr __at (0xFE) io_border;
|
||||||
|
|
||||||
|
/* Сколько банков грузит crt0_banked (читает его стартовый код). */
|
||||||
|
const uint8_t n_banks = 1;
|
||||||
|
|
||||||
|
/* Режим читов (pop_cheat.h): на время разработки включаем в main. */
|
||||||
|
uint8_t pop_cheats;
|
||||||
|
|
||||||
/* seq_2_stand (types.h seqids) — Kid стоит, ждёт ввода. */
|
/* seq_2_stand (types.h seqids) — Kid стоит, ждёт ввода. */
|
||||||
#define SEQ_STAND 2
|
#define SEQ_STAND 2
|
||||||
#define ROOM_1 1
|
#define ROOM_1 1
|
||||||
@@ -198,11 +206,14 @@ int main(void)
|
|||||||
#ifdef ROOMNAV
|
#ifdef ROOMNAV
|
||||||
uint8_t nav_prev = 0;
|
uint8_t nav_prev = 0;
|
||||||
#endif
|
#endif
|
||||||
|
uint8_t cheat_prev = 0;
|
||||||
|
|
||||||
if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */
|
if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */
|
||||||
puts("pop_bg_load failed");
|
puts("pop_bg_load failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
pop_cheats = 1; /* режим разработки: читы включены */
|
||||||
|
pop_guard_reset();
|
||||||
if (pop_kid_data_load("kid_data.bin") != 0) { /* кадры+seqtbl в EMM-странице */
|
if (pop_kid_data_load("kid_data.bin") != 0) { /* кадры+seqtbl в EMM-странице */
|
||||||
printf("kid_data.bin load failed\n");
|
printf("kid_data.bin load failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
@@ -271,6 +282,12 @@ int main(void)
|
|||||||
* стоит, можно спокойно разбирать позу Kid и окклюзию. */
|
* стоит, можно спокойно разбирать позу Kid и окклюзию. */
|
||||||
if (kbd_raw_down(0x16)) frozen = 1;
|
if (kbd_raw_down(0x16)) frozen = 1;
|
||||||
if (kbd_raw_down(0x1E)) frozen = 0;
|
if (kbd_raw_down(0x1E)) frozen = 0;
|
||||||
|
{ /* Читы (seg000: switch под if (cheats_enabled)). K — убить
|
||||||
|
* стража; по фронту нажатия, чтобы не срабатывало каждый кадр. */
|
||||||
|
uint8_t k = kbd_raw_down(KBD_CHEAT_KILL);
|
||||||
|
if (pop_cheats && k && !cheat_prev) pop_guard_kill();
|
||||||
|
cheat_prev = k;
|
||||||
|
}
|
||||||
if (frozen) {
|
if (frozen) {
|
||||||
io_border = 0; /* черный: ждём vsync */
|
io_border = 0; /* черный: ждём vsync */
|
||||||
gfx_wait_vsync();
|
gfx_wait_vsync();
|
||||||
@@ -317,6 +334,7 @@ int main(void)
|
|||||||
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
||||||
kid_tick(); /* play_seq: следующий кадр */
|
kid_tick(); /* play_seq: следующий кадр */
|
||||||
pop_phys_tick(); /* K3: падение/приземление/стена */
|
pop_phys_tick(); /* K3: падение/приземление/стена */
|
||||||
|
pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */
|
||||||
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
||||||
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
||||||
pop_fell_out = 0;
|
pop_fell_out = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user