af5f0a4638
Разгрузка W1/W2 перед ИИ стражей (вариант 2 из двух обсуждённых). 1. pop_guard.c разделён по окнам: состояние/логика (Guard, HP, enter, kill, load_frame) остаются в W1/W2 — их обязан видеть банк guards.c; ОТРИСОВКА уехала в новый pop_gdraw.c, собираемый как --w3 (резидент). Правило границы: резидент = только то, что рисует и зовётся исключительно из главного цикла. Кадр стража стал глобальным (pop_gframe): заполняет логика, читает резидент. 2. Страж не окклюдировался передними гранями тайлов — рисовался поверх столба. В оригинале любой Char это запись midtable, а foretable рисуется после всех midtable (draw_tile_fore, seg008:690), т.е. столб перекрывает всех одинаково. Футпринт персонажа выделен из pop_fore_over_kid в char_footprint(), поверх него добавлен pop_fore_over_char() — слой fore + полоса потолка, без оверлеев поз виса/полёта/подъёма (у стража их нет; появятся — портируем redraw_at_char2 общим кодом, а не догадками). 3. Упаковщик стража: тот же off-by-one, что уже ловили у Kid. load_chtab_from_file(id_chtab_5_guard, 750) даёт images[0] = res751, а рисование индексирует images[frame.image] — значит image=N это res(751+N), а не res(750+N). Из-за сдвига frame_166_stand_inactive рисовался как res767 (выпад) вместо res768 (стойка). Проверено в MAME: страж в комнатах 3 и 21 стоит в правильной позе; окклюзия подтверждена патчем Guard.x в живой сессии — при заходе за столб спрайт корректно срезается его передней гранью. Память: _CODE 26 149 -> 25 703, куча W2 805 -> 1245 Б, резидент W3 11 656 -> 12 819 (свободно 3565 Б), банк 1 236/16384 Б. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
3.7 KiB
C
92 lines
3.7 KiB
C
/*
|
||
* pop_guard.c — СОСТОЯНИЕ и ЛОГИКА стража (W1/W2). См. pop_guard.h.
|
||
*
|
||
* Здесь только то, что должно оставаться достижимым из банка (guards.c):
|
||
* поля Guard, HP, вход в комнату, чит-убийство, чтение кадра из таблицы.
|
||
* ОТРИСОВКА — в pop_gdraw.c, резиденте W3 (её зовёт лишь главный цикл).
|
||
*/
|
||
#include <stdint.h>
|
||
#include "pop_guard.h"
|
||
#include "pop_geom.h"
|
||
#include "pop_level.h"
|
||
#include "pop_kid.h" /* pop_kid_data_frame — таблицы в EMM-странице */
|
||
#include "kid_data.h"
|
||
|
||
pop_char_t Guard;
|
||
uint8_t guardhp_curr, guardhp_max;
|
||
int8_t guardhp_delta;
|
||
|
||
/* Кадр стража (порт cur_frame для Char). Глобальный, а не статик: его
|
||
* заполняет логика (W1/W2), а читает отрисовка из резидента W3. */
|
||
kframe pop_gframe;
|
||
|
||
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;
|
||
}
|
||
|
||
/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у
|
||
* стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где
|
||
* add_frame = 70 для кадров 102..106 (seg006:535). */
|
||
void pop_guard_load_frame(void)
|
||
{
|
||
int16_t idx = (int16_t)Guard.frame;
|
||
if (idx >= 102 && idx < 107) idx += 70; /* add_frame */
|
||
idx -= 149;
|
||
if (idx < 0 || idx >= KID_NGFRAMES) { pop_gframe.image = 255; return; }
|
||
pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&pop_gframe);
|
||
}
|
||
|
||
void pop_guard_enter(uint8_t room)
|
||
{
|
||
uint8_t tile, color, skill;
|
||
int8_t dir;
|
||
pop_guard_reset();
|
||
if (!pop_level_guard(room, &tile, &dir, &color, &skill)) return;
|
||
/* enter_guard (seg002:0112) + pos_guards (seg003): X считается из
|
||
* колонки тайла, а не берётся из файла. */
|
||
Guard.room = room;
|
||
Guard.curr_row = (int8_t)(tile / 10);
|
||
Guard.y = (uint8_t)pop_y_land[Guard.curr_row + 1];
|
||
Guard.x = (uint8_t)(pop_x_bump[(tile % 10) + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX);
|
||
Guard.curr_col = (int8_t)(tile % 10);
|
||
Guard.direction = dir;
|
||
Guard.charid = CHARID_2_GUARD;
|
||
Guard.sword = 0; /* sword_0_sheathed */
|
||
Guard.alive = -1;
|
||
Guard.action = 1; /* actions_1_run_jump */
|
||
Guard.fall_x = Guard.fall_y = 0;
|
||
/* seq_77_guard_stand_inactive даёт кадр 166 (frame_166_stand_inactive,
|
||
* seqtbl.c:266). Интерпретатор последовательностей для стража придёт
|
||
* вместе с ИИ (следующая фаза) — сейчас ставим кадр напрямую, чтобы
|
||
* страж просто СТОЯЛ и было что рисовать/убивать читом. */
|
||
Guard.frame = 166;
|
||
guardhp_max = guardhp_curr = 3; /* get_guard_hp: skill 0, уровень 1 */
|
||
guardhp_delta = 0;
|
||
pop_guard_load_frame();
|
||
}
|