/* * 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 "map.h" #include "heightmap.h" #include "landvalue.h" #include "mapspot.h" #include "senkenconfig.h" #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define OWNER_MASK 0x07 #define FLAG_MASK 0xF8 #define MAPPOS(mapstruct, x, y) mapstruct->map[ ((y) * mapstruct->sizex) + (x)] #define MAPOWNERPOS(mapstruct, x, y) (mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] & OWNER_MASK) #define SETMAPOWNERPOS(mapstruct, x, y, newowner) mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] = (mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] & FLAG_MASK) | newowner #define FLAGSPOS(mapstruct, x, y) (mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] & FLAG_MASK) #define SETFLAGSPOS(mapstruct, x, y, newflags) mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] |= newflags #define UNSETFLAGSPOS(mapstruct, x, y, newflags) mapstruct->ownermap[ ((y) * mapstruct->sizex) + (x)] &= ~(newflags) #define OCCUPANCY(mapstruct, x, y) mapstruct->info[ ((y) * mapstruct->sizex) + (x)].occupancy #define PATRONS(mapstruct, x, y) mapstruct->info[ ((y) * mapstruct->sizex) + (x)].patrons #define ISWITHINRANGE(mapstruct, x, y) (((x < 0) || (x >= mapstruct->sizex) || (y < 0) || (y >= mapstruct->sizey)) ? 0 : 1) typedef struct server_info_s { unsigned int occupancy : 8; unsigned int employment : 8; unsigned int patrons : 8; } server_info_t; struct map_s { int sizex; int sizey; heightmap_t *heightmap; tiles_t *tiles; char *special_spots; unsigned char *map; unsigned char *ownermap; server_info_t *info; map_obj_changed_callback *obj_changed_cb; void *obj_changed_rock; }; extern int map_init(tiles_t *tiles, int sizex, int sizey, mapobj_t defaultobj, map_t **map) { map_t *ret; int i; int j; int r; ret = malloc(sizeof(map_t)); if (!ret) return -1; memset(ret, 0, sizeof(map_t)); r = heightmap_init(sizex, sizey, &ret->heightmap); if (r) return r; ret->tiles = tiles; ret->sizex = sizex; ret->sizey = sizey; ret->map = malloc(sizeof(unsigned char)*sizex*sizey); if (!ret->map) return -1; for (i = 0; i < ret->sizex; i++) { for (j = 0; j < ret->sizey; j++) { MAPPOS(ret, i, j) = defaultobj; } } ret->ownermap = malloc(sizeof(unsigned char)*sizex*sizey); if (!ret->ownermap) return -1; for (i = 0; i < ret->sizex; i++) { for (j = 0; j < ret->sizey; j++) { SETMAPOWNERPOS(ret, i, j, NO_OWNER); } } ret->info = calloc(1, sizeof(server_info_t)*sizex*sizey); if (!ret->info) return -1; ret->special_spots = calloc(1, sizeof(char)*sizex*sizey); if (!ret->special_spots) return -1; *map = ret; return 0; } extern int map_getvariable_cb(char *name, map_t *map, int x, int y) { if (!map) return 0; if (strcasecmp(name, "patrons") == 0) { return PATRONS(map, x, y); } else if (strcasecmp(name, "students") == 0) { return OCCUPANCY(map, x, y); } else if (strcasecmp(name, "heightdiff") == 0) { if (heightmap_islevel_spot(map->heightmap, x, y)) { return 1; } else { return 2; } } else if (strncasecmp(name, "within-",7) == 0) { char *end = NULL; int dist; mapobj_t of2; dist = strtoul(name+7, &end, 10); if ((!end) || (*end != '-')) { printf(_("Error parsing within: %s\n"), name); return 0; } of2 = map_item_name2obj(end+1); if (of2 == MAPOBJ_INVALID) { printf(_("Error. Invalid mapobj [%s] in within statement\n"), end+1); return 0; } return map_within(map, x, y, dist, of2, MAPOWNERPOS(map, x, y)); } printf(_("variable not understood: %s\n"), name); return 0; } char *map_spot2string(mapspot_t *spot) { static char ret[5]; if (spot->mapobj == 255) { ret[0] = '-'; } else { ret[0] = spot->mapobj + '0'; } if (spot->height == 255) { ret[1] = '-'; } else { ret[1] = spot->height + '0'; } if (spot->owner == 7) { ret[2] = '-'; } else { ret[2] = spot->owner + '0'; } if (spot->extra == 255) { ret[3] = '-'; } else { ret[3] = spot->extra + '0'; } ret[4] = '\0'; return ret; } int map_string2spot(char *str, mapspot_t *spot) { if (str[0] == '-') { spot->mapobj = 255; } else { spot->mapobj = str[0] - '0'; } if (str[1] == '-') { spot->height = 255; } else { spot->height = str[1] - '0'; } if (str[2] == '-') { spot->owner = 255; } else { spot->owner = str[2] - '0'; } if (str[3] == '-') { spot->extra = 255; } else { spot->extra = str[3] - '0'; } return 0; } int map_get_sizex(map_t *map) { return map->sizex; } int map_get_sizey(map_t *map) { return map->sizey; } static void get_surrounding(map_t *map, int x, int y, int dirview, surrounding_t *surrounding) { /* xxx should be some negative thingee */ memset(surrounding, 0, sizeof(surrounding_t)); if (x-1 >= 0) surrounding->index[(3-dirview+4)%4] = MAPPOS(map, x-1, y); if (x+1 < map->sizex) surrounding->index[(1-dirview+4)%4] = MAPPOS(map, x+1,y); if (y-1 >= 0) surrounding->index[(2-dirview+4)%4] = MAPPOS(map,x,y-1); if (y+1 < map->sizey) surrounding->index[(0-dirview+4)%4] = MAPPOS(map,x,y+1); } extern int map_empty_land(map_t *map, int x, int y) { return map_item_emptyland(MAPPOS(map, x, y)); } static int map_can_place_one(map_t *map, player_t *player, int x, int y, int dirview, mapobj_t obj, int *cost) { heightsquare_t square; surrounding_t surrounding; tile_t *tile; int r; mapspot_list_t *list = NULL; maptype_t type; int range; /* * If not demolishing and spot already contains something can't * place there */ switch (obj) { case MAPOBJ_ACTION_RAISE: case MAPOBJ_ACTION_LOWER: r = map_change_height_square(&list, map, player, x, y, (obj == MAPOBJ_ACTION_RAISE) ? 1 : -1); if (r) { return 0; } else { *cost = mapspot_list_length(list) * 1000; mapspot_list_free(list); return 1; } case MAPOBJ_ACTION_FLATTEN: return 1; /* xxx */ case MAPOBJ_ACTION_BUYLAND: if ((map_item_emptyland(MAPPOS(map, x, y))) && (map_can_buyland(map, player, x, y))) { *cost = government_calculate_onelandvalue(map, map->tiles, x, y); return 1; } else { return 0; } case MAPOBJ_ACTION_BUYALL: if (map_can_buyland(map, player, x, y)) { *cost = government_calculate_onelandvalue(map, map->tiles, x, y) + tiles_cost(map->tiles, MAPPOS(map, x, y)); return 1; } else { return 0; } case MAPOBJ_ACTION_SELLLAND: if ((map_item_emptyland(MAPPOS(map, x, y))) && (map_can_sellland(map, player, x, y))) { *cost = government_calculate_onelandvalue(map, map->tiles, x, y) * -1; return 1; } else { return 0; } case MAPOBJ_ACTION_DEMOLISH: *cost = tiles_get_demolish_cost(map->tiles, MAPPOS(map, x, y)); return 1; default: type = map_item_gettype(obj); switch(type) { case MAPTYPE_FARM: if (!map_item_landfarmable(MAPPOS(map, x, y))) { return 0; } break; case MAPTYPE_WATERUTIL: range = tiles_getrange(map->tiles, obj); if ((range) && (!map_within_type(map, x, y, range, MAPTYPE_WATER, -1))) { return 0; } break; default: break; } if (!map_item_emptyland(MAPPOS(map, x, y))) { return 0; } } /* must own the land */ if (player) { int playernum = player_getnum(player); if (MAPOWNERPOS(map,x,y) != playernum) { return 0; } } get_surrounding(map, x, y, dirview, &surrounding); heightmap_getheight(map->heightmap, x, y, dirview, &square); /* if we can get a pixmap for it then it we can place there! */ r = tiles_get(map->tiles, obj, &surrounding, &square, &tile); if (r == 0) { *cost = tiles_cost(map->tiles, obj); return 1; } else { return 0; } } int map_can_place(map_t *map, player_t *player, int x, int y, int dirview, mapobj_t obj, int *cost) { int sizex; int sizey; int i; int j; tiles_getsize(map->tiles, obj, &sizex, &sizey); for (i = x; i < x+sizex; i++) { for (j = y; j < y+sizey; j++) { if (!map_can_place_one(map, player, i, j, dirview, obj, cost)) { *cost = 0; return 0; } } } return 1; } int map_get_owner(map_t *map, int x, int y) { if ((x < 0) || (x >= map->sizex)) return -1; if ((y < 0) || (y >= map->sizey)) return -1; return MAPOWNERPOS(map,x,y); } extern void map_find_tiletop(map_t *map, int *xp, int *yp) { int x = *xp; int y = *yp; mapobj_t curtype; int sizex; int sizey; int i; int j; if (!(FLAGSPOS(map, x, y) & MAPFLAG_SUB)) return; curtype = MAPPOS(map, x, y); tiles_getsize(map->tiles, curtype, &sizex, &sizey); for (i = 0; i < sizex; i++) { for (j = 0; j < sizey; j++) { if ((!(FLAGSPOS(map, x-i, y-j) & MAPFLAG_SUB)) && (MAPPOS(map,x-i,y-j) == curtype)) { *xp = x-i; *yp = y-j; return; } } } printf(_("Can't find tile top at %d,%d\n"), x, y); } int map_set_type(map_t *map, int x, int y, mapobj_t type, int force) { int sizex; int sizey; int i; int j; int subflag; int cost; if (!force) { if (!map_can_place(map, NULL, x, y, 0, type, &cost)) { /*printf("Can't place that there!\n");*/ return -1; } } if (type == MAPOBJ_ACTION_DEMOLISH) { mapobj_t curtype = MAPPOS(map, x, y); tiles_getsize(map->tiles, curtype, &sizex, &sizey); if ((sizex > 0) || (sizey > 0)) { map_find_tiletop(map, &x, &y); } type = map_item_emptytype(); subflag = 0; } else { tiles_getsize(map->tiles, type, &sizex, &sizey); subflag = MAPFLAG_SUB; } for (i = x; i < x+sizex; i++) { for (j = y; j < y+sizey; j++) { mapobj_t old = MAPPOS(map,i,j); MAPPOS(map,i,j) = type; if ((i > x) || (j > y)) { SETFLAGSPOS(map,i,j, subflag); } else { UNSETFLAGSPOS(map,i,j, MAPFLAG_SUB); if (map->obj_changed_cb) { map->obj_changed_cb(map, i, j, old, type, map->obj_changed_rock); } } } } return 0; } int map_can_change_height(map_t *map, player_t *player, int x, int y, int change) { /* * can only raise if all adjacent squares are empty */ if (ISWITHINRANGE(map, x,y)) { if (!map_item_emptyland( MAPPOS(map,x ,y ) )) return 0; } if (ISWITHINRANGE(map, x-1,y)) { if (!map_item_emptyland( MAPPOS(map,x-1,y ) )) return 0; } if (ISWITHINRANGE(map, x, y-1)) { if (!map_item_emptyland( MAPPOS(map,x ,y-1) )) return 0; } if (ISWITHINRANGE(map, x-1,y-1)) { if (!map_item_emptyland( MAPPOS(map,x-1,y-1) )) return 0; } /* * Also must own all the adjacent squares */ if (player) { int playernum = player_getnum(player); if (ISWITHINRANGE(map, x,y)) { if (MAPOWNERPOS(map,x ,y ) != playernum) return 0; } if (ISWITHINRANGE(map, x-1,y)) { if (MAPOWNERPOS(map,x-1,y ) != playernum) return 0; } if (ISWITHINRANGE(map, x,y-1)) { if (MAPOWNERPOS(map,x ,y-1) != playernum) return 0; } if (ISWITHINRANGE(map, x-1,y-1)) { if (MAPOWNERPOS(map,x-1,y-1) != playernum) return 0; } } /* xxx if (!heightmap_can_changeheight(map->heightmap, x, y, change)) return 0; */ return 1; } static int opp_exists(int x, int y, void *data, float dist, void *rock) { if ((int)data != (int)rock) return -1; return 0; } extern int map_change_height_list(mapspot_list_t **list, map_t *map, player_t *player, int x, int y, int change) { int newh; int i; int j; int r; if (change == 0) return 0; if (!map_can_change_height(map, player, x, y, change)) { goto bad; } /* * If already contains other direction we know we have a loop */ r = mapspot_within_iterate(*list, x, y, 0.0, opp_exists, (void *)change); if (r) return 0; /* * 8 adjacent nodes must be within MAXHEIGHTDIFFERENCE */ newh = heightmap_get(map->heightmap, x, y) + (mapspot_list_num_within(*list, x, y, 0) * change) + change; for (i = -1; i <= 1; i++) { for (j = -1; j<= 1; j++) { if ((x + i < 0) || (x + i >= map->sizex)) continue; if ((y + j < 0) || (y + j >= map->sizey)) continue; if ((i != 0) || (j != 0)) { int h= heightmap_get(map->heightmap, x + i, y + j) + (mapspot_list_num_within(*list, x + i, y + j, 0) * change); if (newh - h > 1) { r = map_change_height_list(list, map, player, x + i, y + j, 1); if (r) goto bad; } else if (newh - h < -1) { r = map_change_height_list(list, map, player, x + i, y + j, -1); if (r) goto bad; } } } } mapspot_list_add(list, x, y, (void *)change); return 0; bad: mapspot_list_free(*list); *list = NULL; return -1; } void map_change_height_which(map_t *map, int x, int y, int change, int outx[4], int outy[4]) { heightsquare_t square; int most; int different = 0; int i; heightmap_getheight(map->heightmap, x, y, 0, &square); most = square.index[0]; /* * Find the new desired height */ for (i = 0; i < 4; i++) { if (square.index[i] != most) { different = 1; } if (change > 0) { if (square.index[i] > most) { most = square.index[i]; } } else { if (square.index[i] < most) { most = square.index[i]; } } } if (different == 0) most+=change; for (i = 0; i < 4; i++) { outx[i] = -1; outy[i] = -1; } if (square.dir.topleft != most) { outx[0] = x; outy[0] = y; } if (square.dir.topright != most) { outx[1] = x+1; outy[1] = y; } if (square.dir.botleft != most) { outx[2] = x; outy[2] = y+1; } if (square.dir.botright != most) { outx[3] = x+1; outy[3] = y+1; } } extern int map_change_height_square(mapspot_list_t **list, map_t *map, player_t *player, int x, int y, int change) { int outx[4]; int outy[4]; int i; int r; map_change_height_which(map, x, y, change, outx, outy); for (i = 0; i < 4; i++) { if ((outx[i] != -1) && (outy[i] != -1)) { r = map_change_height_list(list, map, player, outx[i], outy[i], change); if (r) return r; } } return 0; } int map_can_change_height_quad(map_t *map, player_t *player, int x, int y, int change) { int xc[4]; int yc[4]; int i; map_change_height_which(map,x,y,change,xc,yc); for (i = 0; i < 4; i++) { if ((xc[i] != -1) && (yc[i] != -1)) { if (!map_can_change_height(map,player, xc[i],yc[i],change)) { return 0; } } } return 1; } int map_can_buyland(map_t *map, player_t *player, int x, int y) { int current_owner = MAPOWNERPOS(map,x,y); if (map_item_gettype(MAPPOS(map, x, y)) == MAPTYPE_WATER) { return 0; } return (current_owner == NO_OWNER); } int map_can_sellland(map_t *map, player_t *player, int x, int y) { int current_owner = MAPOWNERPOS(map,x,y); int me = player_getnum(player); return (current_owner == me); } int map_is_connected(map_t *map, int x, int y, int new_owner) { if ((map_get_owner(map, x - 1, y ) != new_owner) && (map_get_owner(map, x + 1, y ) != new_owner) && (map_get_owner(map, x , y - 1) != new_owner) && (map_get_owner(map, x , y + 1) != new_owner)) { return 0; } return 1; } int map_set_owner(map_t *map, int new_owner, player_t *player, int x, int y, int force) { int current_owner = MAPOWNERPOS(map,x,y); if (new_owner != current_owner) { if (!force) { if ((current_owner != NO_OWNER) && (new_owner != NO_OWNER)) { printf(_("Somebody already owns %d,%d\n"),x,y); return -1; } } if (map_item_gettype(MAPPOS(map, x, y)) == MAPTYPE_WATER) { return -1; } if (player) { int landvalue; landvalue = government_calculate_onelandvalue(map, map->tiles, x, y); if (!player_canafford(player, landvalue)) { printf(_("Can't afford that!\n")); return -1; } if (new_owner == NO_OWNER) { player_addmoney(player, COST_LAND, landvalue); } else { player_reducemoney(player, COST_LAND, landvalue); } } SETMAPOWNERPOS(map,x,y, new_owner); } return 0; } int map_set_spot_cost(map_t *map, int x, int y, mapspot_t *spot) { int cost = 0; if (spot->height != 255) { cost += config_getint("land_changeheight_cost",1000); } if (spot->mapobj != 255) { if (spot->mapobj == MAPOBJ_ACTION_DEMOLISH) { /* is demolish */ cost += tiles_get_demolish_cost(map->tiles, MAPPOS(map,x,y)); } else { cost += tiles_cost(map->tiles, spot->mapobj); } } if (spot->owner != 7) { int lv = government_calculate_onelandvalue(map, map->tiles, x, y); if (spot->owner == NO_OWNER) { cost -= lv; } else { cost += lv; } } return cost; } int map_set_spot(map_t *map, player_t *player, int x, int y, int dirview, int isinit, mapspot_t *spot) { /* first set the height */ if (spot->height != 255) { int change = spot->height - heightmap_get(map->heightmap, x, y); int cost; if (change != 0) { int r; if (!isinit) { if (!map_can_change_height(map, player, x, y, change)) { printf(_("Can't change height! (%d,%d, %d)\n"), x, y, change); return -1; } } cost = config_getint("land_changeheight_cost",1000); if (player) { if (!player_canafford(player, cost)) { printf("Can't afford that!\n"); return -1; } } r = heightmap_setheight(map->heightmap, x, y, isinit, spot->height); if (r) { printf(_("Error setting height\n")); return r; } if (player) { /* xxx other type??? */ player_reducemoney(player, COST_BUILD, cost); } } } /* set the tile */ if (spot->mapobj != 255) { int cost; if (!isinit) { if (!map_can_place(map, player, x, y, dirview, spot->mapobj, &cost)) { /*printf("Can't place that there!\n");*/ return -1; } if (player) { if (!player_canafford(player, cost)) { printf(_("Can't afford that!\n")); return -1; } player_reducemoney(player, COST_BUILD, cost); } } map_set_type(map, x, y, spot->mapobj, 1); } /* set the owner */ if (spot->owner != 7) { int r = map_set_owner(map, spot->owner, player, x, y, 0); if (r) return r; } /* set the extras */ if (spot->extra != 255) { OCCUPANCY(map,x,y) = spot->extra; } return 0; } mapobj_t map_get_info(map_t *map, int x, int y, int dirview, surrounding_t *surrounding, heightsquare_t *heightsquare, int *specials, player_t *me, int *owner) { /* get surrounding tiles */ if (surrounding) { get_surrounding(map, x, y, dirview, surrounding); } /* get the height square */ if (heightsquare) { heightmap_getheight(map->heightmap, x, y, dirview, heightsquare); } /* specials */ if (specials) { *specials = 0; if (map->special_spots[y*map->sizex + x]) { *specials = map->special_spots[y*map->sizex + x]; } } if (owner) { *owner = MAPOWNERPOS(map,x,y); } return MAPPOS(map,x,y); } mapobj_t map_get_type(map_t *map, int x, int y) { if ((x < 0) || (x >= map->sizex)) return MAPOBJ_INVALID; if ((y < 0) || (y >= map->sizey)) return MAPOBJ_INVALID; return MAPPOS(map,x,y); } extern maptype_t map_get_maptype(map_t *map, int x, int y) { mapobj_t obj = map_get_type(map, x, y); return map_item_gettype(obj); } int map_get_spot(map_t *map, int x, int y, mapspot_t *spot) { spot->height = heightmap_get(map->heightmap, x, y); spot->mapobj = MAPPOS(map,x,y); spot->owner = MAPOWNERPOS(map,x,y); spot->extra = OCCUPANCY(map,x,y); return 0; } int map_raise_spot(map_t *map, int x, int y) { if (!map_can_change_height(map, NULL, x, y, 1)) return -1; heightmap_raise(map->heightmap, x, y); return 0; } int map_lower_spot(map_t *map, int x, int y) { if (!map_can_change_height(map, NULL, x, y, -1)) return -1; heightmap_lower(map->heightmap, x, y); return 0; } int map_temporary_reset(map_t *map) { memset(map->special_spots, 0, sizeof(char)*map->sizex*map->sizey); return 0; } int map_set_temporary(map_t *map, player_t *player, int mapx, int mapy, mapobj_t obj, int special_yes, int special_no) { return map_set_temporary_box(map, player, mapx, mapy, mapx, mapy, obj, special_yes, special_no); } static int can_place(map_t *map, player_t *player, int x, int y, mapobj_t obj, int *cost) { if (!map_can_place(map, player, x, y, 0, obj, cost)) { return 0; } else { return 1; } } int map_set_temporary_box(map_t *map, player_t *player, int x1, int y1, int x2, int y2, mapobj_t obj, int special_yes, int special_no) { int mx1, mx2, my1, my2; int x, y; int rem; int sizex; int sizey; int total_cost = 0; map_temporary_reset(map); tiles_getsize(map->tiles, obj, &sizex, &sizey); /* sort low and hi coordinates */ mx1 = MIN(x1,x2); my1 = MIN(y1,y2); mx2 = MAX(x1,x2); my2 = MAX(y1,y2); /* * Make sure is divisible by sizex/sizey */ rem = (mx2 - mx1+1)%sizex; if (rem) { mx2 += sizex-rem; } rem = (my2 - my1+1)%sizey; if (rem) { my2 += sizey-rem; } for (x = mx1; x <= mx2; x+=sizex) { for (y = my1; y <= my2; y+=sizey) { int i; int j; int cost; int can = can_place(map, player, x, y, obj, &cost); total_cost += cost; for (i = 0; i < sizex; i++) { for (j = 0; j < sizey; j++) { int s = can ? special_yes : special_no; if ((i > 0) || (j > 0)) { s |= MAPSPECIAL_SUB; } map->special_spots[(y+j)*map->sizex + (x+i)] = s; } } } } return total_cost; } int map_set_temporary_line(map_t *map, player_t *player, int x1, int y1, int x2, int y2, mapobj_t obj, int special_yes, int special_no) { int fromx, tox; int fromy, toy; int x, y; int cost; int total_cost = 0; map_temporary_reset(map); fromx = MIN(x1,x2); tox = MAX(x1,x2); for (x = fromx; x<= tox; x++) { int can = can_place(map, player, x, y1, obj, &cost); int s = can ? special_yes : special_no; total_cost += cost; map->special_spots[y1*map->sizex + x] = s; } fromy = MIN(y1,y2); toy = MAX(y1,y2); for (y = fromy+1; y<= toy; y++) { int can = can_place(map, player, x2, y, obj, &cost); int s = can ? special_yes : special_no; total_cost += cost; map->special_spots[y*map->sizex + x2] = s; } return total_cost; } static void flood_fill(map_t *map, int x, int y, int special) { heightsquare_t square; heightmap_getheight(map->heightmap, x, y, 0, &square); if (map->special_spots[y*map->sizex + x] == special) return; if (heightmap_islevel(&square) == 0) return; if ((x < 0) || (x >= map->sizex)) return; if ((y < 0) || (y >= map->sizey)) return; /* set this square and continue the fill */ map->special_spots[y*map->sizex + x] = special; flood_fill(map, x+1, y ,special); flood_fill(map, x-1, y ,special); flood_fill(map, x , y+1,special); flood_fill(map, x , y-1,special); } int map_set_temporary_fill(map_t *map, int mapx, int mapy, int special) { map_temporary_reset(map); flood_fill(map, mapx, mapy, special); return 0; } int map_temporary_iterate(map_t *map, int mask, temporary_iterate_callback *callback, void *rock) { int x; int y; int r; for (x = 0; x < map->sizex; x++) { for (y = 0; y < map->sizey; y++) { int s = map->special_spots[y*map->sizex + x]; if ((s & mask) && (!(s & MAPSPECIAL_SUB))) { r = callback(map, x, y, rock); if (r) return r; } } } return 0; } int map_getowner(map_t *map, int x, int y) { return MAPOWNERPOS(map,x,y); } int map_islevel(map_t *map, int x, int y) { return heightmap_islevel_spot(map->heightmap, x, y); } int map_getsteepness(map_t *map, int x, int y) { heightsquare_t square; int lowest = 99999; int highest = -1; int i; heightmap_getheight(map->heightmap, x, y, 0, &square); for (i = 0; i < 4; i++) { if (square.index[i] < lowest) { lowest = square.index[i]; } if (square.index[i] > highest) { highest = square.index[i]; } } return highest-lowest; } int map_is_school_full(map_t *map, int x, int y) { int house = tiles_get_maxstudents(map->tiles, MAPPOS(map, x, y)); if (!house) return 1; if (house - OCCUPANCY(map, x, y) > 0) { return 0; } else { return 1; } } int map_isvacant(map_t *map, int x, int y) { int house = tiles_gethouse(map->tiles, MAPPOS(map, x, y)); if (!house) return 0; if (house - OCCUPANCY(map, x, y) > 0) { return 1; } return 0; } void map_live_info(map_t *map, int x, int y, int *live, int *maxlive) { mapobj_t obj = MAPPOS(map, x, y); maptype_t type = map_item_gettype(obj); if (type == MAPTYPE_SCHOOL) { *maxlive = tiles_get_maxstudents(map->tiles, obj); } else { *maxlive = tiles_gethouse(map->tiles, obj); } *live = OCCUPANCY(map, x, y); } int map_occupancy(map_t *map, int x, int y) { return OCCUPANCY(map, x, y); } int map_rent(map_t *map, int x, int y, int *owner) { if (!map_isvacant(map, x, y)) { printf(_("Can't rent that apt\n")); return -1; } OCCUPANCY(map, x, y) += 1; if (owner) *owner = MAPOWNERPOS(map, x, y); return 0; } int map_unrent(map_t *map, int x, int y) { int house = tiles_gethouse(map->tiles, MAPPOS(map, x, y)); if (!house) { printf(_("Can't unrent from place that doesn't rent: %d,%d\n"), x, y); return -1; } OCCUPANCY(map, x, y) -= 1; return 0; } int map_attend_school(map_t *map, int x, int y) { if (map_is_school_full(map, x, y)) { return -1; } OCCUPANCY(map, x, y) += 1; return 0; } int map_leave_school(map_t *map, int x, int y) { int house = tiles_get_maxstudents(map->tiles, MAPPOS(map, x, y)); if (!house) { printf(_("Can't leave from place that isn't school: %d,%d\n"), x, y); return -1; } OCCUPANCY(map, x, y) -= 1; return 0; } void map_school_info(map_t *map, int x, int y, int *attend, int *maxattend) { *maxattend = tiles_get_maxstudents(map->tiles, MAPPOS(map, x, y)); *attend = OCCUPANCY(map, x, y); } int map_getrent(map_t *map, int mapx, int mapy) { mapobj_t obj = MAPPOS(map, mapx, mapy); return tiles_getrent(map->tiles, obj); } typedef struct getvar_struct_s { int x; int y; map_t *map; } getvar_struct_t; static int getvar_cb(char *name, void *rock) { getvar_struct_t *rstruct = (getvar_struct_t *)rock; return map_getvariable_cb(name, rstruct->map, rstruct->x, rstruct->y); } void map_upkeep_iterate(map_t *map, upkeep_iterate_callback *callback, void *rock) { int x; int y; getvar_struct_t rstruct; rstruct.map = map; for (x = 0; x < map->sizex; x++) { for (y = 0; y < map->sizey; y++) { int owner = MAPOWNERPOS(map, x, y); int upkeep; rstruct.x = x; rstruct.y = y; upkeep = tiles_getupkeep(map->tiles, MAPPOS(map, x, y), &getvar_cb, &rstruct); if (upkeep) { callback(map, x, y, owner, upkeep, rock); } } } } int map_get_upkeep(map_t *map, int x, int y) { getvar_struct_t rstruct; rstruct.map = map; rstruct.x = x; rstruct.y = y; return tiles_getupkeep(map->tiles, MAPPOS(map, x, y), &getvar_cb, &rstruct); } int map_get_patrons(map_t *map, int x, int y) { return PATRONS(map, x, y); } #if 0 void map_revenue_iterate(map_t *map, revenue_iterate_callback *callback, void *rock) { int x; int y; for (x = 0; x < map->sizex; x++) { for (y = 0; y <= map->sizey; y++) { int owner = MAPOWNERPOS(map, x, y); { getvar_struct_t rstruct; int revenue; float food_production; int costs; rstruct.x = x; rstruct.y = y; rstruct.map = map; revenue = tiles_getrevenue(map->tiles, MAPPOS(map, x, y), &map_getvariable_cb, &rstruct); food_production = tiles_getproduction(map->tiles, MAPPOS(map, x, y), &getvariable_cb, &rstruct); costs = /* xxx EMPLOYMENT(map, x, y) * SALARY + */ tiles_getupkeep(map->tiles, MAPPOS(map, x, y)); if (revenue) { int maxemploy = max_employ(map, x, y); int employ = 0; /* xxx EMPLOYMENT(map, x, y); */ if (maxemploy > 0) { revenue *= employ/maxemploy; food_production *= employ/maxemploy; } callback(map, x, y, owner, revenue, costs, food_production, rock); } } } } } int map_get_revenue(map_t *map, int x, int y) { getvar_struct_t rstruct; rstruct.x = x; rstruct.y = y; rstruct.map = map; return tiles_getrevenue(map->tiles, MAPPOS(map, x, y), &getvariable_cb, &rstruct); } #endif /* 0 */ static int is_zoning(mapobj_t obj, void *rock) { return (map_item_gettype(obj) == MAPTYPE_ZONING); } void map_zoning_iterate(map_t *map, map_iterate_callback *callback, void *rock) { int x = rand()%map->sizex; int y = rand()%map->sizey; map_iterate(map, MAP_ITERATE_RANDOM, x, y, 0, &is_zoning, NULL, NULL, NULL, callback, rock); } void map_all_iterate(map_t *map, int startx, int starty, map_iterate_callback *callback, void *rock) { map_iterate(map, MAP_ITERATE_RADIAL, startx, starty, 0, NULL, NULL, NULL, NULL, callback, rock); } void map_player_quit(map_t *map, player_t *player) { int x; int y; int num = player_getnum(player); for (x = 0; x < map->sizex; x++) { for (y = 0; y <= map->sizey; y++) { int owner = MAPOWNERPOS(map, x, y); if (owner == num) { SETMAPOWNERPOS(map, x, y,NO_OWNER); } } } } static inline void iter(map_t *map, int x, int y, map_iterate_criteria_land *land_crit, void *land_rock, map_iterate_criteria_owner *owner_crit, void *owner_rock, map_iterate_callback *cb, void *rock) { mapobj_t obj = MAPPOS(map, x, y); int owner = MAPOWNERPOS(map, x, y); if ((x < 0) || (x >= map->sizex)) return; if ((y < 0) || (y >= map->sizey)) return; if (FLAGSPOS(map, x, y) & MAPFLAG_SUB) { return; } if (land_crit) { if (!land_crit(obj, land_rock)) { return; } } if (owner_crit) { if (!owner_crit(owner, owner_rock)) { return; } } cb(map, x, y, owner, obj, rock); } int map_iterate(map_t *map, iterate_type type, int startx, int starty, int within, map_iterate_criteria_land *land_crit, void *land_rock, map_iterate_criteria_owner *owner_crit, void *owner_rock, map_iterate_callback *cb, void *rock) { int x; int y; int radius; int i; if (type == MAP_ITERATE_WITHIN) { radius = within; } else { radius = map->sizex; } switch (type) { case MAP_ITERATE_WITHIN: /* fallthru */ case MAP_ITERATE_RADIAL: /* fallthru */ case MAP_ITERATE_RANDOM: for (i = 0; i < radius; i++) { /* top + bottom */ for (x = startx - i; x <= startx + i; x++) { iter(map, x, starty - i, land_crit, land_rock, owner_crit, owner_rock, cb, rock); if (i > 0) iter(map, x, starty + i, land_crit, land_rock, owner_crit, owner_rock, cb, rock); } /* left + right */ if (i > 0) { for (y = starty - i+1; y < starty + i; y++) { iter(map, startx - i, y, land_crit, land_rock, owner_crit, owner_rock, cb, rock); iter(map, startx + i, y, land_crit, land_rock, owner_crit, owner_rock, cb, rock); } } } break; case MAP_ITERATE_NORMAL: for (x = 0; x < map->sizex; x++) { for (y = 0; y <= map->sizey; y++) { iter(map, x, y, land_crit, land_rock, owner_crit, owner_rock, cb, rock); } } break; } return 0; } static void count_vacancies(map_t *map, int mapx, int mapy, int owner, mapobj_t obj, void *rock) { int *count = (int *)rock; int house = tiles_gethouse(map->tiles, obj); int vacant; if (!house) return; vacant = house - OCCUPANCY(map, mapx, mapy); (*count) += vacant; } int map_vacancies(map_t *map) { int count = 0; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, NULL, NULL, NULL, NULL, &count_vacancies, &count); return count; } int map_within(map_t *map, int sx, int sy, int dist, mapobj_t obj, int owner) { int x; int y; for (x = sx-dist; x <= sx+dist; x++) { for (y = sy-dist; y <= sy+dist; y++) { if ((x >= 0) && (x < map->sizex) && (y >= 0) && (y < map->sizey)) { if (MAPPOS(map, x, y) == obj) { if ((owner < 0) || (MAPOWNERPOS(map, x, y) == owner)) { return 1; } } } } } return 0; } int map_within_type(map_t *map, int sx, int sy, int dist, maptype_t type, int owner) { int x; int y; for (x = sx-dist; x <= sx+dist; x++) { for (y = sy-dist; y <= sy+dist; y++) { if ((x >= 0) && (x < map->sizex) && (y >= 0) && (y < map->sizey)) { if (map_item_gettype(MAPPOS(map, x, y)) == type) { if ((owner < 0) || (MAPOWNERPOS(map, x, y) == owner)) { return 1; } } } } } return 0; } extern float map_density(map_t *map, int sx, int sy, int dist) { int x; int y; int num = 0; getvar_struct_t rstruct; rstruct.map = map; for (x = sx-dist; x <= sx+dist; x++) { for (y = sy-dist; y <= sy+dist; y++) { if ((x >= 0) && (x < map->sizex) && (y >= 0) && (y < map->sizey)) { rstruct.x = x; rstruct.y = y; num += OCCUPANCY(map,x,y); num += tiles_getnumemploy(map->tiles, MAPPOS(map, x, y)); } } } return ((float)num)/((float)(dist*dist)); } int map_within_count(map_t *map, int sx, int sy, int dist, mapobj_t obj) { int x; int y; int count = 0; for (x = sx-dist-1; x <= sx+dist; x++) { for (y = sy-dist-1; y <= sy+dist; y++) { if ((x >= 0) && (x < map->sizex) && (y >= 0) && (y < map->sizey)) { if (MAPPOS(map, x, y) == obj) { count++; } } } } return count; } int map_within_count_type(map_t *map, int sx, int sy, int dist, maptype_t type) { int x; int y; int count = 0; for (x = sx-dist-1; x <= sx+dist; x++) { for (y = sy-dist-1; y <= sy+dist; y++) { if ((x >= 0) && (x < map->sizex) && (y >= 0) && (y < map->sizey)) { if (map_item_gettype(MAPPOS(map, x, y)) == type) { count++; } } } } return count; } static void find_vacant_list(map_t *map, int mapx, int mapy, int owner, mapobj_t obj, void *rock) { mapspot_list_t **list = (mapspot_list_t **)rock; int house = tiles_gethouse(map->tiles, obj); int vacant; if (!house) return; vacant = house - OCCUPANCY(map, mapx, mapy); if (vacant > 0) { mapspot_list_add(list, mapx, mapy, NULL); } } mapspot_list_t * map_find_vacant_list(map_t *map) { mapspot_list_t *list = NULL; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, NULL, NULL, NULL, NULL, &find_vacant_list, &list); return list; } static void find_tile_list(map_t *map, int mapx, int mapy, int owner, mapobj_t obj, void *rock) { mapspot_list_t **list = (mapspot_list_t **)rock; if ((FLAGSPOS(map, mapx, mapy) & MAPFLAG_SUB)) return; mapspot_list_add(list, mapx, mapy, (void *)obj); } extern int map_is_sub(map_t *map, int mapx, int mapy) { if ((FLAGSPOS(map, mapx, mapy) & MAPFLAG_SUB)) { return 1; } else { return 0; } } extern int map_is_multi(map_t *map, int mapx, int mapy) { int sizex; int sizey; mapobj_t obj = MAPPOS(map, mapx, mapy); tiles_getsize(map->tiles, obj, &sizex, &sizey); if ((sizex > 1) || (sizey > 1)) { return 1; } else { return 0; } } static int is_tile(mapobj_t obj, void *rock) { maptype_t lookingfor = (maptype_t)rock; return (map_item_gettype(obj) == lookingfor); } mapspot_list_t * map_find_tile_list(map_t *map, maptype_t type) { mapspot_list_t *list = NULL; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, &is_tile, (void *)type, NULL, NULL, &find_tile_list, &list); return list; } static int is_obj(mapobj_t obj, void *rock) { mapobj_t lookingfor = (mapobj_t)rock; return (obj == lookingfor); } mapspot_list_t * map_find_tile_list_obj(map_t *map, mapobj_t obj) { mapspot_list_t *list = NULL; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, &is_obj, (void *)obj, NULL, NULL, &find_tile_list, &list); return list; } static int is_in_list(mapobj_t obj, void *rock) { mapobj_t *list = (mapobj_t *)rock; int i; for (i = 0; list[i] != MAPOBJ_INVALID; i++) { if (list[i] == obj) return 1; } return 0; } mapspot_list_t * map_find_tile_list_fromlist(map_t *map, mapobj_t *objlist) { mapspot_list_t *list = NULL; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, &is_in_list, objlist, NULL, NULL, &find_tile_list, &list); return list; } int map_patronize(map_t *map, int x, int y) { int cur = PATRONS(map, x, y); int max = tiles_get_maxpatrons(map->tiles, MAPPOS(map, x, y)); if (cur >= max) { return -1; } PATRONS(map, x, y) += 1; return 0; } int map_clear_patronige(map_t *map) { int x; int y; for (x = 0; x < map->sizex; x++) { for (y = 0; y < map->sizey; y++) { PATRONS(map, x, y) = 0; } } return 0; } extern void map_set_obj_changed_cb(map_t *map, map_obj_changed_callback *cb, void *rock) { map->obj_changed_cb = cb; map->obj_changed_rock = rock; } extern int map_get_height(map_t *map, int x, int y) { return heightmap_get(map->heightmap, x, y); } extern void map_setflag(map_t *map, int x, int y, int flag) { SETFLAGSPOS(map, x, y, flag); } extern int map_isflagset(map_t *map, int x, int y, int flag) { if (FLAGSPOS(map, x, y) & flag) { return 1; } else { return 0; } } extern void map_clearflag_wholemap(map_t *map, int flag) { int x; int y; for (x = 0; x < map->sizex; x++) { for (y = 0; y < map->sizey; y++) { UNSETFLAGSPOS(map, x, y, flag); } } }