/* * Copyright (C) 1999 Peter Amstutz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of *the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ /* General graphics functions */ #include #include #include #include #include #include #include "terrain.h" #include "gfx.h" #include "sky.h" #include "player.h" #include "abram5.xpm" #include "text.h" #include "game.h" #include "log.h" #define round(x) ((x - (int)x) >= .5 ? (int)x+1 : (int)x) ggi_visual_t gfx_vis; ggi_visual_t gfx_tankpic, gfx_tankpic_right, gfx_tankpic_left; int gfx_xmax = 640, gfx_ymax = 480; int gfx_xoff, gfx_yoff; int gfx_xsize, gfx_ysize; ggi_pixel gfx_tankcolor[MAX_TANKS + 1 + 1]; /* MAX_TANKS + blank + grey */ ggi_pixel gfx_wallcolor[MAX_WALLS]; ggi_pixel gfx_white; ggi_pixel gfx_green; ggi_pixel gfx_red; int gfx_visualdepth; int gfx_armorBar=0; /* The maximum value for a color. */ #define CMAX ((uint16)(~0)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) void gfxInit() { /* mode & color white (should) be */ int try_modes[5] = { GT_32BIT, GT_24BIT, GT_16BIT, GT_15BIT, GT_8BIT }; int i; /* This gets used for paletted modes (e.g. GT_8BIT) */ /* The alpha channel is unused. */ ggi_color cmap[] = { /* Greys */ {0, 0, 0, 0}, {1 * CMAX / 5, 1 * CMAX / 5, 1 * CMAX / 5, 0}, {2 * CMAX / 5, 2 * CMAX / 5, 2 * CMAX / 5, 0}, {3 * CMAX / 5, 3 * CMAX / 5, 3 * CMAX / 5, 0}, {4 * CMAX / 5, 4 * CMAX / 5, 4 * CMAX / 5, 0}, {CMAX, CMAX, CMAX, CMAX}, /* Tank colors */ {CMAX, 0, 0, 0}, /* Red */ {CMAX / 2, CMAX, CMAX / 2, 0}, /* off-Blue */ {0, 0, CMAX, 0}, /* Green */ {0, CMAX, CMAX, 0}, /* Cyan */ {CMAX, 0, CMAX, 0}, /* Magenta */ {CMAX, CMAX, 0, 0}, /* Yellow */ {CMAX / 2, 0, 3 * CMAX / 4, 0}, /* Indigo */ {3 * CMAX / 4, CMAX / 2, 0, 0}, /* Orange */ {0xf7 << 8, 0xfb << 8, 0xce << 8}, /* light goldenrod yellow */ /* Reds (for explosions) */ {CMAX / 8, 0x24 << 8, 0x24 << 8, 0}, {CMAX / 4, 0x24 << 8, 0x24 << 8, 0}, {CMAX / 3, 0x24 << 8, 0x24 << 8, 0}, {CMAX / 2, 0x24 << 8, 0x24 << 8, 0}, {3 * CMAX / 5, 0x24 << 8, 0x24 << 8, 0}, {4 * CMAX / 5, 0x24 << 8, 0x24 << 8, 0}, /* Greens (for explosions) */ {0x24 << 8, CMAX / 8, 0x24 << 8, 0}, {0x24 << 8, CMAX / 4, 0x24 << 8, 0}, {0x24 << 8, CMAX / 3, 0x24 << 8, 0}, {0x24 << 8, CMAX / 2, 0x24 << 8, 0}, {0x24 << 8, 3 * CMAX / 5, 0x24 << 8, 0}, {0x24 << 8, 4 * CMAX / 5, 0x24 << 8, 0}, /* Blues (for explosions) */ {0x24 << 8, 0x24 << 8, CMAX / 8, 0}, {0x24 << 8, 0x24 << 8, CMAX / 4, 0}, {0x24 << 8, 0x24 << 8, CMAX / 3, 0}, {0x24 << 8, 0x24 << 8, CMAX / 2, 0}, {0x24 << 8, 0x24 << 8, 3 * CMAX / 5, 0}, {0x24 << 8, 0x24 << 8, 4 * CMAX / 5, 0}, /* Sky blues (match with skyDraw) */ {CMAX >> 3, CMAX >> 3, 1 * CMAX / 5, 0}, {CMAX >> 3, CMAX >> 3, 2 * CMAX / 5, 0}, {CMAX >> 3, CMAX >> 3, 3 * CMAX / 5, 0}, {CMAX >> 3, CMAX >> 3, 4 * CMAX / 5, 0}, /* Terrain */ {0x99 << 8, 0x66 << 8, 0x1a << 8, 0}, }; uint cmapsize = sizeof(cmap) / sizeof(ggi_color); ggiInit(); gfx_vis = ggiOpen(NULL); for(i = 0; i < (sizeof(try_modes) / sizeof(int)); i++) { if(ggiCheckGraphMode (gfx_vis, gfx_xmax, gfx_ymax, gfx_xmax, gfx_ymax, try_modes[i], NULL) == 0) break; } if(i == (sizeof(try_modes) / sizeof(int))) { logPrintf(CRITICAL, "Can't do this graphics mode! Bailing...\n"); exit(1); } else ggiSetGraphMode(gfx_vis, gfx_xmax, gfx_ymax, gfx_xmax, gfx_ymax, try_modes[i]); gfx_visualdepth = try_modes[i]; gfx_xoff = 0; gfx_yoff = 0; gfx_xsize = gfx_xmax; gfx_ysize = gfx_ymax; #if 0 ggiSetColorfulPalette(gfx_vis); #else ggiSetPalette(gfx_vis, GGI_PALETTE_DONTCARE, cmapsize, cmap); #endif ggiSetFlags(gfx_vis, GGIFLAG_ASYNC); gfxInitWallColors(); skyInit(); } void gfxInitTankImage() { ggi_color c; gfx_tankpic_left = gfxXPMtoMemvis(abram5_xpm); gfx_tankpic_left = gfxScaleMemvis(gfx_tankpic_left, gfx_xsize / TANKSCREENRATIO_X, gfx_ysize / TANKSCREENRATIO_Y); gfx_tankpic_right = gfxFlipMemvis(gfx_tankpic_left); gfx_tankpic = gfx_tankpic_right; /* light goldenrod yellow */ c.r = 0xf7 << 8; c.g = 0xfb << 8; c.b = 0xce << 8; gfx_tankcolor[0] = ggiMapColor(gfx_vis, &c); /* red */ c.r = 0xFFFF; c.g = 0x0000; c.b = 0x0000; gfx_tankcolor[1] = ggiMapColor(gfx_vis, &c); gfx_red = gfx_tankcolor[1]; /* green */ c.r = 0x0000; c.g = 0xFFFF; c.b = 0x0000; gfx_tankcolor[2] = ggiMapColor(gfx_vis, &c); gfx_green = gfx_tankcolor[2]; /* off-blue */ c.r = 0x8888; c.g = 0x8888; c.b = 0xFFFF; gfx_tankcolor[3] = ggiMapColor(gfx_vis, &c); /* cyan */ c.r = 0x0000; c.g = 0xFFFF; c.b = 0xFFFF; gfx_tankcolor[4] = ggiMapColor(gfx_vis, &c); /* yellow */ c.r = 0xFFFF; c.g = 0xFFFF; c.b = 0x0000; gfx_tankcolor[5] = ggiMapColor(gfx_vis, &c); /* magenta */ c.r = 0xFFFF; c.g = 0x0000; c.b = 0xFFFF; gfx_tankcolor[6] = ggiMapColor(gfx_vis, &c); /* orange */ c.r = 0xBFFF; c.g = 0x8888; c.b = 0x0000; gfx_tankcolor[7] = ggiMapColor(gfx_vis, &c); /* indigo */ c.r = 0x8888; c.g = 0x0000; c.b = 0xBFFF; gfx_tankcolor[8] = ggiMapColor(gfx_vis, &c); /* aqua */ c.r = 0x0000; c.g = 0xBFFF; c.b = 0x8888; gfx_tankcolor[9] = ggiMapColor(gfx_vis, &c); /* white (reserved for server messages) */ c.r = 0xFFFF; c.g = 0xFFFF; c.b = 0xFFFF; gfx_tankcolor[10] = ggiMapColor(gfx_vis, &c); gfx_white = gfx_tankcolor[10]; /* grey (reserved for observers) */ c.r = 0x9999; c.g = 0x9999; c.b = 0x9999; gfx_tankcolor[11] = ggiMapColor(gfx_vis, &c); } void gfxInitWallColors() { ggi_color c; /* off - gray */ c.r = 0x8000; c.g = 0x8000; c.b = 0x8000; gfx_wallcolor[0] = ggiMapColor(gfx_vis, &c); /* concrete-white */ c.r = 0xFFFF; c.g = 0xFFFF; c.b = 0xFFFF; gfx_wallcolor[1] = ggiMapColor(gfx_vis, &c); /* padded - green */ c.r = 0x0000; c.g = 0xFFFF; c.b = 0x0000; gfx_wallcolor[2] = ggiMapColor(gfx_vis, &c); /* rubber - red */ c.r = 0xFFFF; c.g = 0x0000; c.b = 0x0000; gfx_wallcolor[3] = ggiMapColor(gfx_vis, &c); /* spring - cyan */ c.r = 0x0000; c.g = 0xFFFF; c.b = 0xFFFF; gfx_wallcolor[4] = ggiMapColor(gfx_vis, &c); /* wraparound - yellow */ c.r = 0xFFFF; c.g = 0xFFFF; c.b = 0x0000; gfx_wallcolor[5] = ggiMapColor(gfx_vis, &c); } char updateflag = 0; void gfxUpdate() { updateflag = 1; } void gfxDoUpdate() { if(updateflag) ggiFlush(gfx_vis); updateflag = 0; } void gfxShutdown() { ggiClose(gfx_vis); ggiExit(); } /* change this to one to use the fast but buggy drawing algorithm, or leave * as is to use the slow but unbuggy one */ #if 0 void gfxDrawTerrain(int sx, int sy, int sw, int sh, int tx, int ty, int tw, int th, TerrainSpans_ter * in) { register unsigned x; ggi_color col; col.r = 0x99 << 8; col.g = 0x66 << 8; col.b = 0x1a << 8; col.a = 0xFF << 8; ggiSetGCForeground(gfx_vis, ggiMapColor(gfx_vis, &col)); for(x = 0; x < sw; x++) { TerrainSpans_ter *t; int ltx = tx + x * ter_sizex / gfx_xmax; if(ltx < 0 || ltx >= ter_sizex) continue; for(t = &in[ltx]; t; t = t->nexthigher) { int start, end, gy, gh; if(t->start + t->height < ty) continue; if(t->start > ty + th) break; start = t->start < ty ? ty : t->start; end = (t->start + t->height > th + ty) ? th + ty : t->start + t->height; gy = start * gfx_ymax / ter_sizey; gh = (end - start) * gfx_ymax / ter_sizey; /* move rounding error to where its less visible */ if(gy > 0) gy--; /* flip */ gy = gfx_ymax - gy - gh; /* clip */ if(gh <= 0) continue; /* blip, er, blit :) */ ggiDrawVLine(gfx_vis, sx + x, gy, gh); } } } #else void gfxDrawTerrain(int sx, int sy, int sw, int sh, int tx, int ty, int tw, int th, TerrainSpans_ter * in) { unsigned y; register unsigned x; ggi_color col; /* gfxDrawSky(sx, sy, sw, sh); */ col.r = 0x99 << 8; col.g = 0x66 << 8; col.b = 0x1a << 8; col.a = 0xFF << 8; ggiSetGCForeground(gfx_vis, ggiMapColor(gfx_vis, &col)); for(y = 0; y < sh; y++) { for(x = 0; x < sw; x++) { if(terCheckPos(in, tx + (x * tw) / sw, ty + th - (y * th) / sh)) ggiDrawPixel(gfx_vis, sx + x, sy + y); } } } #endif ggi_visual_t gfxScaleMemvis(ggi_visual_t input, int newx, int newy) { ggi_mode sizei, sizeo; ggi_visual_t ret; unsigned y; register unsigned x; double ptx, pty, stx, sty; ggi_pixel c; ggiGetMode(input, &sizei); ret = ggiOpen("memory"); sizeo = sizei; sizeo.visible.x = newx; sizeo.visible.y = newy; sizeo.virt.x = newx; sizeo.virt.y = newy; ggiSetMode(ret, &sizeo); stx = ((double) sizei.visible.x) / ((double) newx); sty = ((double) sizei.visible.y) / ((double) newy); for(pty = 0, y = 0; pty < sizei.visible.y; pty += sty, y++) { for(ptx = 0, x = 0; ptx < sizei.visible.x; ptx += stx, x++) { ggiGetPixel(input, round(ptx), round(pty), &c); ggiPutPixel(ret, x, y, c); } } return ret; } ggi_visual_t gfxFlipMemvis(ggi_visual_t input) { ggi_mode mode; ggi_visual_t ret; ggi_pixel c; unsigned y; register unsigned x; ggiGetMode(input, &mode); ret = ggiOpen("memory"); ggiSetMode(ret, &mode); for(y = 0; y < mode.visible.y; y++) { for(x = 0; x < mode.visible.x; x++) { ggiGetPixel(input, x, y, &c); ggiPutPixel(ret, mode.visible.x - x, y, c); } } return ret; } void gfxDrawTank(Player_pl * pl) { double ex, ey; ggi_color col; gfxEraseTank(pl, 1); gfxBlitTankSprite(gfxTerrainToScreenXCoord(pl->x), gfxTerrainToScreenYCoord(pl->y), pl->fire_angle < 90 ? gfx_tankpic_right : gfx_tankpic_left, gfx_tankcolor[pl->tankcolor]); ex = pl->x + pl->barreloff_x + pl_barrelen * cos((pl->fire_angle / 180.0) * M_PI); ey = pl->y + pl->barreloff_y + pl_barrelen * sin((pl->fire_angle / 180.0) * M_PI); ggiSetGCForeground(gfx_vis, gfx_tankcolor[pl->tankcolor]); /* draw the barrel */ ggiDrawLine(gfx_vis, gfxTerrainToScreenXCoord(pl->x + pl->barreloff_x), gfxTerrainToScreenYCoord(pl->y + pl->barreloff_y), gfxTerrainToScreenXCoord(ex), gfxTerrainToScreenYCoord(ey)); if(gfx_armorBar == 1) { /* change the color based on the armor percentage */ if(pl->armor > 20) { /* green is good */ col.r = 0x0000; col.g = 0xffff; col.b = 0x0000; col.a = 0xFF << 8; } else { /* red is bad */ col.r = 0xffff; col.g = 0x0000; col.b = 0x0000; col.a = 0xFF << 8; } ggiSetGCForeground(gfx_vis, ggiMapColor(gfx_vis, &col)); /* draw the armor bar */ ggiDrawHLine(gfx_vis, gfxTerrainToScreenXCoord(pl->x - pl_tankwidth / 2), gfxTerrainToScreenYCoord(pl->y + pl_tankheight + pl_barrelen + 5), gfxScaleTerrainToScreenXDimen((pl_tankwidth * pl->armor) / gm_def_armor)); } } void gfxEraseTank(Player_pl * pl, int n) { int sx, sy, sw, sh, tx, ty; if(n) { tx = pl->ox; ty = pl->oy; } else { tx = pl->x; ty = pl->y; } if(pl_tankwidth < 2 * (abs(pl->barreloff_x) + pl_barrelen)) sw = 2 * gfxScaleTerrainToScreenXDimen(abs(pl->barreloff_x) + pl_barrelen) + 2; else sw = gfxScaleTerrainToScreenXDimen(pl_tankwidth); if((pl_tankheight + pl_barrelen) < (pl->barreloff_y + pl_barrelen)) sh = gfxScaleTerrainToScreenYDimen(pl->barreloff_y + pl_barrelen) + 2; else sh = gfxScaleTerrainToScreenYDimen(pl_tankheight + pl_barrelen) + 2; sx = gfxTerrainToScreenXCoord(tx) - sw / 2; sy = gfxTerrainToScreenYCoord(ty) - sh; if(sy < 0) { /* reduce sh (since sy is negative) */ sh += sy; assert(sh >= 0); /* set sy to 0 (top of the screen) */ sy = 0; } skyDraw(sx, sy, sw, sh); gfxDrawTerrain(sx, sy, sw, sh, tx - (pl_tankwidth < 2 * (abs(pl->barreloff_x) + pl_barrelen) ? 2 * (abs(pl->barreloff_x) + pl_barrelen) : pl_tankwidth) / 2 - (ter_sizex / gfx_xsize), pl->y, (pl_tankwidth < (pl->barreloff_x + pl_barrelen) ? (pl->barreloff_x + pl_barrelen) : pl_tankwidth) + (2 * ter_sizex / gfx_xsize), (pl_tankheight < (pl->barreloff_y + pl_barrelen) ? (pl->barreloff_y + pl_barrelen) : pl_tankheight), ter_data); gfxDrawWalls(sx, sy, sw, sh); } void gfxBlitTankSprite(int sx, int sy, ggi_visual_t sp, ggi_pixel color) { unsigned y; register unsigned x; ggi_mode size; ggi_pixel rc; ggiGetMode(sp, &size); sy -= size.visible.y; sx -= size.visible.x / 2; for(y = 0; y < size.visible.y; y++) { for(x = 0; x < size.visible.x; x++) { ggiGetPixel(sp, x, y, &rc); if(rc != 0) { if(rc == 1) ggiPutPixel(gfx_vis, sx + x, sy + y, 1); else ggiPutPixel(gfx_vis, sx + x, sy + y, color); } } } } ggi_visual_t gfxXPMtoMemvis(char **xpm) { int xsize, ysize, colors, pixsize, i, color, q; ggi_color ctable[256]; char hexcolor[64], n; ggi_visual_t ret; int none = -1; ggi_pixel p; sscanf(xpm[0], "%i %i %i %i", &xsize, &ysize, &colors, &pixsize); for(i = 1; i <= colors; i++) { sscanf(&xpm[i][1], " %c %s", &n, hexcolor); if(strcmp(hexcolor, "None") == 0) { ctable[(int) xpm[i][0]].r = 0; ctable[(int) xpm[i][0]].g = 0; ctable[(int) xpm[i][0]].b = 0; ctable[(int) xpm[i][0]].a = 0; none = xpm[i][0]; } else { sscanf(&hexcolor[1], "%X", &color); ctable[(int) xpm[i][0]].r = (color >> 16) << 8; ctable[(int) xpm[i][0]].g = ((color >> 8) & 0xFF) << 8; ctable[(int) xpm[i][0]].b = (color & 0xFF) << 8; ctable[(int) xpm[i][0]].a = 0xFFFF; } } ret = ggiOpen("memory"); /* ggiGetMode(gfx_vis, &gm); */ if(gfx_visualdepth == GT_8BIT) ggiSetGraphMode(ret, xsize, ysize, xsize, ysize, GT_TRUECOLOR); else ggiSetGraphMode(ret, xsize, ysize, xsize, ysize, gfx_visualdepth); for(q = 0; q < ysize; q++) { for(i = 0; i < xsize; i++) { if(none > -1) { if(xpm[q + colors + 1][i] == none) ggiPutPixel(ret, i, q, 0); else { if((p = ggiMapColor(ret, &ctable[(int) xpm[q + colors + 1][i]])) == 0) p = 1; ggiPutPixel(ret, i, q, p); } } else ggiPutPixel(ret, i, q, ggiMapColor(ret, &ctable[(int) xpm[q + colors + 1][i]])); ggiGetPixel(ret, i, q, &p); } } return ret; } /* Circle drawing algorithm from _Computer Graphics: Principals and Practice_ 2nd ed in C, section 3.3.2 */ void gfxCirclePoints(int xo, int yo, int x, int y) { ggiDrawPixel(gfx_vis, xo + x, yo + y); ggiDrawPixel(gfx_vis, xo + y, yo + x); ggiDrawPixel(gfx_vis, xo + y, yo - x); ggiDrawPixel(gfx_vis, xo + x, yo - y); ggiDrawPixel(gfx_vis, xo - x, yo - y); ggiDrawPixel(gfx_vis, xo - y, yo - x); ggiDrawPixel(gfx_vis, xo - y, yo + x); ggiDrawPixel(gfx_vis, xo - x, yo + y); } void gfxDrawCircle(int xo, int yo, int r) { int x = 0; int y = r; int d = 1 - r; int deltaE = 3; int deltaSE = -2 * r + 5; gfxCirclePoints(xo, yo, x, y); while(y > x) { if(d < 0) { d += deltaE; deltaE += 2; deltaSE += 2; } else { d += deltaSE; deltaE += 2; deltaSE += 4; y--; } x++; gfxCirclePoints(xo, yo, x, y); } } void gfxThickCirclePoints(int xo, int yo, int x, int y) { ggiDrawVLine(gfx_vis, xo + x, yo + y - 1, 3); ggiDrawHLine(gfx_vis, xo + y - 1, yo + x, 3); ggiDrawHLine(gfx_vis, xo + y - 1, yo - x, 3); ggiDrawVLine(gfx_vis, xo + x, yo - y - 1, 3); ggiDrawVLine(gfx_vis, xo - x, yo - y - 1, 3); ggiDrawHLine(gfx_vis, xo - y - 1, yo - x, 3); ggiDrawHLine(gfx_vis, xo - y - 1, yo + x, 3); ggiDrawVLine(gfx_vis, xo - x, yo + y - 1, 3); } void gfxDrawThickCircle(int xo, int yo, int r) { int x = 0; int y = r; int d = 1 - r; int deltaE = 3; int deltaSE = -2 * r + 5; gfxThickCirclePoints(xo, yo, x, y); while(y > x) { if(d < 0) { d += deltaE; deltaE += 2; deltaSE += 2; } else { d += deltaSE; deltaE += 2; deltaSE += 4; y--; } x++; gfxThickCirclePoints(xo, yo, x, y); } } /* Ellipse drawing algorithm from _Computer Graphics: Principals and Practice_ 2nd ed in C, section 3.4 This is a straight C conversion of the psudocode in the book. Feel free to optomize it into a totally integer algorithm, it should be rather easy. */ void gfxDrawEllipsePoints(int xo, int yo, int x, int y) { ggiDrawPixel(gfx_vis, xo + x, yo + y); ggiDrawPixel(gfx_vis, xo - x, yo + y); ggiDrawPixel(gfx_vis, xo + x, yo - y); ggiDrawPixel(gfx_vis, xo - x, yo - y); } void gfxDrawEllipse(int xo, int yo, int a, int b) { int x = 0; int y = b; int a2 = a * a; int b2 = b * b; double d1 = b2 - (a2 * b) + (a2 / 4); double d2; gfxDrawEllipsePoints(xo, yo, x, y); while(a2 * (y - 0.5) > b2 * (x + 1.0)) { if(d1 < 0) d1 += b2 * (2.0 * x + 3.0); else { d1 += b2 * (2.0 * x + 3.0) + a2 * (-2.0 * y + 2.0); y--; } x++; gfxDrawEllipsePoints(xo, yo, x, y); } d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2; while(y > 0) { if(d2 < 0) { d2 += b2 * (2.0 * x + 2.0) + a2 * (-2.0 * y + 3.0); x++; } else d2 += a2 * (-2.0 * y + 3.0); y--; gfxDrawEllipsePoints(xo, yo, x, y); } } void gfxDrawThickEllipsePoints(int xo, int yo, int x, int y) { ggiDrawBox(gfx_vis, xo + x - 1, yo + y - 1, 3, 3); ggiDrawBox(gfx_vis, xo - x - 1, yo + y - 1, 3, 3); ggiDrawBox(gfx_vis, xo + x - 1, yo - y - 1, 3, 3); ggiDrawBox(gfx_vis, xo - x - 1, yo - y - 1, 3, 3); } void gfxDrawThickEllipse(int xo, int yo, int a, int b) { int x = 0; int y = b; int a2 = a * a; int b2 = b * b; double d1 = b2 - (a2 * b) + (a2 / 4); double d2; gfxDrawThickEllipsePoints(xo, yo, x, y); while(a2 * (y - 0.5) > b2 * (x + 1.0)) { if(d1 < 0) d1 += b2 * (2.0 * x + 3.0); else { d1 += b2 * (2.0 * x + 3.0) + a2 * (-2.0 * y + 2.0); y--; } x++; gfxDrawThickEllipsePoints(xo, yo, x, y); } d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2; while(y > 0) { if(d2 < 0) { d2 += b2 * (2.0 * x + 2.0) + a2 * (-2.0 * y + 3.0); x++; } else d2 += a2 * (-2.0 * y + 3.0); y--; gfxDrawThickEllipsePoints(xo, yo, x, y); } } /* Uhg. Remind me again why this takes terrain coordinates? Bleh... Also the coordinates are (sort of) weird. Because y is flipped, screenwise the you are specifying the lower-left hand corner and going up, not the upper-left and going down. Watch out. If I get annoyed enough I may change this, then again maybe not. It's sort of a weird function, because in some places we have the terrain coords lying around and it's easy to use those, in others we only have screen coords and we have to convert, then convert back. Yuck. I'm not sure yet of the best solution, so I'm just going to leave it as it is (it works fine otherwise) with this comment here to warn the unwary... */ void gfxDrawArea(int tx, int ty, int w, int h) { int sx, sy; int sw, sh; Player_pl *pl; if(h < 0 || w < 0 || tx > ter_sizex || ty > ter_sizey) return; if(tx < 0) { w -= tx; tx = 0; } if(tx + w > ter_sizex) { w = ter_sizex - tx; } if(ty < 0) { h -= ty; ty = 0; } if(ty + h > ter_sizey) { h = ter_sizey - ty; } sx = gfxTerrainToScreenXCoord(tx); sy = gfxTerrainToScreenYCoord(ty); sw = gfxScaleTerrainToScreenXDimen(w) + 1; sh = gfxScaleTerrainToScreenYDimen(h) + 1; /* don't refresh beyond the top of the screen */ if(sy - sh < 0) { /* this makes the sy-sh's below equal zero */ sh += sy - sh; sy = sh; } skyDraw(sx, sy - sh, sw, sh + 1); gfxDrawTerrain(sx, sy - sh, sw, sh + 1, tx, ty, w, h, ter_data); gfxDrawWalls(sx, sy - sh, sw, sh + 1); for(pl = pl_begin; pl; pl = pl->next) { if(pl->ready == READY && (pl->x + (pl_tankwidth / 2)) > tx && (pl->x - (pl_tankwidth / 2)) < (tx + w) && (pl->y + pl_tankheight + pl_barrelen + 5) > ty && (pl->y < (ty + h))) { gfxDrawTank(pl); } } } void gfxDrawWalls(int sx, int sy, int sw, int sh) { ggiSetGCForeground(gfx_vis, gfx_wallcolor[bal_wall]); if(sx <= 1) ggiDrawVLine(gfx_vis, 0, sy, sh); if((sx + sw) >= (gfx_xmax - 1)) ggiDrawVLine(gfx_vis, gfx_xmax - 1, sy, sh); if(sy <= 1) ggiDrawHLine(gfx_vis, sx, 0, sw); if((sy + sh) >= (gfx_ymax - 1)) ggiDrawHLine(gfx_vis, sx, gfx_ymax - 1, sw); } void gfxDrawPlayerList() { Player_pl *pcur; int y = 0; int x = gfx_xmax - (gfx_xmax * 3) / 10 + 15; ggi_pixel color; ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, x, y, (gfx_xmax * 3) / 10 - 15, gfx_ymax); for(pcur = pl_begin; pcur && y < gfx_ymax; pcur = pcur->next, y += 20) { if(pcur->ready != OBSERVER) color = (pcur->ready == READY) ? gfx_green : gfx_red; else color = gfx_white; ggiSetGCForeground(gfx_vis, color); txtPuts(x, y, "\020"); /* XXX: x + magic number !? */ ggiSetGCForeground(gfx_vis, gfx_tankcolor[pcur->tankcolor]); txtPuts(x+12, y, pcur->name); } gfxUpdate(); } /* The purpose of having the (currently somewhat redundant) Coord() and Dimen() functions (beside the fact that the Y coordinate is flipped from screen to terrain coordinate) is so that in the future, if it becomes desirable that KOTH use less than the current full screen, it will be a simple matter to add in a window offset to the Coord() functions, and possibly other transformations if need be. Which, as you see, we have now done */ int gfxTerrainToScreenXCoord(int tx) { return (gfxScaleTerrainToScreenXDimen(tx) + gfx_xoff); } int gfxTerrainToScreenYCoord(int ty) { return ((gfx_yoff + gfx_ysize) - gfxScaleTerrainToScreenYDimen(ty)); } int gfxScreenToTerrainXCoord(int sx) { return gfxScaleScreenToTerrainXDimen(sx - gfx_xoff); } int gfxScreenToTerrainYCoord(int sy) { return ter_sizey - gfxScaleScreenToTerrainYDimen(sy - gfx_yoff); } int gfxScaleTerrainToScreenXDimen(int tx) { return rint((((double) tx) * ((double) gfx_xsize)) / ((double) ter_sizex)); } int gfxScaleTerrainToScreenYDimen(int ty) { return rint((((double) ty) * ((double) gfx_ysize)) / ((double) ter_sizey)); } int gfxScaleScreenToTerrainXDimen(int sx) { return rint((((double) sx) * ((double) ter_sizex)) / ((double) gfx_xsize)); } int gfxScaleScreenToTerrainYDimen(int sy) { return rint((((double) sy) * ((double) ter_sizey)) / ((double) gfx_ysize)); }