PoP roomtest: убитый страж остаётся мёртвым; меч только по подбору или читу S
Смерть стража теперь персистентна между входами в комнату — по механизму оригинала, а не отдельной таблицей «убит/не убит». В SDLPoP массивы level.guards_* лежат в ОЗУ и движок их ПЕРЕПИСЫВАЕТ: leave_guard (seg002:02F5) кладёт туда позицию/направление/мастерство, а у МЁРТВОГО ещё и curr_seq; enter_guard, увидев непустой seq_hi, поднимает стража прямо в этой последовательности и по кадру смерти (185/177/178) ставит alive = 1. У нас уровень лежит в EMM-странице только на чтение, поэтому в W2 добавлена живая копия — 6 байт на комнату (tile/dir/x/skill/seq_lo/seq_hi): - pop_guard_leave() в начале enter_room запоминает уходящего стража; - pop_guard_enter поднимает труп сохранённой последовательностью И сохранённой X (pos_guards пересчитывает её из колонки только при загрузке уровня, дальше ею владеет leave_guard — иначе тело прыгает в центр тайла). ГРАБЛИ: guards_seq_lo/hi в ФАЙЛЕ уровня не используются, там 0xFF во всех комнатах (оригинал чистит их в reset_level_unused_fields). Прочитав их как есть, я скормил интерпретатору curr_seq = 0xFFFF, и приложение зависало — бордюр оставался синим, цикл не доходил до vsync. Живая копия стартует нулями: 0 = «поднимать стандартной стойкой». Меч Киду больше не выдаётся автоматически: DEBUG_SWORD_ROOM убран, вместо него чит S (выдать меч). Штатный путь — подобрать с пола. Проверено в MAME: чит K убивает стража, уход из комнаты 3 и возврат — тело на месте, страж не воскресает. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,12 +27,18 @@ extern void _gfx_w0_isr(void);
|
||||
#define BP_GTILE 2119u
|
||||
#define BP_GDIR 2143u
|
||||
#define BP_GX 2167u
|
||||
#define BP_GSEQLO 2191u
|
||||
#define BP_GSKILL 2215u
|
||||
#define BP_GSEQHI 2239u
|
||||
#define BP_GCOLOR 2263u
|
||||
#define BP_START 2112u
|
||||
#define ROOM_TILES 30u
|
||||
#define MIN_SIZE (BP_START + 3u) /* минимум, чтобы прочитать старт */
|
||||
|
||||
/* fwd: инициализация живой копии стражей (тело ниже). */
|
||||
static void gstate_init(void);
|
||||
|
||||
|
||||
static uint8_t lvl_blk, lvl_page, lvl_ok;
|
||||
/* Копии таблиц дверных связей в W2 (LINKMAP мутабельна — таймеры кнопок). */
|
||||
static uint8_t dl1[256], dl2[256];
|
||||
@@ -74,6 +80,7 @@ int pop_level_load(const char *path)
|
||||
|
||||
if (n < (int)MIN_SIZE) { mem_free_block(blk); return -1; }
|
||||
lvl_ok = 1;
|
||||
gstate_init(); /* живая копия состояния стражей — из уровня */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -243,22 +250,95 @@ static uint8_t start_byte(uint16_t off)
|
||||
/* Страж комнаты (enter_guard, seg002:0112 + pos_guards, seg003). Возврат
|
||||
* 0 = стража в комнате нет (tile >= 30). X оригинал пересчитывает из
|
||||
* колонки (pos_guards), поэтому guards_x из файла не читаем. */
|
||||
/* ---- ЖИВОЕ состояние стражей (порт level.guards_*) ------------------ *
|
||||
* В оригинале массивы стражей — часть структуры уровня в ОЗУ, и движок их
|
||||
* ПЕРЕПИСЫВАЕТ: leave_guard при выходе из комнаты кладёт туда текущие
|
||||
* позицию/направление/мастерство, а у МЁРТВОГО ещё и curr_seq. Благодаря
|
||||
* этому, вернувшись, находишь труп там же, а не воскресшего стража.
|
||||
* У нас уровень лежит в EMM-странице только на чтение, поэтому держим
|
||||
* мутабельную копию в W2: 6 байт на комнату. */
|
||||
#define GS_TILE 0
|
||||
#define GS_DIR 1
|
||||
#define GS_X 2
|
||||
#define GS_SKILL 3
|
||||
#define GS_SEQL 4
|
||||
#define GS_SEQH 5
|
||||
static uint8_t gstate[24 * 6];
|
||||
|
||||
static void gstate_init(void)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)LVL_DATA_OFF;
|
||||
uint8_t r;
|
||||
gfx_w0_map(lvl_page);
|
||||
for (r = 0; r < 24; r++) {
|
||||
uint8_t *g = gstate + (uint16_t)r * 6;
|
||||
g[GS_TILE] = p[BP_GTILE + r];
|
||||
g[GS_DIR] = p[BP_GDIR + r];
|
||||
g[GS_X] = p[BP_GX + r];
|
||||
g[GS_SKILL] = p[BP_GSKILL + r];
|
||||
/* guards_seq_lo/hi в ФАЙЛЕ не используются — там 0xFF во всех
|
||||
* комнатах (оригинал чистит их в reset_level_unused_fields).
|
||||
* Осмысленное значение туда кладёт только leave_guard, поэтому
|
||||
* стартуем нулями: 0 = «поднимать стандартной стойкой».
|
||||
* Без этого в интерпретатор уходил curr_seq = 0xFFFF и он вис. */
|
||||
g[GS_SEQL] = 0;
|
||||
g[GS_SEQH] = 0;
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
}
|
||||
|
||||
/* leave_guard (seg002:02F5): запомнить состояние стража комнаты. seq
|
||||
* сохраняем ТОЛЬКО у мёртвого — у живого seq_hi = 0 означает «поднять
|
||||
* стандартной стойкой», как в оригинале. */
|
||||
void pop_guard_state_save(uint8_t room, uint8_t tile, int8_t dir, uint8_t x,
|
||||
uint8_t skill, uint16_t seq, uint8_t dead)
|
||||
{
|
||||
uint8_t *g;
|
||||
if (room < 1 || room > 24) return;
|
||||
g = gstate + (uint16_t)(room - 1) * 6;
|
||||
g[GS_TILE] = tile;
|
||||
g[GS_DIR] = (uint8_t)dir;
|
||||
g[GS_X] = x;
|
||||
g[GS_SKILL] = skill;
|
||||
if (dead) { g[GS_SEQL] = (uint8_t)seq; g[GS_SEQH] = (uint8_t)(seq >> 8); }
|
||||
else { g[GS_SEQH] = 0; }
|
||||
}
|
||||
|
||||
uint16_t pop_guard_state_seq(uint8_t room)
|
||||
{
|
||||
const uint8_t *g;
|
||||
if (room < 1 || room > 24) return 0;
|
||||
g = gstate + (uint16_t)(room - 1) * 6;
|
||||
if (g[GS_SEQH] == 0) return 0; /* живой — стандартная стойка */
|
||||
return (uint16_t)(g[GS_SEQL] | ((uint16_t)g[GS_SEQH] << 8));
|
||||
}
|
||||
|
||||
/* Запомненная X стража комнаты (для трупа: он лежит там, где лёг, а не в
|
||||
* центре тайла). */
|
||||
uint8_t pop_guard_state_x(uint8_t room)
|
||||
{
|
||||
if (room < 1 || room > 24) return 0;
|
||||
return gstate[(uint16_t)(room - 1) * 6 + GS_X];
|
||||
}
|
||||
|
||||
uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir,
|
||||
uint8_t *color, uint8_t *skill)
|
||||
{
|
||||
const uint8_t *p;
|
||||
const uint8_t *g;
|
||||
uint8_t t;
|
||||
if (!lvl_ok || room < 1 || room > 24) return 0;
|
||||
gfx_w0_map(lvl_page);
|
||||
p = (const uint8_t *)LVL_DATA_OFF;
|
||||
t = p[BP_GTILE + room - 1];
|
||||
/* Тайл/направление/мастерство — из ЖИВОЙ копии (её правит
|
||||
* pop_guard_state_save); цвет статичен, его читаем со страницы. */
|
||||
g = gstate + (uint16_t)(room - 1) * 6;
|
||||
t = g[GS_TILE];
|
||||
if (t < 30) {
|
||||
*tile = t;
|
||||
*dir = (int8_t)p[BP_GDIR + room - 1];
|
||||
*color = (uint8_t)(p[BP_GCOLOR + room - 1] & 0x0F);
|
||||
*skill = p[BP_GSKILL + room - 1];
|
||||
*dir = (int8_t)g[GS_DIR];
|
||||
*skill = g[GS_SKILL];
|
||||
gfx_w0_map(lvl_page);
|
||||
*color = (uint8_t)(((const uint8_t *)LVL_DATA_OFF)[BP_GCOLOR + room - 1] & 0x0F);
|
||||
gfx_w0_unmap();
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
return (uint8_t)(t < 30);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user