/* * pop_guard.c — состояние стража (W1/W2). См. pop_guard.h. * Логика — в банке (guards.c), отрисовка — из главного цикла. */ #include #include #include #include "pop_guard.h" #include "pop_geom.h" #include "pop_level.h" #include "pop_kid.h" /* play_seq/kid_set_seq пока общие (seqtbl один) */ #include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ #include "kid_data.h" pop_char_t Guard; uint8_t guardhp_curr, guardhp_max; int8_t guardhp_delta; 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; } /* ---- Атласы стража (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]; /* Кадр стража (порт cur_frame для Char): таблица у стражей СВОЯ * (frame_tbl_guard), и индексируется как frame + add_frame − 149, где * add_frame = 70 для кадров 102..106 (seg006:535). */ static kframe gframe; int pop_guard_load(void) { 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) { uint8_t i; if (!g_ok) return; for (i = 0; i < GUARD_PAGES; i++) atlas_free(&gp[i]); g_ok = 0; } /* load_frame для стража (seg006:0293 ветка charid_2_guard). */ static void 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) { gframe.image = 255; return; } pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&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; guard_load_frame(); } void pop_guard_heal(void) { 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]); gvalid[p] = 0; } void pop_guard_draw(void) { int obj_x, obj_y, top, fwd; uint8_t page, idx, p, flip; const uint8_t *img; uint16_t w, h; if (!g_ok || Guard.charid == 0 || gframe.image == 255) return; /* load_frame_to_obj (seg008:1728) — та же математика, что у Kid. */ fwd = (int)Guard.x + ((Guard.direction < 0) ? -gframe.dx : gframe.dx); obj_x = (fwd << 1) - 116; obj_y = (int)Guard.y + gframe.dy; if ((int8_t)(gframe.flags ^ Guard.direction) >= 0) obj_x++; obj_x = obj_x * 8 / 7; /* calc_screen_x_coord */ page = (uint8_t)(gframe.image >> 3); idx = (uint8_t)(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; } gfx_w0_unmap(); }