/* * 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 #include #include "tiles.h" #include "map.h" #include "map_item.h" #define NONE 0 #define ABOVE 1 #define RIGHT 2 #define LEFT 4 #define BELOW 8 #define ALL_DIRS (ABOVE | RIGHT | LEFT | BELOW) #define NUMSLOPES 256 typedef struct tilelist_s { int cost; func_t *upkeep; func_t *revenue; int live; labor_t labor; int rent; int sizex; int sizey; int demolish_cost; func_t *landvalue; int maxstudents; int maxpatrons; int range; int patronperc; int poweruse; int wateruse; int capacity; func_t *production; tile_t *dir[ALL_DIRS+1]; tile_t *slopes[NUMSLOPES]; } tilelist_t; struct tiles_s { char prefix[MAXPATHLEN+1]; tilelist_t list[100]; int numtiles; tiles_loadimage_func_t *loadfunc; void *rock; }; struct labor_group labor_table[] = { {LABOR_NOEDUCATION, "No education", 'N', 0, 18000}, {LABOR_UNSKILLED, "Unskilled" , 'U', 8, 25000}, {LABOR_BLUECOLLAR, "Blue collar" , 'B', 12, 30000}, {LABOR_PROFESSIONAL,"Professional", 'P', 16, 50000}, {LABOR_EXPERT, "Expert" , 'E', 20, 75000} }; static int get_slope(int tl, int tr, int bl, int br) { int ret = ((tl << 6) + (tr << 4) + (bl << 2) + (br << 0)); return ret; } #if 0 static int scale_and_convert(GdkPixbuf *pixbuf, int x, int y, GdkPixmap **pixmap, GdkBitmap **bitmap, int *outsizex, int *outsizey) { GdkPixbuf *scaledpixbuf; float curx; float cury; /* figure out destination y size */ curx = gdk_pixbuf_get_width(pixbuf); cury = gdk_pixbuf_get_height(pixbuf); y = (int)(((float)x)/curx*cury); if (y <= 0) y = 1; scaledpixbuf = gdk_pixbuf_scale_simple(pixbuf, x, y, GDK_INTERP_NEAREST); if (!scaledpixbuf) { printf("error scaling pixbuf!\n"); return -1; } gdk_pixbuf_render_pixmap_and_mask (scaledpixbuf, pixmap, bitmap, 127); if (!*pixmap) { printf("Error making pixmap\n"); return -1; } gdk_window_get_size((GdkWindow *)*pixmap, outsizex, outsizey); return 0; } #endif /* 0 */ /* * Try to find a cached copy of this image */ static int find_cached(tiles_t *tiles, char *filename, tile_t **tile) { int i; for (i = 0; i < tiles->numtiles;i++) { int j; tilelist_t *tilel = &tiles->list[i]; tile_t *t; /* look at dirs */ for (j = NONE; j <= ALL_DIRS; j++) { t = tilel->dir[j]; if ((t) && (t->filename) && (strcasecmp(filename, t->filename) == 0)) { *tile = t; return 0; } } /* look at slopes */ for (j = 0; j < NUMSLOPES; j++) { t = tilel->slopes[j]; if ((t) && (t->filename) && (strcasecmp(filename, t->filename) == 0)) { *tile = t; return 0; } } } return -1; } static int load_color(tiles_t *tiles, tile_t **tile, int r, int g, int b) { tile_t *ret; ret = calloc(1, sizeof(tile_t)); if (!ret) return -1; ret->color_r = r; ret->color_g = g; ret->color_b = b; *tile = ret; return 0; } static int slope_image_height(int slope) { int tileheight = 16; int h[4]; int i; int min = 999; int max = -999; h[0] = 0 - ((slope >> 6) & 3) * tileheight; h[1] = 32 - ((slope >> 4) & 3) * tileheight; h[2] = 32 - ((slope >> 2) & 3) * tileheight; h[3] = 64 - ((slope >> 0) & 3) * tileheight; for (i = 0; i < 4; i++) { if (h[i] < min) { min = h[i]; } if (h[i] > max) { max = h[i]; } } return abs(max - min); } static int mypow(int x, int y) { int i; int ret; if (y == 0) return 1; ret = x; for (i = 1; i < y; i++) { ret *= x; } return ret; } static int actual_load_image(tiles_t *tiles, tile_t *tile, int rotation, int zoom) { char fname[1024]; char shortfnamebuf[1024]; char *shortfname; int width; int height; int i; int hasdup = 1; /* advance to right image */ snprintf(shortfnamebuf, sizeof(shortfnamebuf)-1,"%s", tile->filename); shortfname = shortfnamebuf; for (i = 0; i <= rotation; i++) { char *comma = strchr(shortfname, ','); if (comma) { hasdup = 0; *comma = '\0'; if (i < rotation) { shortfname = comma + 1; } } } /* look to see if we've already loaded it */ if (hasdup) { for (i = 0; i < 4; i++) { if (tile->image[i][zoom] != NULL) { tile->image[rotation][zoom] = tile->image[i][zoom]; tile->extray[rotation][zoom] = tile->extray[i][zoom]; return 0; } } } width = tile->width / mypow(2, zoom); height = tile->height / mypow(2, zoom); snprintf(fname, sizeof(fname)-1,"%s/%s",tiles->prefix, shortfname); tile->image[rotation][zoom] = tiles->loadfunc(fname, width, height, &tile->extray[rotation][zoom], tiles->rock); if (!tile->image[rotation][zoom]) printf("Error loading image: %s\n", fname); return 0; } static int load_image(tiles_t *tiles, tile_t **tile, char *filename, int ispattern, int sizex, int sizey, int slope) { tile_t *ret; int width; int height; int i; int j; int issame = 0; /* Look to see if we've already loaded this filename */ if (find_cached(tiles, filename, tile) == 0) { return 0; } ret = malloc(sizeof(tile_t)); if (!ret) return -1; memset(ret, 0, sizeof(tile_t)); ret->filename = strdup(filename); ret->ispattern = ispattern; ret->color_r = -1; ret->color_g = -1; ret->color_b = -1; ret->width = 64*sizex + 64*sizey; if (slope) { ret->height = slope_image_height(slope); } else { ret->height = (32*sizex + 32*sizey); } #if 0 for (j = 0; j < 4; j++) { for (i = 0; i < 7; i++) { actual_load_image(tiles, ret, j, i); } } #endif /* 0 */ *tile = ret; return 0; } static int parse_slope(char *in) { if (strlen(in) != 4) { printf("slope should be 4 long\n"); return -1; } return get_slope(in[0] - '0', in[1] - '0', in[2] - '0', in[3] - '0'); } static int parse_neighbor(char *str) { if (strcasecmp(str,"NONE") == 0) return NONE; if (strcasecmp(str,"ABOVE") == 0) return ABOVE; if (strcasecmp(str,"BELOW") == 0) return BELOW; if (strcasecmp(str,"RIGHT") == 0) return RIGHT; if (strcasecmp(str,"LEFT") == 0) return LEFT; if (strcasecmp(str,"ALL_DIRS") == 0) return ALL_DIRS; return -1; } static int parse_neighborstring(char *str) { int ret = 0; while (str) { char *next; next = strchr(str,'|'); if (next) { *next = '\0'; next++; } ret |= parse_neighbor(str); str = next; } return ret; } static int parsehex(char *str) { int d1 = 0; int d2 = 0; if (isdigit((int)str[0])) { d1 = str[0] - '0'; } else { d1 = str[0] - 'A'+10; } if (isdigit((int)str[1])) { d2 = str[1] - '0'; } else { d2 = str[1] - 'A'+10; } return d1*16+d2; } static int add_tile(tiles_t *tiles, int objnumber, char *type, char *name) { char filename[1024]; int sizex = tiles->list[objnumber].sizex; int sizey = tiles->list[objnumber].sizey; int color_r = -1; int color_g = -1; int color_b = -1; int ispattern = 0; if (name == NULL) { printf("NULL filaname for %d\n",objnumber); return -1; } if (*name == '#') { color_r = parsehex(name+1); color_g = parsehex(name+3); color_b = parsehex(name+5); } else if (*name == '%') { ispattern = 1; snprintf(filename, sizeof(filename)-1,"%s",name+1); } else { snprintf(filename, sizeof(filename)-1,"%s",name); } if (strncasecmp(type,"slope",5) == 0) { if (strcasecmp(type+5,"all") == 0) { int i; for (i = 0; i < NUMSLOPES; i++ ) { if (color_r > -1) { load_color(tiles, &tiles->list[objnumber].slopes[i], color_r, color_g, color_b); } else { load_image(tiles, &tiles->list[objnumber].slopes[i], filename, ispattern, sizex, sizey, i); } } return 0; /* xxx */ } else { int slope = parse_slope(type+5); if (color_r > -1) { return load_color(tiles, &tiles->list[objnumber].slopes[slope], color_r, color_g, color_b); } else { return load_image(tiles, &tiles->list[objnumber].slopes[slope], filename, ispattern, sizex, sizey, slope); } } } else { int dirs = parse_neighborstring(type); if (color_r > -1) { return load_color(tiles, &tiles->list[objnumber].dir[dirs], color_r, color_g, color_b); } else { return load_image(tiles, &tiles->list[objnumber].dir[dirs], filename, ispattern, sizex, sizey, 0); } } } /* * Make into a args structure */ static int make_args(char *str, char **argv, int maxargs, int *argc) { int numargs = 0; while (str) { char *end; while ((*str) && (isspace((int)*str))) str++; if (*str == '\0') break; end = str; while ((*end) && (!isspace((int)*end))) end++; if (*end) { *end = '\0'; end++; } argv[numargs++] = str; str = end; } *argc = numargs; argv[numargs] = NULL; return 0; } static int parseline(char *line, char **name, char **val) { char *start = line; char *colon; while ((*start) && (isspace((int)*start))) start++; if (*start == '\0') { return 0; } colon = strchr(start, ':'); if (!colon) { printf("no colon found\n"); return -1; } *colon = '\0'; colon++; while ((*colon) && (isspace((int)*colon))) colon++; if (*colon == '\0') { printf("no value found\n"); return -1; } *name = start; *val = colon; return 0; } static int parse_labor(char **args, int argc, labor_t *labor) { int i; for (i = 0; i < argc; i++) { int value; char *p; int j; value = strtoul(args[i], &p, 10); if ((value < 0) || (!p) || (p[1] != '\0')) { printf("Invalid labor parameter %s\n", args[i]); continue; } for (j = 0; j < LABOR_NUMGROUPS; j++) { if (labor_table[j].id == *p) { enum labor_skill skill = labor_table[j].skill; labor->workers[skill] = value; break; } } if (j == LABOR_NUMGROUPS) { printf("Couldn't find labor type '%c' in %s\n", *p, args[i]); } } return 0; } static int readspecfile(tiles_t *tiles, char *specfile) { FILE *f; char line[1024]; int objnum = 0; char *p; f = fopen(specfile,"r"); if (!f) { return -1; } strcpy(tiles->prefix, specfile); p = strrchr(tiles->prefix, '/'); if (p) { *(p+1) = '\0'; } while (fgets(line, sizeof(line)-1, f)) { char *name = NULL; char *val = NULL; char *argv[10]; int argc; if (parseline(line, &name, &val)) { printf("Error parsing line: %s\n",line); continue; } if (name == NULL) continue; /* comments */ if (*name == '#') continue; if (make_args(val, argv, 10, &argc)) { printf("Error making args: %s\n",val); continue; } if (strcasecmp(name,"OBJECT") == 0) { if (argc != 1) { printf("OBJECT must have 1 arguments\n"); continue; } objnum = map_item_registerobj(argv[0]); if (objnum == MAPOBJ_INVALID) { printf("Tile [%s] not supported\n", argv[0]); } if (objnum + 1 > tiles->numtiles) { tiles->numtiles = objnum + 1; } memset(&tiles->list[objnum], 0, sizeof(tilelist_t)); tiles->list[objnum].sizex = 1; tiles->list[objnum].sizey = 1; } else if (strcasecmp(name,"TYPE") == 0) { map_item_settype(objnum, argv[0]); } else if (strcasecmp(name,"TILE") == 0) { add_tile(tiles, objnum, argv[0], argv[1]); } else if (strcasecmp(name,"COST") == 0) { tiles->list[objnum].cost = atoi(argv[0]); } else if (strcasecmp(name,"UPKEEP") == 0) { tiles->list[objnum].upkeep = function_parse(argv, argc, NULL); } else if (strcasecmp(name,"REVENUE") == 0) { tiles->list[objnum].revenue = function_parse(argv, argc, NULL); } else if (strcasecmp(name,"RENT") == 0) { tiles->list[objnum].rent = atoi(argv[0]); } else if (strcasecmp(name,"LIVE") == 0) { tiles->list[objnum].live = atoi(argv[0]); } else if (strcasecmp(name,"LABOR") == 0) { parse_labor(argv, argc, &tiles->list[objnum].labor); } else if (strcasecmp(name,"LANDVALUE") == 0) { tiles->list[objnum].landvalue = function_parse(argv, argc, NULL); } else if (strcasecmp(name,"DEMOLISHCOST") == 0) { tiles->list[objnum].demolish_cost = atoi(argv[0]); } else if (strcasecmp(name,"STUDENTS") == 0) { tiles->list[objnum].maxstudents = atoi(argv[0]); } else if (strcasecmp(name,"MAXPATRONS") == 0) { tiles->list[objnum].maxpatrons = atoi(argv[0]); } else if (strcasecmp(name,"PRODUCTION") == 0) { tiles->list[objnum].production = function_parse(argv, argc, NULL); } else if (strcasecmp(name,"RANGE") == 0) { tiles->list[objnum].range = atoi(argv[0]); } else if (strcasecmp(name,"PATRONPERC") == 0) { tiles->list[objnum].patronperc = atoi(argv[0]); } else if (strcasecmp(name,"POWERUSE") == 0) { tiles->list[objnum].poweruse = atoi(argv[0]); } else if (strcasecmp(name,"WATERUSE") == 0) { tiles->list[objnum].wateruse = atoi(argv[0]); } else if (strcasecmp(name,"CAPACITY") == 0) { tiles->list[objnum].capacity = atoi(argv[0]); } else if (strcasecmp(name,"SIZE") == 0) { if ((isdigit((int)argv[0][0])) && (argv[0][1] == 'x') && (isdigit((int)argv[0][2]))) { tiles->list[objnum].sizex = argv[0][0] - '0'; tiles->list[objnum].sizey = argv[0][2] - '0'; } else { printf("Error parsing size argument\n"); } } else { printf("Option %s being ignored\n", name); } } return 0; } /* * If 'window' is NULL don't load images */ int tiles_init(tiles_t **tiles, char *specfile, tiles_loadimage_func_t *loadfunc, void *rock) { tiles_t *ret; ret = malloc(sizeof(tiles_t)); if (!ret) return -1; memset(ret, 0, sizeof(tiles_t)); ret->loadfunc = loadfunc; ret->rock = rock; *tiles = ret; return readspecfile(ret, specfile); } int tiles_get_flat(tiles_t *tiles, mapobj_t objtype, surrounding_t *surounding, tile_t **tile) { int neighbormask = 0; if ((objtype > tiles->numtiles) || (objtype < 0)) { printf("invalid type\n"); return -1; } if (surounding->dir.above == objtype) neighbormask |= ABOVE; if (surounding->dir.below == objtype) neighbormask |= BELOW; if (surounding->dir.right == objtype) neighbormask |= RIGHT; if (surounding->dir.left == objtype) neighbormask |= LEFT; if (tiles->list[objtype].dir[neighbormask] != NULL) { *tile = tiles->list[objtype].dir[neighbormask]; return 0; } else if (tiles->list[objtype].dir[0] != NULL) { /* if don't have that neighbor tile just use the generic one */ *tile = tiles->list[objtype].dir[0]; return 0; } printf("Can't find tile for objtype = %d\n", objtype); return -1; } static int tiles_heightsquare_to_slope(heightsquare_t *square) { int lowest = square->dir.topleft; /* find the lowest corner */ if (square->dir.topright < lowest) lowest = square->dir.topright; if (square->dir.botleft < lowest) lowest = square->dir.botleft; if (square->dir.botright < lowest) lowest = square->dir.botright; /* return error if any of corners are >= 4 away from this one */ if (square->dir.topleft - lowest >= 4) return -1; if (square->dir.topright - lowest >= 4) return -1; if (square->dir.botleft - lowest >= 4) return -1; if (square->dir.botright - lowest >= 4) return -1; return get_slope(square->dir.topleft - lowest, square->dir.topright - lowest, square->dir.botleft - lowest, square->dir.botright - lowest); } int tiles_get_slope(tiles_t *tiles, mapobj_t objtype, int slope, tile_t **tile) { /* xxx check input values */ if (tiles->list[objtype].slopes[slope] != NULL) { *tile = tiles->list[objtype].slopes[slope]; return 0; } return -1; } int tiles_get(tiles_t *tiles, mapobj_t objtype, surrounding_t *surounding, heightsquare_t *heightsquare, tile_t **tile) { if (!heightmap_islevel(heightsquare)) { int slope = tiles_heightsquare_to_slope(heightsquare); return tiles_get_slope(tiles, objtype, slope, tile); } else { return tiles_get_flat(tiles, objtype, surounding, tile); } } void * tile_image(tiles_t *tiles, tile_t *tile, int rotation, int zoom) { if (tile->color_r > -1) return NULL; if (!tile->image[rotation][zoom]) { actual_load_image(tiles, tile, rotation, zoom); } return tile->image[rotation][zoom]; } int tiles_cost(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].cost; } int tiles_getrevenue(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock) { return function_evaluate(tiles->list[objtype].revenue, cb, rock); } int tiles_getrent(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].rent; } int tiles_getupkeep(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock) { return function_evaluate(tiles->list[objtype].upkeep, cb, rock); } int tiles_gethouse(tiles_t *tiles, mapobj_t objtype) { if ((objtype < 0) || (objtype >= tiles->numtiles)) { return 0; } return tiles->list[objtype].live; } labor_t * tiles_getemploy(tiles_t *tiles, mapobj_t objtype) { return &tiles->list[objtype].labor; } int tiles_getnumemploy(tiles_t *tiles, mapobj_t objtype) { int i; int tot = 0; labor_t *labor = &tiles->list[objtype].labor; for (i = 0; i < LABOR_NUMGROUPS; i++) { tot += labor->workers[i]; } return tot; } void tiles_getsize(tiles_t *tiles, mapobj_t objtype, int *sizex, int *sizey) { if ((objtype < 0) || (objtype >= tiles->numtiles)) { *sizex = 1; *sizey = 1; return; } *sizex = tiles->list[objtype].sizex; *sizey = tiles->list[objtype].sizey; } int tiles_getlandvalue(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock) { return function_evaluate(tiles->list[objtype].landvalue, cb, rock); } int tiles_get_demolish_cost(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].demolish_cost; } int tiles_get_maxstudents(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].maxstudents; } int tiles_get_maxpatrons(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].maxpatrons; } int tiles_getproduction(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock) { return function_evaluate(tiles->list[objtype].production, cb, rock); } int tiles_getrange(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].range; } float tiles_getpatronperc(tiles_t *tiles, mapobj_t objtype) { return ((float)tiles->list[objtype].patronperc)/((float)100); } int tiles_getpoweruse(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].poweruse; } int tiles_getwateruse(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].wateruse; } int tiles_getcapacity(tiles_t *tiles, mapobj_t objtype) { return tiles->list[objtype].capacity; }