xrick2gs/src/draw.c

684 lines
14 KiB
C

/*
* xrick/src/draw.c
*
* Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
*
* The use and distribution terms for this software are contained in the file
* named README, which can be found in the root of this distribution. By
* using this software in any fashion, you are agreeing to be bound by the
* terms of this license.
*
* You must not remove this notice, or any other, from this software.
*/
/*
* NOTES
*
* This is the only file which accesses the video. Anything calling d_*
* function should be video-independant.
*
* draw.c draws into a 320x200 or 0x0140x0xc8 8-bits depth frame buffer,
* using the CGA 2 bits color codes. It is up to the video to figure out
* how to display the frame buffer. Whatever draw.c does, does not show
* until the screen is explicitely refreshed.
*
* The "screen" is the whole 0x0140 by 0x00c8 screen, coordinates go from
* 0x0000,0x0000 to 0x013f,0x00c7.
*
* The "map" is a 0x0100 by 0x0140 rectangle that represents the active
* game area.
*
* Relative to the screen, the "map" is located at 0x0020,-0x0040 : the
* "map" is composed of two hidden 0x0100 by 0x0040 rectangles (one at the
* top and one at the bottom) and one visible 0x0100 by 0x00c0 rectangle (in
* the middle).
*
* The "map screen" is the visible rectangle ; it is a 0x0100 by 0xc0
* rectangle located at 0x0020,0x00.
*
* Coordinates can be relative to the screen, the map, or the map screen.
*
* Coordinates can be expressed in pixels. When relative to the map or the
* map screen, they can also be expressed in tiles, the map being composed
* of rows of 0x20 tiles of 0x08 by 0x08 pixels.
*/
#include "system.h"
#include "game.h"
#include "draw.h"
#include "sysvid.h"
#include "sprites.h"
#include "tiles.h"
#include "maps.h"
#include "rects.h"
#include "img.h"
/*
* counters positions (pixels, screen)
*/
#ifdef GFXPC
#define DRAW_STATUS_SCORE_X 0x28
#define DRAW_STATUS_LIVES_X 0xE8
#define DRAW_STATUS_Y 0x08
#endif
#define DRAW_STATUS_BULLETS_X 0x68
#define DRAW_STATUS_BOMBS_X 0xA8
#ifdef GFXST
#define DRAW_STATUS_SCORE_X 0x20
#define DRAW_STATUS_LIVES_X 0xF0
#define DRAW_STATUS_Y 0
#endif
/*
* public vars
*/
U8 *draw_tllst; /* pointer to tiles list */
#ifdef GFXPC
U16 draw_filter; /* CGA colors filter */
#endif
U8 draw_tilesBank; /* tile number offset */
rect_t draw_STATUSRECT = {
DRAW_STATUS_SCORE_X, DRAW_STATUS_Y,
DRAW_STATUS_LIVES_X + 6 * 8 - DRAW_STATUS_SCORE_X, 8,
NULL
};
rect_t draw_SCREENRECT = { 0, 0, SYSVID_WIDTH, SYSVID_HEIGHT, NULL };
/*
* private vars
*/
static U8 *fb; /* frame buffer pointer */
/*
* Set the frame buffer pointer
*
* x, y: position (pixels, screen)
*/
void
draw_setfb(U16 x, U16 y)
{
fb = sysvid_fb + x + y * SYSVID_WIDTH;
}
/*
* Clip to map screen
*
* x, y: position (pixels, map) CHANGED clipped
* width, height: dimension CHANGED clipped
* return: TRUE if fully clipped, FALSE if still (at least partly) visible
*/
U8
draw_clipms(S16 *x, S16 *y, U16 *width, U16 *height)
{
if (*x < 0) {
if (*x + *width < 0)
return TRUE;
else {
*width += *x;
*x = 0;
}
}
else {
if (*x > 0x0100)
return TRUE;
else if (*x + *width > 0x0100) {
*width = 0x0100 - *x;
}
}
if (*y < DRAW_XYMAP_SCRTOP) {
if ((*y + *height) < DRAW_XYMAP_SCRTOP)
return TRUE;
else {
*height += *y - DRAW_XYMAP_SCRTOP;
*y = DRAW_XYMAP_SCRTOP;
}
}
else {
if (*y >= DRAW_XYMAP_HBTOP)
return TRUE;
else if (*y + *height > DRAW_XYMAP_HBTOP)
*height = DRAW_XYMAP_HBTOP - *y;
}
return FALSE;
}
/*
* Draw a list of tiles onto the frame buffer
* start at position indicated by fb ; at the end of each (sub)list,
* perform a "carriage return + line feed" i.e. go back to the initial
* position then go down one tile row (8 pixels)
*
* ASM 1e33
* fb: CHANGED (see above)
* draw_tllst: CHANGED points to the element following 0xfe/0xff end code
*/
void
draw_tilesList(void)
{
U8 *t;
t = fb;
while (draw_tilesSubList() != 0xFE) { /* draw sub-list */
t += 8 * SYSVID_WIDTH; /* go down one tile i.e. 8 lines */
fb = t;
}
}
/*
* Draw a list of tiles onto the frame buffer -- same as draw_tilesList,
* but accept an immediate string as parameter. Note that the string needs
* to be properly terminated with 0xfe (\376) and 0xff (\377) chars.
*/
void
draw_tilesListImm(U8 *list)
{
draw_tllst = list;
draw_tilesList();
}
/*
* Draw a sub-list of tiles onto the frame buffer
* start at position indicated by fb ; leave fb pointing to the next
* tile to the right of the last tile drawn
*
* ASM 1e41
* fpb: CHANGED (see above)
* draw_tllst: CHANGED points to the element following 0xfe/0xff end code
* returns: end code (0xfe : end of list ; 0xff : end of sub-list)
*/
U8
draw_tilesSubList()
{
U8 i;
i = *(draw_tllst++);
while (i != 0xFF && i != 0xFE) { /* while not end */
draw_tile(i); /* draw tile */
i = *(draw_tllst++);
}
return i;
}
/*
* Draw a tile
* at position indicated by fb ; leave fb pointing to the next tile
* to the right of the tile drawn
*
* ASM 1e6c
* tlnbr: tile number
* draw_filter: CGA colors filter
* fb: CHANGED (see above)
*/
void
draw_tile(U8 tileNumber)
{
U8 i, k, *f;
#ifdef GFXPC
U16 x;
#endif
#ifdef GFXST
U32 x;
#endif
f = fb; /* frame buffer */
for (i = 0; i < 8; i++) { /* for all 8 pixel lines */
#ifdef GFXPC
x = tiles_data[draw_tilesBank][tileNumber][i] & draw_filter;
/*
* tiles / perform the transformation from CGA 2 bits
* per pixel to frame buffer 8 bits per pixels
*/
for (k = 8; k--; x >>= 2)
f[k] = x & 3;
f += SYSVID_WIDTH; /* next line */
#endif
#ifdef GFXST
x = tiles_data[draw_tilesBank][tileNumber][i];
/*
* tiles / perform the transformation from ST 4 bits
* per pixel to frame buffer 8 bits per pixels
*/
for (k = 8; k--; x >>= 4)
f[k] = x & 0x0F;
f += SYSVID_WIDTH; /* next line */
#endif
}
fb += 8; /* next tile */
}
/*
* Draw a sprite
*
* ASM 1a09
* nbr: sprite number
* x, y: sprite position (pixels, screen)
* fb: CHANGED
*/
#ifdef GFXPC
void
draw_sprite(U8 nbr, U16 x, U16 y)
{
U8 i, j, k, *f;
U16 xm = 0, xp = 0;
draw_setfb(x, y);
for (i = 0; i < 4; i++) { /* for each tile column */
f = fb; /* frame buffer */
for (j = 0; j < 0x15; j++) { /* for each pixel row */
xm = sprites_data[nbr][i][j].mask; /* mask */
xp = sprites_data[nbr][i][j].pict; /* picture */
/*
* sprites / perform the transformation from CGA 2 bits
* per pixel to frame buffer 8 bits per pixels
*/
for (k = 8; k--; xm >>= 2, xp >>= 2)
f[k] = (f[k] & (xm & 3)) | (xp & 3);
f += SYSVID_WIDTH;
}
fb += 8;
}
}
#endif
/*
* Draw a sprite
*
* foobar
*/
#ifdef GFXST
void
draw_sprite(U8 number, U16 x, U16 y)
{
U8 i, j, k, *f;
U16 g;
U32 d;
draw_setfb(x, y);
g = 0;
for (i = 0; i < 0x15; i++) { /* rows */
f = fb;
for (j = 0; j < 4; j++) { /* cols */
d = sprites_data[number][g++];
for (k = 8; k--; d >>= 4)
if (d & 0x0F) f[k] = (f[k] & 0xF0) | (d & 0x0F);
f += 8;
}
fb += SYSVID_WIDTH;
}
}
#endif
/*
* Draw a sprite
*
* NOTE re-using original ST graphics format
*/
#ifdef GFXST
void
draw_sprite2(U8 number, U16 x, U16 y, U8 front)
{
U32 d = 0; /* sprite data */
S16 x0, y0; /* clipped x, y */
U16 w, h; /* width, height */
S16 g, /* sprite data offset*/
r, c, /* row, column */
i, /* frame buffer shifter */
im; /* tile flag shifter */
U8 flg; /* tile flag */
x0 = x;
y0 = y;
w = 0x20;
h = 0x15;
if (draw_clipms(&x0, &y0, &w, &h)) /* return if not visible */
return;
g = 0;
draw_setfb(x0 - DRAW_XYMAP_SCRLEFT, y0 - DRAW_XYMAP_SCRTOP + 8);
for (r = 0; r < 0x15; r++) {
if (r >= h || y + r < y0) continue;
i = 0x1f;
im = x - (x & 0xfff8);
flg = map_eflg[map_map[(y + r) >> 3][(x + 0x1f)>> 3]];
#ifdef ENABLE_CHEATS
#define LOOP(N, C0, C1) \
d = sprites_data[number][g + N]; \
for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
if (im == 0) { \
flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
im = 8; \
} \
if (c >= w || x + c < x0) continue; \
if (!front && !game_cheat3 && (flg & MAP_EFLG_FGND)) continue; \
if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
if (game_cheat3) fb[i] |= 0x10; \
}
#else
#define LOOP(N, C0, C1) \
d = sprites_data[number][g + N]; \
for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
if (im == 0) { \
flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
im = 8; \
} \
if (!front && (flg & MAP_EFLG_FGND)) continue; \
if (c >= w || x + c < x0) continue; \
if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
}
#endif
LOOP(3, 0x1f, 0x18);
LOOP(2, 0x17, 0x10);
LOOP(1, 0x0f, 0x08);
LOOP(0, 0x07, 0x00);
#undef LOOP
fb += SYSVID_WIDTH;
g += 4;
}
}
#endif
/*
* Draw a sprite
* align to tile column, determine plane automatically, and clip
*
* nbr: sprite number
* x, y: sprite position (pixels, map).
* fb: CHANGED
*/
#ifdef GFXPC
void
draw_sprite2(U8 number, U16 x, U16 y, U8 front)
{
U8 k, *f, c, r, dx;
U16 cmax, rmax;
U16 xm = 0, xp = 0;
S16 xmap, ymap;
/* align to tile column, prepare map coordinate and clip */
xmap = x & 0xFFF8;
ymap = y;
cmax = 0x20; /* width, 4 tile columns, 8 pixels each */
rmax = 0x15; /* height, 15 pixels */
dx = (x - xmap) * 2;
if (draw_clipms(&xmap, &ymap, &cmax, &rmax)) /* return if not visible */
return;
/* get back to screen */
draw_setfb(xmap - DRAW_XYMAP_SCRLEFT, ymap - DRAW_XYMAP_SCRTOP);
xmap >>= 3;
cmax >>= 3;
/* draw */
for (c = 0; c < cmax; c++) { /* for each tile column */
f = fb;
for (r = 0; r < rmax; r++) { /* for each pixel row */
/* check that tile is not hidden behind foreground */
#ifdef ENABLE_CHEATS
if (front || game_cheat3 ||
!(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
#else
if (front ||
!(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
#endif
xp = xm = 0;
if (c > 0) {
xm |= sprites_data[number][c - 1][r].mask << (16 - dx);
xp |= sprites_data[number][c - 1][r].pict << (16 - dx);
}
else
xm |= 0xFFFF << (16 - dx);
if (c < cmax) {
xm |= sprites_data[number][c][r].mask >> dx;
xp |= sprites_data[number][c][r].pict >> dx;
}
else
xm |= 0xFFFF >> dx;
/*
* sprites / perform the transformation from CGA 2 bits
* per pixel to frame buffer 8 bits per pixels
*/
for (k = 8; k--; xm >>= 2, xp >>= 2) {
f[k] = ((f[k] & (xm & 3)) | (xp & 3));
#ifdef ENABLE_CHEATS
if (game_cheat3) f[k] |= 4;
#endif
}
}
f += SYSVID_WIDTH;
}
fb += 8;
}
}
#endif
/*
* Redraw the map behind a sprite
* align to tile column and row, and clip
*
* x, y: sprite position (pixels, map).
*/
void
draw_spriteBackground(U16 x, U16 y)
{
U8 r, c;
U16 rmax, cmax;
S16 xmap, ymap;
U16 xs, ys;
/* aligne to column and row, prepare map coordinate, and clip */
xmap = x & 0xFFF8;
ymap = y & 0xFFF8;
cmax = (x - xmap == 0 ? 0x20 : 0x28); /* width, 4 tl cols, 8 pix each */
rmax = (y & 0x04) ? 0x20 : 0x18; /* height, 3 or 4 tile rows */
if (draw_clipms(&xmap, &ymap, &cmax, &rmax)) /* don't draw if fully clipped */
return;
/* get back to screen */
xs = xmap - DRAW_XYMAP_SCRLEFT;
ys = ymap - DRAW_XYMAP_SCRTOP;
xmap >>= 3;
ymap >>= 3;
cmax >>= 3;
rmax >>= 3;
/* draw */
for (r = 0; r < rmax; r++) { /* for each row */
#ifdef GFXPC
draw_setfb(xs, ys + r * 8);
#endif
#ifdef GFXST
draw_setfb(xs, 8 + ys + r * 8);
#endif
for (c = 0; c < cmax; c++) { /* for each column */
draw_tile(map_map[ymap + r][xmap + c]);
}
}
}
/*
* Draw entire map screen background tiles onto frame buffer.
*
* ASM 0af5, 0a54
*/
void
draw_map(void)
{
U8 i, j;
draw_tilesBank = map_tilesBank;
for (i = 0; i < 0x18; i++) { /* 0x18 rows */
#ifdef GFXPC
draw_setfb(0x20, (i * 8));
#endif
#ifdef GFXST
draw_setfb(0x20, 8 + (i * 8));
#endif
for (j = 0; j < 0x20; j++) /* 0x20 tiles per row */
draw_tile(map_map[i + 8][j]);
}
}
/*
* Draw status indicators
*
* ASM 0309
*/
void
draw_drawStatus(void)
{
S8 i;
U32 sv;
static U8 s[7] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfe};
draw_tilesBank = 0;
for (i = 5, sv = game_score; i >= 0; i--) {
s[i] = 0x30 + (U8)(sv % 10);
sv /= 10;
}
draw_tllst = s;
draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
draw_tilesList();
draw_setfb(DRAW_STATUS_BULLETS_X, DRAW_STATUS_Y);
for (i = 0; i < game_bullets; i++)
draw_tile(TILES_BULLET);
draw_setfb(DRAW_STATUS_BOMBS_X, DRAW_STATUS_Y);
for (i = 0; i < game_bombs; i++)
draw_tile(TILES_BOMB);
draw_setfb(DRAW_STATUS_LIVES_X, DRAW_STATUS_Y);
for (i = 0; i < game_lives; i++)
draw_tile(TILES_RICK);
}
/*
* Draw info indicators
*/
#ifdef ENABLE_CHEATS
void
draw_infos(void)
{
draw_tilesBank = 0;
#ifdef GFXPC
draw_filter = 0xffff;
#endif
draw_setfb(0x00, DRAW_STATUS_Y);
draw_tile(game_cheat1 ? 'T' : '@');
draw_setfb(0x08, DRAW_STATUS_Y);
draw_tile(game_cheat2 ? 'N' : '@');
draw_setfb(0x10, DRAW_STATUS_Y);
draw_tile(game_cheat3 ? 'V' : '@');
}
#endif
/*
* Clear status indicators
*/
void
draw_clearStatus(void)
{
U8 i;
#ifdef GFXPC
draw_tilesBank = map_tilesBank;
#endif
#ifdef GFXST
draw_tilesBank = 0;
#endif
draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
for (i = 0; i < DRAW_STATUS_LIVES_X/8 + 6 - DRAW_STATUS_SCORE_X/8; i++) {
#ifdef GFXPC
draw_tile(map_map[MAP_ROW_SCRTOP + (DRAW_STATUS_Y / 8)][i]);
#endif
#ifdef GFXST
draw_tile('@');
#endif
}
}
/*
* Draw a picture
*/
#ifdef GFXST
void
draw_pic(U16 x, U16 y, U16 w, U16 h, U32 *pic)
{
U8 *f;
U16 i, j, k, pp;
U32 v;
draw_setfb(x, y);
pp = 0;
for (i = 0; i < h; i++) { /* rows */
f = fb;
for (j = 0; j < w; j += 8) { /* cols */
v = pic[pp++];
for (k = 8; k--; v >>=4)
f[k] = v & 0x0F;
f += 8;
}
fb += SYSVID_WIDTH;
}
}
#endif
/*
* Draw a bitmap
*/
void
draw_img(img_t *i)
{
U16 k;
draw_setfb(0, 0);
if (i->ncolors > 0)
sysvid_setPalette(i->colors, i->ncolors);
for (k = 0; k < SYSVID_WIDTH * SYSVID_HEIGHT; k++)
fb[k] = i->pixels[k];
}
/* eof */