cd8d566d82
Порт PoP на Sprinter. Текущий PoC — applications/PoP/roomtest/: комната 1 (фон-композиция тайлов) + Kid с управлением на raw-клавиатуре и коллизией с картой. - roomtest — pop_bg (фон), pop_kid (спрайты Kid, column-major флип, seqtbl-анимация), pop_ctrl (порт control() PoP на held-state kbd_raw), pop_map (коллизия seg004/005: бег/стоп у стены, падение/приземление, отскок seq_47, вертикальный прыжок K4.1). MEMORY=small (DATA сразу за CODE, ~23КБ кода не лезет в huge). - toolchain (PoP) — pop_pack_kid/pop_pack_bg/render_room/extract — распаковка res-графики MSDOS в атласы + композиция комнат. - toolchain/ (корень) — make_hdd.sh (быстрый HDD-тест вместо FDD), png_strip.py / room_compose.py (ассет-пайплайн). - docs — PORT_PLAN, KID_PLAN, форматы ресурсов (Apple II / MSDOS / DAT). - bgtest/coltest/poc — ранние PoC (фон, коллизия, первый прототип). .gitignore: build-артефакты applications/*/*/*; исключены внешние референс-репозитории (SDLPoP/mininim/PR/Apple-II — свои git-клоны) и оригинальные game-данные MSDOS/ (копирайт, только для реверса форматов). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
159 lines
6.3 KiB
C
159 lines
6.3 KiB
C
/*
|
||
* room.c — вся логика комнаты, ОБЫЧНЫЙ HOME-файл (без --bank).
|
||
*
|
||
* 2026-07-15/16: изначально была банковская (--bank 1), сначала в W3
|
||
* (huge-дефолт), потом пробовали W1 (--memory big) — оба варианта
|
||
* упирались в проблемы (W3 конфликтует с gfx_blit_part/atlas_load,
|
||
* которые сами трогают W3; W1-банк несовместим с CODE=W1 — трамплин
|
||
* сам бы уехал из W1; --memory big тесно — CODE+DATA+BSS ~20КБ, не
|
||
* лезет в один 16КБ). После замены sprintf() на ручное hex-форматирование
|
||
* пути (экономия ~2.9КБ — sprintf/vsprintf тянули за собой заметный
|
||
* кусок форматирующего кода, единственный потребитель в HOME) всё
|
||
* снова влезает в W1 обычным HOME-кодом — банк ОКАЗАЛСЯ НЕ НУЖЕН.
|
||
* Так проще: gfx_blit_part()/atlas_load() (трогают W3 транзитивно)
|
||
* теперь просто обычные HOME-функции, без всяких W1/W3-танцев.
|
||
*
|
||
* gfx_blit_part пишет ПРЯМО в текущий банк (видео-ОЗУ + теневая копия
|
||
* при GFX_BANK_NORMAL) — перманентно, не через sprite_t. Вызывающий
|
||
* код обязан позвать room_draw() на КАЖДУЮ из двух страниц дабл-буфера
|
||
* при входе в комнату, иначе heal спрайтов увидит фон только на одной
|
||
* странице.
|
||
*/
|
||
#include <stdio.h>
|
||
#include <gfx.h>
|
||
#include "room.h"
|
||
|
||
/* 32x64 на тайл: 3*64+8=200 — сходится с разрешением DOS 320x200
|
||
* (8px внизу — под полоску жизней/строку текста, не часть игрового
|
||
* поля). Уточнить точнее, когда будут готовы настоящие ассеты. */
|
||
uint8_t room_row_h[ROOM_ROWS] = {64, 64, 64};
|
||
tile_atlas_t tile_atlas[32];
|
||
|
||
int tile_atlas_load(const char *dir)
|
||
{
|
||
static const char HEXD[] = "0123456789abcdef";
|
||
char path[64];
|
||
int i, count = 0;
|
||
|
||
for (i = 0; i < 32; i++) {
|
||
/* Вручную, не sprintf("%stile%02x.atl",...) — единственный
|
||
* потребитель printf-семейства в этом приложении, тянул за
|
||
* собой ~2.9КБ форматирующего кода; формат тут фиксированный
|
||
* (2 hex-цифры). */
|
||
char *p = path;
|
||
const char *s = dir;
|
||
while (*s) *p++ = *s++;
|
||
s = "tile";
|
||
while (*s) *p++ = *s++;
|
||
*p++ = HEXD[(i >> 4) & 0xF];
|
||
*p++ = HEXD[i & 0xF];
|
||
s = ".atl";
|
||
while (*s) *p++ = *s++;
|
||
*p = '\0';
|
||
if (atlas_load(&tile_atlas[i].at, path) == 0) {
|
||
tile_atlas[i].loaded = 1;
|
||
count++;
|
||
} else {
|
||
tile_atlas[i].loaded = 0;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
void tile_atlas_free(void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 32; i++) {
|
||
if (tile_atlas[i].loaded) {
|
||
atlas_free(&tile_atlas[i].at);
|
||
tile_atlas[i].loaded = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
int level_load_room(level_t *lv, uint8_t room, const char *path)
|
||
{
|
||
FILE *f = fopen(path, "rb");
|
||
uint8_t buf[ROOM_TILES * 2 + 3]; /* fore[30]+back[30]+start_room/pos/dir */
|
||
int i;
|
||
|
||
if (!f)
|
||
return -1;
|
||
if (fread(buf, 1, sizeof(buf), f) != sizeof(buf)) {
|
||
fclose(f);
|
||
return -1;
|
||
}
|
||
fclose(f);
|
||
|
||
for (i = 0; i < ROOM_TILES; i++) {
|
||
tile_t *t = level_tile(lv, room, (uint8_t)(i % ROOM_COLS),
|
||
(uint8_t)(i / ROOM_COLS));
|
||
t->type = buf[i];
|
||
t->state = buf[ROOM_TILES + i];
|
||
}
|
||
lv->start_room = buf[ROOM_TILES * 2];
|
||
lv->start_location = buf[ROOM_TILES * 2 + 1];
|
||
lv->start_direction = (int8_t)buf[ROOM_TILES * 2 + 2];
|
||
return 0;
|
||
}
|
||
|
||
/* _bgi_blit_rows_raw держит ОДИН DI на весь блит (см. её шапку) —
|
||
* вызывающий обязан резать блиты выше 16 строк на чанки, иначе окно
|
||
* DI слишком длинное (риск сорвать кадровую цепочку IRQ/CBL). Наши
|
||
* тайлы 64px высотой — режем на чанки по CHUNK_H строк. */
|
||
#define CHUNK_H 16
|
||
|
||
void room_draw(level_t *lv, uint8_t room)
|
||
{
|
||
int row, col, y = 0;
|
||
|
||
for (row = 0; row < ROOM_ROWS; row++) {
|
||
int x = 0;
|
||
uint8_t fh = room_row_h[row];
|
||
|
||
for (col = 0; col < ROOM_COLS; col++) {
|
||
tile_t *t = level_tile(lv, room, (uint8_t)col, (uint8_t)row);
|
||
uint8_t type = TILE_TYPE(t->type);
|
||
|
||
if (tile_atlas[type].loaded) {
|
||
uint8_t count = tile_atlas[type].at.count;
|
||
uint8_t variant = 0;
|
||
uint8_t is_var;
|
||
|
||
if (count != 0) {
|
||
switch (type) {
|
||
case TILE_SPIKES:
|
||
case TILE_GATE:
|
||
case TILE_TAPESTRY:
|
||
case TILE_TAPESTRY_TOP:
|
||
case TILE_CHOPPER:
|
||
case TILE_POTION:
|
||
is_var = 1;
|
||
break;
|
||
default:
|
||
is_var = 0;
|
||
}
|
||
variant = is_var
|
||
? (uint8_t)(t->state % count)
|
||
: (uint8_t)((room * 31u + col * 7u + row * 13u) % count);
|
||
}
|
||
const void *img = atlas_image(&tile_atlas[type].at, 0);
|
||
uint8_t sub_y;
|
||
|
||
gfx_w0_map(tile_atlas[type].at.page);
|
||
for (sub_y = 0; sub_y < fh; sub_y = (uint8_t)(sub_y + CHUNK_H)) {
|
||
uint8_t chunk = (uint8_t)(fh - sub_y);
|
||
if (chunk > CHUNK_H)
|
||
chunk = CHUNK_H;
|
||
gfx_blit_part(x, y + sub_y, img, variant * ROOM_TILE_W,
|
||
sub_y, ROOM_TILE_W, chunk);
|
||
}
|
||
gfx_w0_unmap();
|
||
}
|
||
x += ROOM_TILE_W;
|
||
}
|
||
y += fh;
|
||
}
|
||
}
|