Files
Sprinter-SDCC/libbgi/include/graphics.h
T
snark13 47a4b084c4 Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):

* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
  снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
  (UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
  равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
  ABOUT.  About показывает тот же текст, что стартовый Sprinter screen,
  минус строка про клавиши — общий about_text(), чтобы экраны не
  разъехались.  CONTROLS собирается таблицей и центрируется по
  фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
  (SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
  а не пункт меню.  POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
  модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах.  Нижняя:
  порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
  24 тика).  Верхняя отладочная переведена с палочек на текст
  «Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
  своим форматированием чисел (без printf и без деления).
  Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
  не растёт, весь рендер в банке.  Бюджет после правок не изменился
  (_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
  всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
  не блитятся вовсе, а вход в комнату заливает только игровое поле
  (pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
  ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
  статус-строке: геометрия, семантика text_time_total как идентификатора
  сообщения, мигание, рестарт по истечении 36/288.

Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:58:36 +03:00

253 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* graphics.h — Turbo-C-совместимый (функционально) BGI-слой для Sprinter.
*
* Слой поверх низкоуровневого gfx.h. Состояние (текущий цвет, позиция,
* границы экрана) хранится внутри — как в оригинальном BGI, где функции
* рисуют «текущим» цветом от «текущей» позиции.
*
* РЕЖИМ фиксируется на этапе ЛИНКОВКИ выбором driver-библиотеки:
* sprinter-cc --gfx 256 → 320×256×256 (эта версия)
* (--gfx 16 → 640×256×16 будет позже; API идентичен, менять код не надо)
* Одновременно два режима использовать нельзя.
*
* Отличия от Turbo-C (функциональная, а не буквальная совместимость):
* - initgraph() без аргументов-указателей на драйвер: драйвер задан
* линковкой, грузить .bgi-файл с диска не нужно.
* - в 256-режиме initgraph() загружает EGA-совместимые цвета 0..15 в
* палитру, так что setcolor(RED) и т.п. работают как в Turbo-C;
* индексы 16..255 свободны под свои цвета (getmaxcolor() = 255).
* - fill-паттерны/стили линий/стили текста — Фаза 2 (пока bar/bar-подобное
* заливается текущим цветом, линии сплошные, текст 8×8).
*/
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <stdint.h>
/* ---- Стандартные EGA/VGA цвета (индексы палитры 0..15) ------------ */
enum {
BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA,
YELLOW, WHITE
};
/* 8-битный цвет (индекс палитры). При смене формата достаточно поменять
* один typedef — все public-функции используют этот тип. */
typedef uint8_t color_t;
/* ---- Коды graphresult() ------------------------------------------ */
#define grOk 0
#define grNoInitGraph (-1)
#define grError (-11)
/* ---- Внутреннее состояние BGI ------------------------------------- *
* НЕ трогать напрямую — только через функции ниже. Объявлено здесь,
* чтобы тривиальные аксессоры были inline (без call/ret и без
* .rel-модуля на каждый геттер). Именно `inline` БЕЗ static: SDCC
* инлайнит вызов и НЕ эмитит standalone-тело (static inline эмитил бы
* мёртвую копию в каждый включивший модуль — проверено 2026-07-10);
* если SDCC когда-нибудь откажется инлайнить — будет громкая ошибка
* линковки, не тихая деградация. Хранилище — libbgi/common/
* _bgi_state.c и _bgi_fill_state.c. */
extern uint8_t _bgi_fg, _bgi_bg;
extern int _bgi_cx, _bgi_cy;
extern int _bgi_maxx, _bgi_maxy, _bgi_maxcolor;
extern int _bgi_result;
extern uint8_t _bgi_fill_pattern, _bgi_fill_color;
/* ---- Setup / teardown -------------------------------------------- */
/* Перейти в графический режим (для этой либы — 320×256×256), загрузить
* EGA-палитру, сбросить состояние: цвет = WHITE, фон = BLACK, текущая
* позиция = (0,0). Предыдущий видеорежим запоминается для closegraph. */
void initgraph(void);
/* То же состояние BGI, но без загрузки EGA-палитры; перед SETVMOD очищает
* обе графические VRAM-страницы для чистого собственного fade-in. */
void initgraph_black(void);
/* Вернуться в текстовый режим, действовавший до initgraph(). */
void closegraph(void);
/* Код последней ошибки; вызов сбрасывает его в grOk (как в BGI). */
inline int graphresult(void)
{
int r = _bgi_result;
_bgi_result = grOk;
return r;
}
/* Очистить экран фоновым цветом и вернуть позицию в (0,0). */
void cleardevice(void);
/* ---- Границы и цвета --------------------------------------------- */
inline int getmaxx(void) { return _bgi_maxx; } /* 319 */
inline int getmaxy(void) { return _bgi_maxy; } /* 255 */
inline color_t getmaxcolor(void) { return (color_t)_bgi_maxcolor; } /* 255 */
/* текущий цвет рисования */
inline void setcolor(color_t color) { _bgi_fg = (uint8_t)color; }
inline color_t getcolor(void) { return _bgi_fg; }
/* фоновый цвет (для cleardevice/текста) */
inline void setbkcolor(color_t color) { _bgi_bg = (uint8_t)color; }
inline color_t getbkcolor(void) { return _bgi_bg; }
/* ---- Точки ------------------------------------------------------- */
void putpixel(int x, int y, color_t color);
color_t getpixel(int x, int y);
/* ---- Текущая позиция и линии ------------------------------------- */
/* задать текущую позицию (CP) */
inline void moveto(int x, int y) { _bgi_cx = x; _bgi_cy = y; }
/* сдвинуть CP относительно */
inline void moverel(int dx, int dy) { _bgi_cx += dx; _bgi_cy += dy; }
inline int getx(void) { return _bgi_cx; }
inline int gety(void) { return _bgi_cy; }
void lineto(int x, int y); /* линия CP→(x,y), CP := (x,y) */
void linerel(int dx, int dy); /* линия CP→CP+(dx,dy), CP сдвигается */
void line(int x1, int y1, int x2, int y2); /* линия, CP не меняет */
/* ---- Фигуры ------------------------------------------------------ */
void rectangle(int left, int top, int right, int bottom); /* контур */
void bar(int left, int top, int right, int bottom); /* заливка */
void circle(int x, int y, int radius); /* окружность */
/* Дуги/эллипсы: угол в градусах, 0°=восток, против часовой стрелки.
* Полный эллипс — ellipse(x,y,0,360,xr,yr). */
void arc(int x, int y, int stangle, int endangle, int radius);
void ellipse(int x, int y, int stangle, int endangle,
int xradius, int yradius);
/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */
void drawpoly(int numpoints, const int *polypoints);
/* ---- Стиль линий ------------------------------------------------- *
* Действует на line/lineto/linerel/rectangle/drawpoly. USERBIT_LINE
* использует 16-битный upattern. thickness: NORM_WIDTH или THICK_WIDTH.
* (Окружности/дуги пока всегда сплошные 1px — упрощение.) */
enum { SOLID_LINE = 0, DOTTED_LINE, CENTER_LINE, DASHED_LINE, USERBIT_LINE };
#define NORM_WIDTH 1
#define THICK_WIDTH 3
struct linesettingstype { int linestyle; unsigned upattern; int thickness; };
void setlinestyle(int linestyle, unsigned upattern, int thickness);
void getlinesettings(struct linesettingstype *lineinfo);
/* ---- Заливки ----------------------------------------------------- *
* Стиль заливки — паттерн (8×8) + цвет; действует на bar/bar3d/
* fillpoly/fillellipse. SOLID_FILL заполняет сплошняком, EMPTY_FILL —
* фоновым цветом. USER_FILL пока трактуется как SOLID. */
enum {
EMPTY_FILL = 0, SOLID_FILL, LINE_FILL, LTSLASH_FILL, SLASH_FILL,
BKSLASH_FILL, LTBKSLASH_FILL, HATCH_FILL, XHATCH_FILL,
INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL
};
struct fillsettingstype { int pattern; color_t color; };
inline void setfillstyle(int pattern, color_t color)
{
_bgi_fill_pattern = (uint8_t)pattern;
_bgi_fill_color = (uint8_t)color;
}
void getfillsettings(struct fillsettingstype *fillinfo);
/* bar — залитый прямоугольник (текущий стиль заливки, без рамки). */
/* bar3d — 3D-брусок: перёд залит стилем заливки, рёбра — тек. цветом;
* topflag != 0 рисует верхнюю грань. */
void bar3d(int left, int top, int right, int bottom, int depth, int topflag);
/* fillpoly — залитый многоугольник (авто-замыкается); контур тек.
* цветом, нутро — стилем заливки. Вогнутые заполняются по выпуклой
* оболочке строк (min/max X на строку) — упрощение. */
void fillpoly(int numpoints, const int *polypoints);
/* fillellipse — залитый эллипс (полуоси xradius,yradius). */
void fillellipse(int x, int y, int xradius, int yradius);
/* floodfill — заливка области, содержащей (x,y), текущим стилем до
* границы цвета border. Заливка сплошная цветом заливки (паттерн в
* floodfill пока не применяется — упрощение). */
void floodfill(int x, int y, int border);
/* pieslice — залитый сектор круга; sector — эллиптический сектор.
* Контур (дуга + два радиуса) тек. цветом, нутро — стилем заливки.
* Для секторов >180° возможен перелив в «выемку» (min/max по строке). */
void pieslice(int x, int y, int stangle, int endangle, int radius);
void sector(int x, int y, int stangle, int endangle,
int xradius, int yradius);
/* ---- Растровые образы (спрайты) ---------------------------------- *
* Формат буфера: 2×uint16 (ширина, высота в пикселях) + пиксели
* построчно (1 байт/пиксель в режиме 256). Буфер выделяет вызывающий
* размером imagesize(). */
enum { COPY_PUT = 0, XOR_PUT, OR_PUT, AND_PUT, NOT_PUT };
/* Байт под образ прямоугольника (left,top)-(right,bottom) включительно.
* ВНИМАНИЕ: результат — 16-бит unsigned; образы ≥64 КБ не поддержаны. */
unsigned imagesize(int left, int top, int right, int bottom);
/* Сохранить прямоугольник экрана в bitmap (размер = imagesize()). */
void getimage(int left, int top, int right, int bottom, void *bitmap);
/* Вывести образ левым-верхним углом в (left,top) операцией op
* (COPY/XOR/OR/AND/NOT_PUT). COPY_PUT — через акселератор, с
* клиппингом по экрану и текущим банком gfx_set_bank (см. <gfx.h>);
* XOR/OR/AND/NOT — по-пиксельно, без клиппинга. */
void putimage(int left, int top, const void *bitmap, int op);
/* ---- Спрайты (аппаратная прозрачность; docs/sprite-api-design.md) - *
* Спрайт — тот же буфер getimage-формата; прозрачные точки в данных =
* байты 0xFF (GFX_TRANSPARENT). Прозрачность и сохранность фона
* обеспечивает железо (банк GFX_BANK_SPRITE = 0x5C): прозрачные байты
* не записываются, ОЗУ-копия фона не трогается — стирание без
* save-буфера через gfx_heal (см. <gfx.h>).
* ВАЖНО: фон/сцену рисовать ОБЫЧНЫМ банком (0x50) — то, что нарисовано
* при активном бите 2 (0x54/0x5C), в ОЗУ-копию не попадает и heal
* вернёт то, что лежало под ним. */
/* Вывести спрайт в (x,y): банк на время вызова — GFX_BANK_SPRITE,
* затем прежний. Клиппится по экрану. */
void putsprite(int x, int y, const void *img);
/* Переместить спрайт: восстановить фон под (oldx,oldy) (gfx_heal по
* размерам img) и вывести спрайт в (x,y). Перекрытие старой и новой
* позиций безопасно (heal читает фон из ОЗУ-копии). */
void movesprite(int oldx, int oldy, int x, int y, const void *img);
/* ---- Текст ------------------------------------------------------- *
* Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */
void outtextxy(int x, int y, const char *text); /* в (x,y), CP не меняет */
void outtext(const char *text); /* в CP; CP сдвигается вправо */
/* ---- Стиль текста ------------------------------------------------ *
* Поддержан только DEFAULT_FONT (8×8, растровый); charsize 1..10 —
* целочисленный масштаб; direction — HORIZ_DIR или VERT_DIR (поворот
* 90° против часовой). Фон текста прозрачный (рисуется только цвет). */
enum { DEFAULT_FONT = 0, TRIPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT,
GOTHIC_FONT };
enum { HORIZ_DIR = 0, VERT_DIR = 1 };
struct textsettingstype {
int font;
int direction;
int charsize;
int horiz;
int vert;
};
void settextstyle(int font, int direction, int charsize);
void gettextsettings(struct textsettingstype *textinfo);
int textwidth(const char *text);
int textheight(const char *text);
#endif