4f7d9c05966a6fd8f69aab98bd6c42b3fbf2441d
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
79ea473910 |
PoP roomtest: страж замечает Кида и достаёт меч (ИИ, шаг 1)
Первый самостоятельный кусок ИИ стража (план, пункт 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> |
||
|
|
8bbc6b4d07 |
PoP roomtest: интерпретатор последовательностей стал общим (Char) — стражи
Пункт 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> |
||
|
|
1e6c377edc |
PoP roomtest: pop_bg и pop_map в банки; --dataseg BANKn стал опцией
Резидента --w3 больше нет: отрисовка (pop_bg + pop_gdraw) уехала в БАНК 2, физика/коллизия (pop_map) — в БАНК 3. Куча W1/W2 1294 -> 6750 Б. Что это разблокировало. Резидент был тупиком: из банка он недостижим ни прямо, ни транзитивно, поэтому pop_map (самый крупный модуль, 5.8 КБ) в банк было не увести — он зовёт mob-отрисовку. Проверено, что банк->банк РАБОТАЕТ: ___sdcc_bcall_ehl читает страницу окна портом 0xE2 и кладёт её на СТЕК своего кадра (runtime/bank.s), поэтому вложенность корректна по построению. Подтверждено в MAME цепочкой W1 -> банк1 -> банк2 -> банк1: nested=124 after=8, ровно ожидаемое. Значит развязка mob'а (самое рисковое место, loose-полы) НЕ понадобилась — pop_map зовёт pop_bg трамплином. Правила вызовов проверены на сгенерированном asm и записаны в memory sdcc_banked_call_rules: трамплин выбирает ОБЪЯВЛЕНИЕ (__banked), а не раскладка — даже внутри одного .c между __banked функциями он есть. Внутрибанковые функции оставлены непомеченными и зовутся напрямую, в т.ч. через границу файла (pop_gdraw -> pop_fore_over_char). sprinter-cc: --dataseg BANKn БОЛЬШЕ НЕ ставится по умолчанию. Раньше вся писучая память банкового модуля уезжала в страницу банка и снаружи не читалась (проверено на .map: глобал лёг по 0x0001C000) — грабли на каждом переносе. Теперь данные банков по умолчанию в общем _DATA (W1/W2, замаплен всегда), а прежнее поведение — по явному --bank-data. Замеры (комната 3, Kid + страж; кадр Sprinter в турбо = 430 000 тактов): до переноса 338 508 (0.79 кадра) + pop_bg в банк 2 347 100 (+2.5 %) + pop_map в банк 3 371 100 (+9.6 % к исходному, 0.86 кадра) Плата — трамплины (~654 такта на вызов, ~30 вызовов за кадр). При пейсинге в 3 кадра это 29 % логического кадра, но запас до ОДНОГО кадра всего ~59 000 тактов — под звук его надо возвращать (следующий шаг: батчить кроссбанковые вызовы, начиная с pop_redraw_needed). Профилирование бордюром включено по умолчанию (make PROF=0 выключает) и переведено на реально работающие биты: бит 0 (красный) у бордюра Sprinter ИГНОРИРУЕТСЯ, поэтому различимых состояний четыре и значения обязаны быть чётными — 0 чёрный (ждём vsync), 2 синий (логика), 4 зелёный (фон), 6 циан (спрайты). Раньше нечётные номера сливались и полосы не читались. Проверено в MAME: комнаты 1/3 рисуются как прежде, бег и коллизия работают, в комнате 12 плиты проваливаются со щебнем — то есть цепочка loose банк3 -> банк2 живая. Полосы бордюра в комнате 3: логика 73 строки, фон 64, спрайты 128, свободно 23. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
af5f0a4638 |
PoP roomtest: отрисовка стража в резидент W3 + fore-окклюзия + off-by-one спрайта
Разгрузка 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> |
||
|
|
3dbad6120c |
PoP roomtest: страж — спрайты, таблица кадров и появление в комнате
Шаги 1-2 из плана стражей: 1. Спрайты (chtab_5_guard, база 750). Новый упаковщик pop_pack_guard.py: data/GUARD/res751..784 -> GUARD\g0..g4.atl (5 EMM-страниц, адресация id>>3 / id&7, как у Kid). Палитра берётся НЕ из PNG, а из res10.bin (guard_palettes: 7 палитр по 16 цветов, 6-бит) по level.guards_color — на уровне 1 у обоих стражей color = 2; группа слотов 0x90..0x9F добавлена в общий kid.pal. 2. Таблица кадров стража у оригинала СВОЯ (frame_tbl_guard, seg006:372, 41 запись) и индексируется как frame + add_frame - 149, где add_frame = 70 для кадров 102..106. Она дописана в kid_data.bin (3515 -> 3720 Б, смещение в KID_BIN_GFRAMES_OFF); pop_kid получил pop_kid_data_frame() — чтение кадра из ЛЮБОЙ таблицы страницы данных. 3. Появление: pop_level_guard() читает guards_tile/dir/color/skill из уровня, pop_guard_enter() ставит стража по enter_guard (seg002:0112) + pos_guards (seg003): row из тайла, y = y_land[row+1], x из колонки, charid = guard, sword сложен, alive = -1, HP = 3. Отрисовка pop_guard_draw() — та же математика, что kid_draw (load_frame_to_obj + calc_screen_x_coord), но атлас стража и своя таблица кадров; heal по странице дабл-буфера, как у Kid. Интерпретатора последовательностей у стража ПОКА НЕТ: кадр ставится напрямую (166 = frame_166_stand_inactive, что и даёт seq_77 при входе в комнату). play_seq для произвольного персонажа + ИИ — следующая фаза. Проверено в MAME: в комнате 3 страж появляется на своём месте (ряд 1, кол 7) и рисуется; цвета совпадают с эталонным рендером спрайта в палитре color=2. ВНИМАНИЕ по памяти: куча W2 просела до 805 Б (было 2349). Перед ИИ стражей нужен шаг 5 плана (данные: room_modif 720 Б, dl1/dl2 512 Б, _kbdraw_down 512 Б) либо вынос кода отрисовки стража в резидент W3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e1ee447b7f |
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> |