/* * pop_gdraw.c — ОТРИСОВКА стража. Резидент окна W3 (--w3 pop_gdraw.c). * * Почему отдельным файлом, а не внутри pop_guard.c: --w3 забирает МОДУЛЬ * целиком (код+rodata в 0xC000), а состояние стража и его логика обязаны * остаться в W1/W2 — из банка (guards.c) резидент W3 недостижим ни прямо, * ни транзитивно (docs/layout_plan_v2.md §2 R2). Значит граница проходит * ровно между «что рисует» (зовёт только главный цикл → резидент) и «что * знает» (зовёт и банк → W1/W2). * * Писучие статики резидента (--dataseg не трогаем) остаются в обычном * _DATA, то есть в W2 — сюда переезжает КОД, не данные. */ #include #include #include #include "pop_guard.h" #include "pop_kid.h" /* pop_sword_draw — клинок общий с Кидом */ #include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */ #include "kid_data.h" /* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */ #define GUARD_PAGES 5 static atlas_t gp[GUARD_PAGES]; static uint8_t g_ok; /* Прямоугольник последней отрисовки ПО СТРАНИЦЕ дабл-буфера (как у Kid: * heal обязан стирать спрайт именно той страницы, в которую рисуем). */ static int gx_l[2], gy_l[2]; static uint16_t gw_l[2], gh_l[2]; static uint8_t gvalid[2]; /* Клинок — ОТДЕЛЬНЫМ прямоугольником, не объединением со стражем: объединение * почти вдвое больше суммы двух (клинок уходит вперёд-вверх), а heal стоит * ровно по площади. Объединение считаем только для окна fore-клипа. */ static int sx_l[2], sy_l[2]; static uint16_t sw_l[2], sh_l[2]; static uint8_t svalid[2]; int pop_guard_load(void) __banked { static const char *const names[GUARD_PAGES] = { "GUARD\\g0.atl", "GUARD\\g1.atl", "GUARD\\g2.atl", "GUARD\\g3.atl", "GUARD\\g4.atl" }; uint8_t i; for (i = 0; i < GUARD_PAGES; i++) if (atlas_load(&gp[i], names[i]) != 0) return -1; g_ok = 1; return 0; } void pop_guard_free(void) __banked { uint8_t i; if (!g_ok) return; for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]); g_ok = 0; } void pop_guard_heal(void) __banked { uint8_t p = gfx_get_draw_page() & 1; if (gvalid[p]) gfx_heal(gx_l[p], gy_l[p] + POP_YOFF, gw_l[p], gh_l[p]); if (svalid[p]) gfx_heal(sx_l[p], sy_l[p] + POP_YOFF, sw_l[p], sh_l[p]); gvalid[p] = svalid[p] = 0; } void pop_guard_draw(void) __banked { int obj_x, obj_y, top, fwd, fp_x; uint8_t page, idx, p, flip; const uint8_t *img; uint16_t w, h; /* Окно fore-клипа — по ОБЪЕДИНЁННОМУ габариту «страж + клинок»; сам * футпринт (pop_fore_over_char) считается по спрайту стража, поэтому * w/h ниже НЕ трогаем. */ int clip_x = 0, clip_y = 0; uint16_t clip_w = 0, clip_h = 0; if (!g_ok || Guard.charid == 0 || pop_gframe.image == 255) return; /* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */ fwd = (int)Guard.x + ((Guard.direction < 0) ? -pop_gframe.dx : pop_gframe.dx); obj_x = (fwd << 1) - 116; obj_y = (int)Guard.y + pop_gframe.dy; if ((int8_t)(pop_gframe.flags ^ Guard.direction) >= 0) obj_x++; fp_x = obj_x; /* ЛОГИЧЕСКАЯ X (до ×8/7) — футпринт */ obj_x = obj_x * 8 / 7; /* calc_screen_x_coord */ page = (uint8_t)(pop_gframe.image >> 3); idx = (uint8_t)(pop_gframe.image & 7); if (page >= GUARD_PAGES) return; img = (const uint8_t *)atlas_image(&gp[page], idx); gfx_w0_map(gp[page].page); w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); if (w && h) { int bx = obj_x; flip = (uint8_t)(Guard.direction >= 0); /* спрайты нарисованы влево */ if (flip) bx -= (int)w; top = obj_y - (int)h + 1; p = gfx_get_draw_page() & 1; gfx_set_bank(GFX_BANK_SPRITE); gfx_blit_cols_part(bx, top + POP_YOFF, img, flip, 0, 0); gfx_set_bank(GFX_BANK_NORMAL); gx_l[p] = bx; gy_l[p] = top; gw_l[p] = w; gh_l[p] = h; gvalid[p] = 1; clip_x = bx; clip_y = top; clip_w = w; clip_h = h; } /* Клинок стража — тот же chtab_0, что у Кида (add_sword_to_objtable, * seg006:1798): у ЖИВОГО стража меч в руке всегда. Свой прямоугольник * heal; окно fore-клипа — объединение (без него клинок полез бы поверх * переднего столба). */ { int sx, sy; uint16_t sw, sh; if (pop_sword_draw(&Guard, &pop_gframe, obj_x, obj_y, &sx, &sy, &sw, &sh)) { p = gfx_get_draw_page() & 1; sx_l[p] = sx; sy_l[p] = sy; sw_l[p] = sw; sh_l[p] = sh; svalid[p] = 1; if (!clip_w) { clip_x = sx; clip_y = sy; clip_w = sw; clip_h = sh; } else { int x1 = clip_x + (int)clip_w, y1 = clip_y + (int)clip_h; if (sx < clip_x) clip_x = sx; if (sy < clip_y) clip_y = sy; if (sx + (int)sw > x1) x1 = sx + (int)sw; if (sy + (int)sh > y1) y1 = sy + (int)sh; clip_w = (uint16_t)(x1 - clip_x); clip_h = (uint16_t)(y1 - clip_y); } } } gfx_w0_unmap(); /* Окно fore-слоя = прямоугольник стража с клинком (см. pop_bg.c, * fclip_on): без него проход возвращал ЦЕЛЫЕ тайлы — 432 К тактов. */ pop_fore_set_clip(clip_x, clip_y + POP_YOFF, (int)clip_w, (int)clip_h); /* Передние грани тайлов ПОВЕРХ стража: в оригинале персонаж — запись * midtable, а foretable рисуется после всех midtable-записей, поэтому * колонна/кромка пола перекрывает ЛЮБОГО персонажа, не только Kid * (seg008:690 draw_tile_fore). Без этого страж виден сквозь передний * столб. */ if (w && h) pop_fore_over_char(fp_x, obj_y, w, h, Guard.direction); }