/* * Copyright (C) 2003 Tim Martin * * 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 */ #include #include #include #include "screen.h" /* xxx hide this structure */ struct screen_s { int x; int y; int sizex; int sizey; /* current displayable size of tiles (based upon current zoom) */ int tilex; int tiley; int tileheight; int dirview; /* view of screen * 0 = 0,0 at top of screen * 1 = 0,max at top */ point_t *screenmap; /*square_t *viewarea; int viewsizex; int viewsizey;*/ map_t *map; int mapsizex; int mapsizey; }; #define VIEWSPOT(screen, x,y) screen->screenmap[y * screen->sizex + x] int screen_init(screen_t **ret) { screen_t *screen; screen = calloc(1, sizeof(screen_t)); if (!screen) return -1; screen->tilex = 64; screen->tiley = 32; screen->tileheight = 8; *ret = screen; return 0; } void screen_viewarea_clear(screen_t *screen) { if (!screen->screenmap) { printf("viewarea is null!\n"); return; } memset(screen->screenmap, -1, sizeof(point_t)*screen->sizex*screen->sizey); } void screen_viewarea_set(screen_t *screen, int x, int y, square_t *square) { int i; int j; /* iterate over polygon setting points */ for (i = square->parts.topy; i < square->parts.boty; i++) { int startx; int endx; if (i < square->parts.lefty) { float dy1 = -1.0 * ((float)(square->parts.topx - square->parts.leftx))/ ((float)(square->parts.topy - square->parts.lefty)); float dy2 = 1.0 * ((float)(square->parts.topx - square->parts.rightx))/ ((float)(square->parts.topy - square->parts.righty)); startx = square->parts.leftx + (square->parts.lefty - i) * dy1; endx = square->parts.rightx - (square->parts.righty - i) * dy2; } else { float dy1 = 1.0 * ((float)(square->parts.botx - square->parts.leftx))/ ((float)(square->parts.boty - square->parts.lefty)); float dy2 = -1.0 * ((float)(square->parts.botx - square->parts.rightx))/ ((float)(square->parts.boty - square->parts.righty)); startx = square->parts.leftx + (square->parts.lefty - i) * dy1; endx = square->parts.rightx - (square->parts.righty - i) * dy2; } for (j = startx; j < endx; j++) { if ((i >= 0) && (j >= 0) && (i < screen->sizey) && (j < screen->sizex)) { int sx = j; int sy = i; VIEWSPOT(screen,sx,sy).x = x; VIEWSPOT(screen,sx,sy).y = y; } } } /* xxx */ /*memcpy(&(VIEWSPOT(screen,x,y)), square, sizeof(square_t));*/ } void screen_setmap(screen_t *screen, map_t *map) { screen->map = map; screen->mapsizex = map_get_sizex(map); screen->mapsizey = map_get_sizey(map); } int screen_setsize(screen_t *screen, int sizex, int sizey) { if (screen->screenmap) { free(screen->screenmap); } screen->screenmap = malloc(sizeof(point_t) * sizex * sizey); screen->sizex = sizex; screen->sizey = sizey; if (!screen->screenmap) return -1; screen_viewarea_clear(screen); return 0; } #if 0 #define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) static int point_iswithin_polygon(int x, int y, square_t *sq) { int counter = 0; int i; double xinters; point_t *p1; point_t *p2; p1 = &sq->points[0]; for (i = 1; i<= 4; i++ ) { p2 = &sq->points[i % 4]; if (y > MIN(p1->y,p2->y)) { if (y <= MAX(p1->y,p2->y)) { if (x <= MAX(p1->x,p2->x)) { if (p1->y != p2->y) { xinters = (y-p1->y)*(p2->x-p1->x)/(p2->y-p1->y)+p1->x; if (p1->x == p2->x || x <= xinters) counter++; } } } } p1 = p2; } if (counter % 2 == 0) return 0; else return 1; } #endif /* 0 */ static void nominal_screen_to_map(screen_t *screen, int sx, int sy, int *mapx, int *mapy) { int sizey = screen->mapsizey - 1; int sizex = screen->mapsizex - 1; int retx = 0; int rety = 0; switch (screen->dirview) { default: case 0: retx = sx/screen->tilex + sy/screen->tiley - sizey/2; rety = - sx/screen->tilex + sy/screen->tiley + sizey/2; break; case 1: retx = - sx/screen->tilex - sy/screen->tiley + sizex/2 + sizey; rety = - sx/screen->tilex + sy/screen->tiley + sizex/2; break; case 2: retx = - sx/screen->tilex - sy/screen->tiley + sizex + sizey/2; rety = sx/screen->tilex - sy/screen->tiley + sizey/2; break; case 3: retx = sx/screen->tilex - sy/screen->tiley + sizex/2; rety = sx/screen->tilex + sy/screen->tiley - sizex/2; break; } if (retx < 0) retx = 0; if (rety < 0) rety = 0; if (retx >= screen->mapsizex) retx = screen->mapsizex-1; if (rety >= screen->mapsizey) rety = screen->mapsizey-1; *mapx = retx; *mapy = rety; } int screen_map_iterate(screen_t *screen, screen_map_iterator_t *func, void *rock) { int mapsizex = map_get_sizex(screen->map); int mapsizey = map_get_sizey(screen->map); int xplusy = mapsizex + mapsizey; int i; int startx; int starty; int stepx1; int stepx2; int stepy1; int stepy2; int xchange; int ychange; int minx = 0; int miny = 0; int maxx = mapsizey; int maxy = mapsizey; int switched; switch (screen->dirview) { default: case 0: /* iterate over all the squares backwards */ startx = 0; starty = 0; stepx1 = 0; stepx2 = 1; stepy1 = 1; stepy2 = 0; xchange = 1; ychange = -1; nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &minx, &miny); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y + 3*screen->sizey/2, &maxx, &maxy); startx = minx; starty = miny; xplusy = (maxx - minx) + (maxy - miny); break; case 1: /* iterate over all the squares backwards */ startx = 0; starty = mapsizey -1; stepx1 = 1; stepx2 = 0; stepy1 = 0; stepy2 = -1; xchange = -1; ychange = -1; nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y + 3*screen->sizey/2, &miny, &maxx); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &maxy, &minx); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &starty, &startx); xplusy = (maxx - minx) + (maxy - miny); /*startx = minx; starty = miny; minx = 0; miny = 0; maxx = mapsizey; maxy = mapsizey;*/ break; case 2: /* iterate over all the squares backwards */ startx = mapsizex - 1; starty = mapsizey - 1; stepx1 = 0; stepx2 = -1; stepy1 = -1; stepy2 = 0; xchange = -1; ychange = 1; nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y + 3*screen->sizey/2, &minx, &miny); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &maxx, &maxy); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &startx, &starty); xplusy = (maxx - minx) + (maxy - miny); break; case 3: /* iterate over all the squares backwards */ startx = mapsizex - 1; starty = 0; stepx1 = 0; stepx2 = -1; stepy1 = 1; stepy2 = 0; xchange = -1; ychange = -1; nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y + 3*screen->sizey/2, &minx, &maxy); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &maxx, &miny); nominal_screen_to_map(screen, screen->x + screen->sizex/2, screen->y - screen->sizey/2, &startx, &starty); xplusy = (maxx - minx) + (maxy - miny); break; } switched = 0; for (i = 0; i < xplusy+1; i++) { int x = startx; int y = starty; if (switched == 0) { startx+=stepx1; starty+=stepy1; if (stepx1 > 0) { if (startx >= screen->mapsizex-1) switched = 1; } else if (stepx1 < 0) { if (startx <= 0) switched = 1; } if (stepy1 > 0) { if (starty >= screen->mapsizey-1) switched = 1; } else if (stepy1 < 0) { if (starty <= 0) switched = 1; } } else { startx+=stepx2; starty+=stepy2; } /*if ((startx > maxx) && (starty > maxy)) break;*/ do { if ((x >= minx) && (x <= maxx) && (y >= miny) && (y <= maxy)) { if (func(x,y,rock)) { return 1; } } x += xchange; y += ychange; } while ((x >= 0) && (y>= 0) && (x < mapsizex) && (y < mapsizey)); } return 0; } #if 0 struct find_mapcoord_s { screen_t *screen; int screenx; int screeny; int found; int mapx; int mapy; }; static int find_mapcoord(int x, int y, void *rock) { struct find_mapcoord_s *fcoord = (struct find_mapcoord_s *)rock; if (screen_viewarea_isset(fcoord->screen, x, y)) { if (point_iswithin_polygon(fcoord->screenx, fcoord->screeny, &VIEWSPOT(fcoord->screen,x,y))) { fcoord->found = 1; fcoord->mapx = x; fcoord->mapy = y; return 1; } } return 0; } #endif /* 0 */ int screen_to_mapcoord(screen_t *screen, int screenx, int screeny, int *mapx, int *mapy) { *mapx = -1; *mapy = -1; if (screenx < 0) { screenx = 0; } if (screeny < 0) { screeny = 0; } if (screenx >= screen->sizex) { screenx = screen->sizex - 1; } if (screeny >= screen->sizey) { screeny = screen->sizey - 1; } if (!screen->screenmap) { printf("viewarea is null!\n"); return -1; } *mapx = VIEWSPOT(screen,screenx,screeny).x; *mapy = VIEWSPOT(screen,screenx,screeny).y; /* * we're off the game board. Let's try to find the closest square */ if (*mapx == -1) { int midx; int midy; mapcoord_to_screen(screen, screen->mapsizex, screen->mapsizey, screen->mapsizex/2, screen->mapsizey/2, &midx, &midy); do { if (screen->x + screenx < midx) { screenx++; if (screenx >= screen->sizex) return -1; } else if (screen->x + screenx > midx) { screenx--; if (screenx < 0) return -1; } if (screen->y + screeny < midy) { screeny++; if (screeny >= screen->sizey) return -1; } else if (screen->y + screeny > midy) { screeny--; if (screeny < 0) return -1; } *mapx = VIEWSPOT(screen,screenx,screeny).x; *mapy = VIEWSPOT(screen,screenx,screeny).y; /* * Prevent infinite looping */ if ((screen->x + screenx == midx) && (screen->y + screeny == midy)) { return 0; } } while (*mapx == -1); } return 0; #if 0 struct find_mapcoord_s fcoord; fcoord.screen = screen; fcoord.screenx = screenx; fcoord.screeny = screeny; fcoord.mapx = -1; fcoord.mapy = -1; fcoord.found = 0; /* iterate over all the squares backwards */ screen_map_iterate(screen, &find_mapcoord, &fcoord); if (fcoord.found) { *mapx = fcoord.mapx; *mapy = fcoord.mapy; return 0; } return -1; #endif /* 0 */ } int screen_to_mapcoord_screen_center(screen_t *screen, int *mapx, int *mapy) { return screen_to_mapcoord(screen, screen->sizex/2, screen->sizey/2, mapx, mapy); } int mapcoord_to_screen(screen_t *screen, int mapsizex, int mapsizey, int mapx, int mapy, int *screenx, int *screeny) { mapsizex--; mapsizey--; switch (screen->dirview) { default: case 0: *screenx = (mapsizey + mapx - mapy) * (screen->tilex/2); *screeny = (mapx + mapy) * (screen->tiley/2); break; case 1: *screenx = (mapsizex + mapsizey - mapx - mapy) * (screen->tilex/2); *screeny = (mapsizey + mapx - mapy) * (screen->tiley/2); break; case 2: *screenx = (mapsizex - mapx + mapy) * (screen->tilex/2); *screeny = (mapsizey + mapsizey - mapx - mapy) * (screen->tiley/2); break; case 3: *screenx = (mapx + mapy) * (screen->tilex/2); *screeny = (mapsizex - mapx + mapy) * (screen->tiley/2); break; } return 0; } /* * zoom the viewable size. positive means zoom in. negative means zoom out * returns -1 if already at zooming limit */ int screen_zoom(screen_t *screen, int zoom) { int mapx; int mapy; /* remember tile that was in the middle of the screen before */ screen_to_mapcoord(screen, screen->sizex/2, screen->sizey/2, &mapx, &mapy); if (zoom > 0) { if (screen->tilex == 128) { return -1; } screen->tilex *= 2; screen->tiley *= 2; screen->tileheight *= 2; } else { if (screen->tilex <= 8) { return -1; } screen->tilex /= 2; screen->tiley /= 2; screen->tileheight /= 2; } /* now re-center the screen */ if (mapx != -1) { int sx; int sy; mapcoord_to_screen(screen, screen->mapsizex, screen->mapsizey, mapx, mapy, &sx, &sy); screen->x = sx - screen->sizex/2; screen->y = sy - screen->sizey/2; } return 0; } int screen_getzoom(screen_t *screen) { if (screen->tilex == 128) return 0; if (screen->tilex == 64) return 1; if (screen->tilex == 32) return 2; if (screen->tilex == 16) return 3; if (screen->tilex == 8) return 4; if (screen->tilex == 4) return 5; if (screen->tilex == 2) return 6; return -1; } void screen_keep_in_bounds(screen_t *screen, int mapsizex, int mapsizey, int *sx, int *sy) { if (*sx < 0) *sx = 0; if (*sx > screen->tilex/2 * (mapsizex + mapsizey)) *sx = screen->tilex/2 * (mapsizex + mapsizey); if (*sy < 0) *sy = 0; if (*sy > screen->tiley/2 * (mapsizex + mapsizey)) *sy = screen->tiley/2 * (mapsizex + mapsizey); } int screen_is_square_viewable(screen_t *screen, int mapx, int mapy, int *screenx, int *screeny) { /* translate map coordinates to screen coordinates */ mapcoord_to_screen(screen, screen->mapsizex, screen->mapsizey, mapx, mapy, screenx, screeny); if ((*screenx + screen->tilex >= screen->x) && (*screenx <= screen->x + screen->sizex) && (*screeny + screen->tiley >= screen->y) && (*screeny - 8 * screen->tileheight <= screen->y + screen->sizey)) { (*screenx)-=screen->x; (*screeny)-=screen->y; return 1; } else { return 0; } } void screen_set_position(screen_t *screen, int x, int y) { screen->x = x; screen->y = y; } void screen_rotate(screen_t *screen, int offset) { screen->dirview+=offset; screen->dirview = (screen->dirview+4)%4; } int screen_getdirview(screen_t *screen) { return screen->dirview; } void screen_getdirviewadds(screen_t *screen, int *x, int *y) { switch (screen->dirview) { case 0: *x = 1; *y = 1; break; case 1: *x = -1; *y = 1; break; case 2: *x = -1; *y = -1; case 3: *x = 1; *y = -1; break; } } void screen_tilesizes(screen_t *screen, int *sx, int *sy, int *th) { *sx = screen->tilex; *sy = screen->tiley; *th = screen->tileheight; } int screen_tileheight(screen_t *screen) { return screen->tileheight; }