/* * 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 */