diff --git a/applications/PoP/roomtest/Makefile b/applications/PoP/roomtest/Makefile index 16c62b4..d7babc0 100644 --- a/applications/PoP/roomtest/Makefile +++ b/applications/PoP/roomtest/Makefile @@ -12,7 +12,7 @@ MEMORY ?= small # __banked-модулей (стражи): из банка резидентная страница W3 не видна — # ни напрямую, ни транзитивно. См. docs/layout_plan_v2.md §2 (R2), §4. EXTRA_FLAGS ?= --gfx 256 --w3 pop_bg.c -EXTRA_SRCS := pop_kid.c pop_ctrl.c pop_level.c pop_map.c pop_trob.c pop_redraw.c +EXTRA_SRCS := pop_kid.c pop_ctrl.c pop_level.c pop_map.c pop_trob.c pop_redraw.c pop_geom.c BG_DIR := $(CURDIR)/../poc/res/bg KID_DIR := $(CURDIR)/../poc/res/kid @@ -42,4 +42,4 @@ $(KID_DATA): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py kid_data.h $(KID_BIN): $(TC)/pop_extract_kid_data.py cd $(TC) && python3 pop_extract_kid_data.py -$(EXAMPLE).exe: pop_bg.c pop_bg.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA) +$(EXAMPLE).exe: pop_bg.c pop_bg.h pop_geom.c pop_geom.h pop_redraw.c pop_redraw.h pop_kid.c pop_kid.h pop_ctrl.c pop_ctrl.h pop_map.c pop_map.h pop_level.c pop_level.h pop_trob.c pop_trob.h kid_data.h $(BG_DATA) $(KID_DATA) $(KID_BIN) $(LVL_DATA) diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index 243241c..96d59d8 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -14,6 +14,7 @@ #include #include #include "pop_bg.h" +#include "pop_geom.h" #include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */ #include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев (см. ниже) */ @@ -311,11 +312,7 @@ static uint8_t wall_modifier(int row, int col) /* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */ static unsigned long rnd_seed; -static uint16_t prandom(uint16_t maxv) -{ - rnd_seed = rnd_seed * 214013UL + 2531011UL; - return (uint16_t)((uint16_t)(rnd_seed >> 16) % (uint16_t)(maxv + 1)); -} +#define prandom(maxv) pop_prandom(&rnd_seed, (maxv)) /* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */ static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col) @@ -1125,10 +1122,7 @@ static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка ( return (int8_t)xh; } -static int8_t y_to_row(int y) /* y_to_row_mod4 (seg006, TILE_SIZEY=63) */ -{ - return (int8_t)((y + 60) / 63 % 4 - 1); -} +#define y_to_row(y) pop_y_to_row((int16_t)(y)) /* общий (pop_geom) */ static void fore_only_tile(int row, int col) { diff --git a/applications/PoP/roomtest/pop_geom.c b/applications/PoP/roomtest/pop_geom.c new file mode 100644 index 0000000..3f3f2fb --- /dev/null +++ b/applications/PoP/roomtest/pop_geom.c @@ -0,0 +1,23 @@ +/* + * pop_geom.c — общая геометрия + PRNG оригинала. См. pop_geom.h. + */ +#include "pop_geom.h" + +/* data.h: шаг 14 (ширина тайла в координатах персонажа), [5] = col 0. */ +const int16_t pop_x_bump[20] = { + -12, 2, 16, 30, 44, 58, 72, 86, 100, 114, + 128, 142, 156, 170, 184, 198, 212, 226, 240, 254 +}; +const int16_t pop_y_land[5] = {-8, 55, 118, 181, 244}; + +int8_t pop_y_to_row(int16_t y) +{ + return (int8_t)((y + 60) / TILE_SIZEY % 4 - 1); +} + +uint16_t pop_prandom(unsigned long *seed, uint16_t maxv) +{ + unsigned long s = *seed * 214013UL + 2531011UL; + *seed = s; + return (uint16_t)((uint16_t)(s >> 16) % (uint16_t)(maxv + 1)); +} diff --git a/applications/PoP/roomtest/pop_geom.h b/applications/PoP/roomtest/pop_geom.h new file mode 100644 index 0000000..71c0a38 --- /dev/null +++ b/applications/PoP/roomtest/pop_geom.h @@ -0,0 +1,34 @@ +/* + * pop_geom.h — общая геометрия PoP и PRNG оригинала. + * + * Сюда сведено то, что раньше дублировалось по модулям (x_bump/y_land в + * pop_kid и pop_map, y_to_row в pop_bg и pop_map, 32-битный LCG в pop_bg и + * pop_trob). Кроме экономии это ФУНДАМЕНТ bank-safe API: модуль живёт в + * W1/W2 и не трогает графику, поэтому его может звать и главный цикл, и + * резидент W3, и будущий __banked-код стражей (docs/layout_plan_v2.md §4). + */ +#ifndef POP_GEOM_H +#define POP_GEOM_H + +#include + +#define FIRST_ONSCREEN_COLUMN 5 /* индекс col 0 комнаты в x_bump */ +#define TILE_SIZEX 14 /* ширина тайла в координатах персонажа */ +#define TILE_SIZEY 63 /* высота ряда */ + +/* x_bump (data.h): левая граница колонки в координатах персонажа; колонка + * комнаты col = x_bump[col + FIRST_ONSCREEN_COLUMN]. y_land: пол ряда + * (индекс row+1; [0] — ряд «над комнатой»). */ +extern const int16_t pop_x_bump[20]; +extern const int16_t pop_y_land[5]; + +/* y_to_row_mod4 (seg006): ряд по координате y с оборотом mod 4 (−1 = полоса + * над комнатой, 3 = под комнатой). */ +int8_t pop_y_to_row(int16_t y); + +/* prandom (seg009:321) — 32-битный LCG оригинала. Сид ВНЕШНИЙ: у стен + * (раскладка кладки) и у анимаций тайлов (фаза факелов) свои + * последовательности, смешивать их нельзя. */ +uint16_t pop_prandom(unsigned long *seed, uint16_t maxv); + +#endif diff --git a/applications/PoP/roomtest/pop_kid.c b/applications/PoP/roomtest/pop_kid.c index 8d05048..7e2168a 100644 --- a/applications/PoP/roomtest/pop_kid.c +++ b/applications/PoP/roomtest/pop_kid.c @@ -12,6 +12,7 @@ #include "pop_kid.h" #include "pop_bg.h" /* POP_YOFF — вертикальное центрирование */ #include "pop_map.h" /* pop_clip_char_top (clip_char) */ +#include "pop_geom.h" #include "kid_data.h" kid_t Kid; @@ -20,14 +21,6 @@ kid_t Kid; * в play_seq, потребляется pop_check_knock (pop_map) — трясёт loose-полы. */ int8_t knock; -/* Координатные таблицы (data.h). */ -static const uint8_t x_bump[20] = { - (uint8_t)-12, 2, 16, 30, 44, 58, 72, 86, 100, 114, - 128, 142, 156, 170, 184, 198, 212, 226, 240, 254 -}; -static const int16_t y_land[5] = {-8, 55, 118, 181, 244}; -#define FIRST_ONSCREEN_COLUMN 5 -#define TILE_SIZEX 14 /* ---- Атласы Kid (прямая адресация image>>3) ------------------------ */ #define KID_MAXPAGES 28 @@ -258,8 +251,8 @@ void kid_init(uint8_t seq_id, int8_t col, int8_t row, int8_t dir) Kid.curr_col = col; Kid.curr_row = row; Kid.direction = dir; - Kid.x = (uint8_t)(x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX); - Kid.y = (uint8_t)y_land[row + 1]; + Kid.x = (uint8_t)(pop_x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX); + Kid.y = (uint8_t)pop_y_land[row + 1]; Kid.fall_x = Kid.fall_y = 0; Kid.repeat = 0; Kid.curr_seq = kid_seq_off[seq_id]; diff --git a/applications/PoP/roomtest/pop_map.c b/applications/PoP/roomtest/pop_map.c index 7b7a6ce..005de26 100644 --- a/applications/PoP/roomtest/pop_map.c +++ b/applications/PoP/roomtest/pop_map.c @@ -5,7 +5,7 @@ * FRAME_NEEDS_FLOOR (0x40), под персонажем не floor -> start_fall(); * стена -> in_wall(); * freefall (action 4) -> do_fall(): fall_y-ускорение (в fall_accel/speed), - * достиг y_land[curr_row+1] -> land()/inc_curr_row. + * достиг pop_y_land[curr_row+1] -> land()/inc_curr_row. * * Координатная система PoP: экран.left = 58, тайл = 14 ед. (TILE_SIZEX); * col = (xpos-58)/14 (округл. вниз), xl = остаток; get_tile_div_mod_m7 = @@ -19,6 +19,7 @@ #include "pop_kid.h" #include "pop_map.h" #include "pop_ctrl.h" /* pop_ctrl_shift_held() — для check_grab */ +#include "pop_geom.h" /* общая геометрия (x_bump/y_land/y_to_row) */ #include "pop_redraw.h" /* пометки перерисовки (порт set_redraw_*) */ /* pop_bg нужен ТОЛЬКО падающему куску (mob): spawn/tick/pos — это движущийся * ОБЪЕКТ, а не перерисовка тайла (в оригинале он и живёт отдельно: mobs + @@ -88,23 +89,17 @@ #define FRAME_NEEDS_FLOOR 0x40 #define FRAME_WEIGHT_X 0x1F -#define TILE_SIZEX 14 #define TILE_RIGHTX 13 #define SCREENSPACE_X 58 #define FALL_ACCEL 3 #define FALL_MAX 33 #define TILE_MIDX 7 -#define FIRST_ONSCREEN_COLUMN 5 #define FRAME_THIN 0x20 -static const int16_t y_land[5] = {-8, 55, 118, 181, 244}; + static const int8_t dir_front[2] = {-1, 1}; /* [dir+1]: dir=-1->-1, 0->+1 */ static const int8_t dir_behind[2] = {1, -1}; /* [dir+1]: назад по направлению */ -static const int16_t x_bump[20] = { - -12, 2, 16, 30, 44, 58, 72, 86, 100, 114, - 128, 142, 156, 170, 184, 198, 212, 226, 240, 254 -}; /* wall_type(tile): gate/doortop=1, mirror=2, chomper=3, wall=4, иначе 0. */ static const int8_t wall_dl[6] = {0, 10, 0, -1, 0, 0}; /* wall_dist_from_left */ static const int8_t wall_dr[6] = {0, 0, 10, 13, 0, 0}; /* wall_dist_from_right */ @@ -372,7 +367,7 @@ static int dist_from_wall_forward(uint8_t tiletype, int8_t tcol) uint8_t type = wall_type(tiletype); int coll_left, cx; if (type == 0) return 127; - coll_left = x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + coll_left = pop_x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; cx = char_front_coll(); if (Kid.direction < 0) /* лицом влево */ return cx - (coll_left + TILE_RIGHTX - wall_dr[type]); @@ -428,7 +423,7 @@ static uint8_t fell_on_spikes(void) static void land(void) { uint8_t seq; - Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; + Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; if (fell_on_spikes()) return; /* упал на вредные пики — смерть */ /* к краю пола — чуть назад (как оригинал) */ if (!tile_is_floor(get_tile_infrontof_char()) && distance_to_edge_weight() < 3) @@ -523,7 +518,7 @@ static void check_grab(void) uint8_t old_x; if (!pop_ctrl_shift_held()) return; /* Shift не зажат */ if ((uint8_t)Kid.fall_y >= 32) return; /* падает слишком быстро */ - if ((uint16_t)y_land[Kid.curr_row + 1] > (uint16_t)(Kid.y + 25)) return; + if ((uint16_t)pop_y_land[Kid.curr_row + 1] > (uint16_t)(Kid.y + 25)) return; old_x = Kid.x; Kid.x = (uint8_t)char_dx_forward(-8); determine_col(); @@ -532,7 +527,7 @@ static void check_grab(void) determine_col(); } else { Kid.x = (uint8_t)char_dx_forward((int8_t)distance_to_edge_weight()); - Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; + Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; Kid.fall_y = 0; kid_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR); play_seq(); @@ -544,8 +539,8 @@ static void check_grab(void) static void do_fall(void) { uint8_t nrow = (uint8_t)(Kid.curr_row + 1); - if (nrow > 4) nrow = 4; /* защита y_land[] от выхода */ - if ((uint16_t)y_land[nrow] > (uint16_t)Kid.y) { + if (nrow > 4) nrow = 4; /* защита pop_y_land[] от выхода */ + if ((uint16_t)pop_y_land[nrow] > (uint16_t)Kid.y) { check_grab(); /* ещё летит — попытка зацепа */ } else { if (get_tile_at_char() == TILE_WALL) @@ -905,11 +900,11 @@ static void bumped_floor(void) uint8_t frame; /* Оригинал сравнивает БЕЗЗНАКОВО: если персонаж НИЖЕ уровня пола, * разность заворачивается в большое число и тоже даёт «падать». */ - if ((uint16_t)(y_land[Kid.curr_row + 1] - (int16_t)Kid.y) >= 15) { + if ((uint16_t)(pop_y_land[Kid.curr_row + 1] - (int16_t)Kid.y) >= 15) { bumped_fall(); /* высоко над полом — в воздухе */ return; } - Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; /* ПРИЖАТЬ к полу */ + Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; /* ПРИЖАТЬ к полу */ if (Kid.fall_y >= 22) { /* влетел быстро — только отжать */ Kid.x = (uint8_t)char_dx_forward(-5); return; @@ -947,7 +942,7 @@ static void check_bumped(void) * hang->hang_fall->seq_11 (правильное приземление). */ if (Kid.frame >= 87 && Kid.frame < 100) return; /* Вне рядов комнаты (падение мимо пола): get_tile даёт WALL, а - * dist_from_wall_forward/x_bump[] — мусор с большим отриц. сдвигом → + * dist_from_wall_forward/pop_x_bump[] — мусор с большим отриц. сдвигом → * underflow X и уход в стену. fell_out ловит do_fall. */ if (Kid.curr_row < 0 || Kid.curr_row > 2) return; /* seg004 check_collisions: на кадрах разворота коллизии НЕ считаются @@ -978,7 +973,7 @@ static void check_bumped(void) { uint8_t f = 0, prev, need; if (wt) { - int coll_left = x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; + int coll_left = pop_x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX; int lw = wall_dl[wt] + coll_left; /* get_left_wall_xpos */ int rw = coll_left - wall_dr[wt] + TILE_RIGHTX; /* get_right_wall_xpos */ int e = char_x_forward_edge(), wh = ((int)kid_fp_width() + 1) >> 1; @@ -1157,7 +1152,7 @@ static void fell_on_your_head(void) uint8_t frame = Kid.frame, action = Kid.action; if ((frame < 5 || frame >= 15) && (action < ACT_HANGCLIMB || action == ACT_TURN)) { - Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; + Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; pop_kid_hurt = 1; /* hitp_delta<0 → draw_hurt_splash */ if (take_hp(1)) { kid_set_seq(SEQ_22_CRUSHED); @@ -1269,8 +1264,7 @@ void pop_loose_tick(void) } } -/* y_to_row_mod4 (seg006): ряд по y с оборотом mod4 (TILE_SIZEY=63). */ -static int8_t y_to_row(int16_t y) { return (int8_t)(((y + 60) / 63) % 4 - 1); } + /* ---- clip_char (seg006:1749), первый блок: обрезка спрайта СВЕРХУ ----- * * Оригинал перед add_objtable кладёт в objtable прямоугольник клипа; блиттер @@ -1299,7 +1293,7 @@ int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) xr = xl + wh; /* char_x_right */ top_y = obj_y - (int)h + 1; /* char_top_y */ if (top_y >= 192) top_y = 0; /* весь спрайт ниже комнаты */ - trow = y_to_row((int16_t)top_y); /* char_top_row */ + trow = pop_y_to_row((int16_t)top_y); /* char_top_row */ cL = get_tile_div_mod(xl); if (cL < 0) cL = 0; cR = get_tile_div_mod(xr); if (cR > 9) cR = 9; @@ -1337,13 +1331,13 @@ static void check_leave_below(void) Kid.action != ACT_MIDAIR && (int8_t)Kid.y < 10 && (int8_t)Kid.y > -16) { Kid.y = (uint8_t)(Kid.y + 189); - Kid.curr_row = y_to_row((int16_t)Kid.y); + Kid.curr_row = pop_y_to_row((int16_t)Kid.y); pop_leave_dir = 3; return; } if (Kid.y >= 211) { Kid.y = (uint8_t)(Kid.y - 189); - Kid.curr_row = y_to_row((int16_t)Kid.y); + Kid.curr_row = pop_y_to_row((int16_t)Kid.y); pop_fell_out = 1; } } @@ -1429,8 +1423,8 @@ static int is_spike_harmful(uint8_t tilepos) static void spiked(uint8_t tilepos) { pop_trob_modif(g_room)[tilepos] = 0xFF; - Kid.y = (uint8_t)y_land[Kid.curr_row + 1]; - Kid.x = (uint8_t)(x_bump[Kid.curr_col + FIRST_ONSCREEN_COLUMN] + 10); + Kid.y = (uint8_t)pop_y_land[Kid.curr_row + 1]; + Kid.x = (uint8_t)(pop_x_bump[Kid.curr_col + FIRST_ONSCREEN_COLUMN] + 10); Kid.x = (uint8_t)char_dx_forward(8); Kid.fall_y = 0; take_hp(100); /* (TODO: sound_48_spiked) */ diff --git a/applications/PoP/roomtest/pop_trob.c b/applications/PoP/roomtest/pop_trob.c index 8c01350..83298b7 100644 --- a/applications/PoP/roomtest/pop_trob.c +++ b/applications/PoP/roomtest/pop_trob.c @@ -5,6 +5,7 @@ */ #include "pop_trob.h" #include "pop_level.h" +#include "pop_geom.h" #include "pop_redraw.h" /* пометки перерисовки тайлов (вместо прямых вызовов) */ /* pop_bg нужен ТОЛЬКО для пламени факела и пузырька зелья: это не тайловая * перерисовка, а покадровый оверлей поверх фона (в оригинале — свои @@ -48,11 +49,7 @@ static uint8_t trob_drawn[POP_ROOMTILES]; * выбора следующего кадра пламени. */ static unsigned long trob_seed; -static uint16_t trob_prandom(uint16_t maxv) -{ - trob_seed = trob_seed * 214013UL + 2531011UL; - return (uint16_t)((uint16_t)(trob_seed >> 16) % (uint16_t)(maxv + 1)); -} +#define trob_prandom(maxv) pop_prandom(&trob_seed, (maxv)) void pop_trob_reset(void)