6673279cef992e6d0eaf2433bd3fd08e83add4e4
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6673279cef |
PoP: скелет уровня 3, цвета стражей, окклюзия соперника; кэш соседних комнат
Скелет (L3-SKEL, ассеты + механика): - pop_pack_guard.py получил параметр набора (GUARD/SKEL): атлас скелета poc/res/skel/g0..g3.atl (28 кадров), палитра — из его res750.pal (на ур. 3 curr_guard_color = 0, оригинал палитру не подменяет); - pop_guard_load выбирает набор по tbl_guard_type и перезагружается ПРИ СМЕНЕ УРОВНЯ (load_lev_spr, seg000:1092) — без этого скелет рисовался атласом стража и был невидим; - load_frame: charid_4_skeleton идёт по таблице стража (seg006:529), тень — только в кадрах 150..189. Пока ветка была одна (charid_2_guard), скелет получал image из таблицы Кида (180 при 28 спрайтах) и не рисовался; - check_skel (seg002:1042), ветка charid_4 в enter_guard, возрождение в комнате 3 при падении (seg002:252), autocontrol_skeleton; - leveldoor_open (seg007:456) — новый флаг, сбрасывается стартом уровня. Цвета стражей (BUG-GUARD-COLOR-1, закрыт): - все 7 палитр res10.bin -> pop_guard_pal.h, заливка 16 слотов по guards_color комнаты перед отрисовкой (set_chtab_palette, seg003:257). Проверено в MAME: ур. 2 комн. 11 = цвет 1, комн. 7 = цвет 3, полоса HP меняется вместе со стражем. Грабля: gfx_pal_load отдаёт указатель в BIOS, а тот читает только #4000-#BFFF — таблицу из банка копируем в стек. Кэш соседних комнат (BUG-SWORD-GHOST-1, закрыт): - pop_map кэширует fg соседей слева/справа ЦЕЛИКОМ и резолвит col -10..19. Было -2..11, дальше мнимая стена: луч видимости упирался в неё (страж после follow_guard в col 12), Кид прятал меч посреди боя и не мог достать обратно. +48 байт W2. Окклюзия соперника: - pop_fore_over_char получил проход other_overlay_tile (порядок midtable, seg008:1B06) и расширение перебора объединённым прямоугольником «персонаж + клинок + брызги» — падающий скелет больше не рисуется поверх кладки и верхней грани пола; - клип полем 192 строк (reset_obj_clip, seg006:0507) для спрайта, клинка (общий pop_sword_draw) и брызг — спрайт не залезает на полосу HP; - ROOMNAV после смерти Кида делает честный pop_start_level: телепорт «оживлял» мёртвого мимо старта уровня, оставляя живого скелета рядом с вернувшейся кучей костей. Ассеты чомпера (под L3-CHOMP): весь набор кадров в атласе явным списком (101-105 низ, 111-113 верх, 106-110 фронт, 114-123 кровь mono-силуэтом) — render_room анимированные тайлы пропускает, и в атласе не было ни одного. Число EMM-страниц не изменилось. Тесты: tests-host все 5 наборов зелёные, t_char вырос до 65 проверок (резолв колонок за краем комнаты, возрождение скелета); в testkit добавлен гард «код наехал на данные» (DATA_LOC). Доски: TASKS.md разнесён на TASKS_OPEN/TASKS_CLOSED, закрытые баги с разбором корней — в bug_closed.md; заведены DRAW-CHAR (отрисовка одна на всех Char, как физика после GUARD-PHYS) и L3-COLOR (зелёная кладка уровня 3: level_var_palettes = ресурс 20, есть в MSDOS/PRINCE.DAT). В roomtest.c временно оставлен автостоп на падении соперника (отладка падений скелета) — помечен ВРЕМЕННО. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e67117219f |
BUG-CTRL-FRAME-1: геометрия Кида считалась по кадру стража
cur_frame — один глобал на всех персонажей (как в оригинале), владелец — тот, кто последним прошёл load_frame. Последним в кадре тикает страж, поэтому к моменту control() Кида там лежал кадр СТРАЖА, а через kid_cur_dx/dy/flags по нему считается вся геометрия управления: dx_weight -> determine_col -> distance_to_edge_weight -> get_edge_distance -> выбор ветки в check_jump_up. Оригинал зовёт load_fram_det_col() (seg006:0144) сразу после loadkid/loadshad и ДО control() — play_kid_frame (seg000:1211) и play_guard_frame (seg000:1248). У нас этого не было. Замерено брейкпоинтом на pop_jump_up_seq: Kid x=156 col=6 кадр 15 (dx=0 weight_x=3) при кадре стража image17 (dx=-1 weight_x=8) дал curr_col=7 и distance=2 вместо 6 и 10 — то есть jump_up_plain (вернулось A=28, пустой прыжок) вместо «шаг назад на x=160 + зацеп». Предсказание по кадру стража совпало с намеренным до единицы. Отсюда же плавающее поведение: кадр стража меняется каждый тик, distance Кида скакал через порог 6 — то прыжок, то попытка зацепа с неверной X. И «голова Кида поверх плиты (1,6)» — не баг отрисовки, а следствие позы, которой в оригинале в этом месте не бывает. Фикс: pop_load_fram_det_col() (pop_kid.c) + вызовы в pop_ctrl_tick и pop_guard_tick. determine_col — только на ветке Кида: у нас он существует лишь для него (pop_map работает с Kid, а не с Char). Разбор с числами — bug_closed.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3983fa4513 |
PoP roomtest: HP-учёт, индикаторы HP, чит бессмертия; фикс кэша кадра
Боёвка (порт seg002/seg006): - check_sword_hurting / check_hurting / check_sword_hurt / hurt_by_sword / take_hp через дельты; do_delta_hp сводит их раз в кадр; - парирование (justblocked), refractimer после ранения стража; - смерть по seq_71_dying — через неё же теперь работает чит K: страж действительно погибает, а не замирает на месте; - парные окна Char/Opp: loadkid_and_opp / savekid_and_opp / saveshad_and_opp. Индикаторы HP (порт draw_kid_hp / draw_guard_hp): Кид слева, страж справа. Перерисовка ТОЛЬКО при изменении числа и тогда на ОБЕИХ страницах дабл-буфера (счётчик hp_todo, иначе на второй странице осталось бы старое значение и мерцало через кадр); pop_hp_invalidate при входе в комнату, где фон перерисован целиком. Чит I — бессмертие Кида (нашего изобретения, в оригинале его нет). Перекрывает и путь «безоружного закалывают насмерть»: тот идёт мимо HP, и без этого чит бесполезен ровно там, где нужен. ДВА НАЙДЕННЫХ БАГА: 1. Полосу HP блитил row-major примитивом, а атласы Кида и стража хранятся COLUMN-major (ради бесплатного флипа) — марки выходили транспонированными. Теперь колоночный блит, стрелки как в оригинале. 2. Кэш кадра для ОТРИСОВКИ заполняли pop_savekid/pop_saveshad. Любое окно Char БЕЗ play_seq — а это оба окна боёвки — записывало Киду кадр, который принадлежал СТРАЖУ, и kid_draw искал этот image в атласе Кида, рисуя произвольную позу. Владельцем кэша стал load_frame: он один знает, чей кадр загружен (по Char.charid). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d014a3f577 |
PoP roomtest: ИИ стража — подход к Киду и боевые ветки диспетчера
Пункт 2 плана закрыт: страж не только замечает Кида, но и идёт к нему и дерётся. Порт по SDLPoP, диспетчер общий — ИИ выставляет те же control_*, что и клавиатура игрока. guards.c (банк 1), порт seg002: - autocontrol_guard_active (737) + kid_in_sight (0A93) + kid_armed (0AC1) + kid_far (09CB); - guard_advance / guard_block / guard_strike с таблицами вероятностей по 12 градациям мастерства (seg002:26..38), бросок prob > prandom(255); - move_2_backward / move_3_up / move_6_shift / move_down_back; - таймеры justblocked / kid_sword_strike / guard_refrac убывают раз в кадр в autocontrol_opponent, как в оригинале. pop_ctrl.c, порт seg005: control_with_sword (964), swordfight (0CDB), sword_strike, parry, forward_with_sword, back_with_sword. Ветвление у Кида и у соперника разное — соперник блокирует только на кадре 152, Кид ещё и по 153 (и тогда последовательность прокручивается сразу). pop_guard.c: guard_skill из данных уровня (12 градаций, вне диапазона -> 3), HP по get_guard_hp (extrastrength[skill] + tbl_guard_hp[уровень]), собственный сид бросков pop_fight_seed — иначе перерисовка стены сбивала бы решения стража. char_opp_dist переехал из банка в pop_kid.c: он нужен по ОБЕ стороны банковой границы — и ИИ, и диспетчеру боёвки. Проверено в MAME (комната 3): страж проходит комнату, встаёт в дистанцию и машет мечом, позы меняются. Урона пока нет — HP-учёт и check_hurt следующим шагом, без них бой не заканчивается. Бюджет В БОЮ (175 кадров): 412 224 – 421 068 = 0.958–0.979 кадра. В покое было 400 800. Запас в худшем кадре ~9 000 — тесно, но в один кадр укладываемся; оптимизация отложена сознательно. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |