/* * pop_kdraw.c — отрисовка ИЗ АТЛАСОВ КИДА, которая нужна только слою * персонажей: клинок в руке (add_sword_to_objtable, seg006:1798) и блит * спрайта chtab_2 по его id (полоса HP). БАНК 4, вместе с pop_cdraw.c. * * Почему здесь. Оба вызова приходят РОВНО из pop_cdraw.c и больше ниоткуда * (проверено грепом), а лежали они в pop_kid.c — то есть в резиденте W1, * съедая ~1.2 КБ дефицитного окна ради кода, который зовёт сосед по банку. * Раз модуль в том же банке 4 — вызовы прямые, без трамплина: объявления в * _pop_kdraw.h НЕ помечены __banked (см. там же условие корректности). * * Загрузка атласа меча (pop_sword_load) осталась публичной и __banked: её * зовёт инициализация, один раз за игру. */ #include #include #include #include "pop_kid.h" #include "_pop_kdraw.h" #include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */ #include "pop_guard.h" /* CHARID_* — клинок есть и у стража */ #include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ #include "_pop_draw.h" /* pop_onscreen_cols */ #include "pop_map.h" /* pop_upside — зелье инверсии */ #include "pop_vflip.h" /* зеркальные страницы атласов */ #include "kid_data.h" /* chtab_0 «меч в руке» (add_sword_to_objtable, seg006:1798): клинок — * ОТДЕЛЬНЫЙ спрайт поверх персонажа, один и тот же атлас у Кида и у стража. * Таблица sword_tbl (kid_data.h, генерится из seg006) индексируется * sword_frame = кадр.sword & 0x3F; id 255 = клинка в этом кадре нет. * Смещения — в ЭКРАННЫХ пикселях: оригинал применяет их ПОСЛЕ * calc_screen_x_coord, x — «вперёд» по направлению персонажа. */ static const uint8_t SWORD_ID[KID_NSWORD] = KID_SWORD_ID_INIT; static const int8_t SWORD_DX[KID_NSWORD] = KID_SWORD_DX_INIT; static const int8_t SWORD_DY[KID_NSWORD] = KID_SWORD_DY_INIT; static atlas_t swordp; static uint8_t sword_ok; /* Меч приходит УЖЕ ЗАГРУЖЕННЫМ: его читает pop_kid_load из того же архива, * что и остальные страницы Кида, — открывать архив второй раз ради одного * элемента незачем (`open` дороже самого чтения). */ int pop_sword_take(const atlas_t *a) __banked { if (!a) return -1; swordp = *a; sword_ok = 1; return 0; } /* Зеркальная страница меча (зелье инверсии) — пара к pop_sword_take. */ int pop_sword_vflip_take(const atlas_t *m) __banked { if (!sword_ok) return 0; return pop_vflip_take(&swordp, m); } /* add_sword_to_objtable (seg006:1798). Клинок рисуется, если персонаж в * кадрах подъёма/ножен, ИЛИ держит меч, ИЛИ это ЖИВОЙ страж (у стража меч * в руке всегда, даже пока sword ещё sheathed). Страницу W0 оставляем * замапленной — вызывающий делает gfx_w0_unmap сам. */ uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr, int obj_x, int obj_y, int *rx, int *ry, uint16_t *rw, uint16_t *rh) { uint8_t sf, id, flip; const uint8_t *simg; uint16_t w, h; int bx, top; if (!sword_ok) return 0; if (!((ch->frame >= 229 && ch->frame < 238) || ch->sword != 0 || ((ch->charid == CHARID_2_GUARD || ch->charid == CHARID_4_SKELETON) && ch->alive < 0))) return 0; sf = (uint8_t)(fr->sword & 0x3F); if (sf == 0 || sf >= KID_NSWORD) return 0; id = SWORD_ID[sf]; if (id == 0xFF) return 0; simg = (const uint8_t *)atlas_image(&swordp, id); { /* ПЕРЕВОРОТ: зеркальная копия страницы меча (см. pop_vflip.h) */ uint8_t vpg = swordp.page; if (pop_upside) { uint8_t f = pop_vflip_page(vpg); if (f) vpg = f; } gfx_w0_map(vpg); } w = (uint16_t)(simg[0] | ((uint16_t)simg[1] << 8)); h = (uint16_t)(simg[2] | ((uint16_t)simg[3] << 8)); if (!w || !h) return 0; flip = (uint8_t)(ch->direction >= 0); bx = obj_x + ((ch->direction < 0) ? -(int)SWORD_DX[sf] : (int)SWORD_DX[sf]); top = obj_y + (int)SWORD_DY[sf] - (int)h + 1; /* Та же формула, что у самого персонажа (pop_cdraw.c): верх кадра * [top, top+h) уезжает в 192 - top - h. */ if (pop_upside) top = POP_PLAYFIELD_H - top - (int)h; if (flip) bx -= (int)w; /* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507): клинок * обрезается по нему так же, как сам персонаж. Без этого у падающего * в пропасть скелета меч оставался виден НИЖЕ поля, на полосе HP. */ { int skip = 0, rows = (int)h; if (top < 0) { skip = -top; rows -= skip; top = 0; } if (top + rows > 192) rows = 192 - top; if (rows <= 0) return 0; gfx_set_bank(GFX_BANK_SPRITE); pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ /* Клинок почти всегда целиком на экране — noclip-путь (см. gfx.h: * подготовка клипающего варианта стоит ~5.6 К тактов на вызов). */ if (pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows)) gfx_blit_cols_part_noclip(bx, top + POP_YOFF, simg, flip, (uint8_t)skip, (uint8_t)rows); else gfx_blit_cols_part(bx, top + POP_YOFF, simg, flip, skip, rows); gfx_set_bank(GFX_BANK_NORMAL); *rx = bx; *ry = top; *rw = w; *rh = (uint16_t)rows; } return 1; } /* Блит спрайта chtab_2 (атласы Kid) по ЕГО id, левым верхним углом в (x,y). * Нужен полосе HP: она рисует id 216/217 из тех же атласов, что и кадры * Кида, а держать вторую копию раскладки в pop_cdraw ни к чему. */ void pop_kid_img_blit(uint8_t image, int x, int y) { pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */ uint8_t pg = (uint8_t)(image >> 3); /* раскладка атласов: 8 спрайтов/стр. */ uint8_t idx = (uint8_t)(image & 7); const uint8_t *img; uint16_t w, h; if (pg >= kid_npages) return; img = (const uint8_t *)atlas_image(&kidp[pg], idx); gfx_w0_map(kidp[pg].page); w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); /* Атласы Кида хранятся COLUMN-MAJOR (transpose_cols в pop_pack_kid — * ради бесплатного горизонтального флипа), поэтому блит обязан быть * колоночным. Row-major gfx_blit_noclip выдал бы марку транспонированной. * noclip БЕЗ проверки (CLIP-1): единственный вызывающий — полоса HP * (pop_cdraw.c), у неё координаты фиксированные и заведомо на экране. */ if (w && h) gfx_blit_cols_part_noclip(x, y, img, 0, 0, 0); gfx_w0_unmap(); }