/********************************************************************/ /* */ /* L I QQ U U I DD W W A RR 555 */ /* L I Q Q U U I D D W W A A R R 5 */ /* L I Q Q U U I D D W W W AAA RR 55 */ /* L I Q Q U U I D D WW WW A A R R 5 */ /* LLL I Q Q U I DD W W A A R R 55 */ /* */ /* b */ /* bb y y */ /* b b yyy */ /* bb y */ /* yy */ /* */ /* U U FFF O O TTT */ /* U U F O O O O T */ /* U U TIRET FF O O O O T */ /* U U F O O O O T */ /* U F O O T */ /* */ /********************************************************************/ /*****************************************************************************/ /* Liquid War is a multiplayer wargame */ /* Copyright (C) 1998-2007 Christian Mauduit */ /* */ /* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* */ /* Liquid War homepage : http://www.ufoot.org/liquidwar/v5 */ /* Contact author : ufoot@ufoot.org */ /*****************************************************************************/ /********************************************************************/ /* nom : map.c */ /* contenu : decryptage des tableaux en memoire */ /* date de modif : 3 mai 98 */ /********************************************************************/ /*==================================================================*/ /* includes */ /*==================================================================*/ #include #include #include "alleg2.h" #include "bigdata.h" #include "disk.h" #include "map.h" #include "texture.h" #include "network.h" #include "log.h" #include "macro.h" #include "serial.h" #include "path.h" #include "random.h" /*==================================================================*/ /* constantes */ /*==================================================================*/ #define LIGHT_OR_DARK_TRESHOLD 315 #define CONSIDERED_AS_DARK 0 #define CONSIDERED_AS_LIGHT 2 #define PLAYABLE_AREA 1 #define MINI_SIDE_SIZE 4 #define MINI_PLAYABLE_AREA 1024 /*==================================================================*/ /* fonctions */ /*==================================================================*/ /*------------------------------------------------------------------*/ static int calc_zoom_factor (int w, int h, int min_w, int min_h) { int factor_w, factor_h, factor; if (w <= 0 || h <= 0) { log_println_str ("Error: map size is null or negative!"); w = h = 1; } factor_w = ((min_w - 1) / w) + 1; factor_h = ((min_h - 1) / h) + 1; factor = (factor_w > factor_h) ? factor_w : factor_h; return factor; } /*------------------------------------------------------------------*/ static void sort_light_and_dark (BITMAP * bmp, PALETTE pal) { char table[256]; int i, x, y; for (i = 0; i < 256; ++i) { if (6 * pal[i].r + 3 * pal[i].g + pal[i].b > LIGHT_OR_DARK_TRESHOLD) table[i] = CONSIDERED_AS_LIGHT; else table[i] = CONSIDERED_AS_DARK; } for (y = 0; y < bmp->h; ++y) for (x = 0; x < bmp->w; ++x) putpixel (bmp, x, y, table[getpixel (bmp, x, y)]); } /*------------------------------------------------------------------*/ static BITMAP * extract_significant_part (BITMAP * src) { int min_x = src->w; int min_y = src->h; int max_x = -1; int max_y = -1; int dst_x, dst_y, dst_w, dst_h; int x, y; BITMAP *result; for (y = 0; y < src->h; ++y) for (x = 0; x < src->w; ++x) if (getpixel (src, x, y) == CONSIDERED_AS_DARK) { if (min_x > x) min_x = x; if (min_y > y) min_y = y; if (max_x < x) max_x = x; if (max_y < y) max_y = y; } dst_x = min_x; dst_y = min_y; dst_w = max_x - min_x + 1; dst_h = max_y - min_y + 1; if (dst_w < MINI_SIDE_SIZE || dst_h < MINI_SIDE_SIZE) result = NULL; else { result = my_create_bitmap (dst_w, dst_h); if (result) blit (src, result, dst_x, dst_y, 0, 0, dst_w, dst_h); } return result; } /*-----------------------------------------------------------------*/ static int spread_color_down (BITMAP * bmp, int color1, int color2) { int x, y, x1, y1, x2, y2, found = 0; for (y = 0; y < bmp->h; ++y) for (x = 0; x < bmp->w; ++x) if (getpixel (bmp, x, y) == color2) { x1 = x - 1; if (x1 < 0) x1 = 0; x2 = x + 1; if (x2 > bmp->w - 1) x2 = bmp->w - 1; y1 = y - 1; if (y1 < 0) y1 = 0; y2 = y + 1; if (y2 > bmp->h - 1) y2 = bmp->h - 1; if (getpixel (bmp, x2, y) == color1) { putpixel (bmp, x2, y, color2); found++; } if (getpixel (bmp, x2, y2) == color1) { putpixel (bmp, x2, y2, color2); found++; } if (getpixel (bmp, x, y2) == color1) { putpixel (bmp, x, y2, color2); found++; } if (getpixel (bmp, x1, y2) == color1) { putpixel (bmp, x1, y2, color2); found++; } } return found; } /*-----------------------------------------------------------------*/ static int spread_color_up (BITMAP * bmp, int color1, int color2) { int x, y, x1, y1, x2, y2, found = 0; for (y = bmp->h - 1; y >= 0; --y) for (x = bmp->w - 1; x >= 0; --x) if (getpixel (bmp, x, y) == color2) { x1 = x - 1; if (x1 < 0) x1 = 0; x2 = x + 1; if (x2 > bmp->w - 1) x2 = bmp->w - 1; y1 = y - 1; if (y1 < 0) y1 = 0; y2 = y + 1; if (y2 > bmp->h - 1) y2 = bmp->h - 1; if (getpixel (bmp, x1, y) == color1) { putpixel (bmp, x1, y, color2); found++; } if (getpixel (bmp, x1, y1) == color1) { putpixel (bmp, x1, y1, color2); found++; } if (getpixel (bmp, x, y1) == color1) { putpixel (bmp, x, y1, color2); found++; } if (getpixel (bmp, x2, y1) == color1) { putpixel (bmp, x2, y1, color2); found++; } } return found; } /*-----------------------------------------------------------------*/ static int check_if_playable (BITMAP * bmp) { int x = 0, y = 0, x0, y0; int unplayable = 0; int playable_place = 0; /* * Now we draw the "outside" of the map. This is usefull * to avoid bugs (core dumps) if the map is not correctly drawn. */ hline (bmp, 0, 0, bmp->w - 1, CONSIDERED_AS_DARK); hline (bmp, 0, bmp->h - 1, bmp->w - 1, CONSIDERED_AS_DARK); vline (bmp, 0, 0, bmp->h - 1, CONSIDERED_AS_DARK); vline (bmp, bmp->w - 1, 0, bmp->h - 1, CONSIDERED_AS_DARK); x0 = y0 = -1; for (y = 0; y < bmp->h && y0 < 0; ++y) for (x = 0; x < bmp->w && x0 < 0; ++x) if (getpixel (bmp, x, y) == CONSIDERED_AS_LIGHT) { x0 = x; y0 = y; } if (x0 > 0 && y0 > 0) { putpixel (bmp, x, y, PLAYABLE_AREA); while (spread_color_down (bmp, CONSIDERED_AS_LIGHT, PLAYABLE_AREA) + spread_color_up (bmp, CONSIDERED_AS_LIGHT, PLAYABLE_AREA)); } else unplayable |= 1; for (y = 0; y < bmp->h; ++y) for (x = 0; x < bmp->w; ++x) if (getpixel (bmp, x, y) == PLAYABLE_AREA) ++playable_place; unplayable |= (playable_place < MINI_PLAYABLE_AREA); return (!unplayable); } /*-----------------------------------------------------------------*/ static void fill_with_fg_and_bg (BITMAP * bmp, int fg, int bg) { int x, y; /* * We set up the real fg and bg color instead of the * CONSIDERED_AS_... constants. */ for (y = 0; y < bmp->h; ++y) { for (x = 0; x < bmp->w; ++x) { putpixel (bmp, x, y, getpixel (bmp, x, y) == PLAYABLE_AREA ? bg : fg); } } } /*-----------------------------------------------------------------*/ static void convert_to_buffer (BITMAP * bmp, char *buffer, int *size, int *bg_size) { int pos_src, l, wh; char *data; wh = bmp->h * bmp->w; data = bmp->dat; pos_src = 0; (*size) = 0; (*bg_size) = 0; while (pos_src < wh) { l = 0; if (data[pos_src] != PLAYABLE_AREA) { while (pos_src < wh && (data[pos_src] != PLAYABLE_AREA) && l < 127) { l++; pos_src++; } buffer[(*size)++] = l; } else { while (pos_src < wh && (data[pos_src] == PLAYABLE_AREA) && l < 127) { l++; pos_src++; } buffer[(*size)++] = -l; (*bg_size) += l; } } buffer[(*size)++] = 0; } /*------------------------------------------------------------------*/ void * lw_map_archive_raw_bmp (BITMAP * bmp, PALETTE pal, const char *filename) { int w = 0, h = 0, size = 0, bg_size = 0; BITMAP *sub_bmp; char *temp = NULL; char *result = NULL; static int index = 1; char readable_name_buffer[LW_MAP_READABLE_NAME_SIZE + 1]; char system_name_buffer[LW_MAP_SYSTEM_NAME_SIZE + 1]; if (bmp) { sort_light_and_dark (bmp, pal); sub_bmp = extract_significant_part (bmp); if (sub_bmp) { w = sub_bmp->w; h = sub_bmp->h; if (check_if_playable (sub_bmp)) { temp = malloc_in_big_data_bottom (w * h + 1); if (temp) convert_to_buffer (sub_bmp, temp, &size, &bg_size); } destroy_bitmap (sub_bmp); } destroy_bitmap (bmp); } if (temp) { result = malloc (size + 8 + LW_MAP_SYSTEM_NAME_SIZE + LW_MAP_READABLE_NAME_SIZE); if (result) { /* * Write the header (size + geometry) */ lw_serial_set_map_header ((void *) result, size, (short) w, (short) h); /* * Then we store its system name */ memset (system_name_buffer, 0, sizeof (system_name_buffer)); LW_MACRO_STRCPY (system_name_buffer, lw_path_get_system_name (filename)); memcpy (result + 8, system_name_buffer, LW_MAP_SYSTEM_NAME_SIZE); /* * Then we store its readable name */ memset (readable_name_buffer, 0, sizeof (readable_name_buffer)); if (strcmp (filename, "lwmapgen") == 0) { /* * This is a map generated by lwmapgen, we label it as such. */ LW_MACRO_SPRINTF0 (readable_name_buffer, "Random map"); } else { LW_MACRO_SPRINTF1 (readable_name_buffer, "Custom map %d", index++); } memcpy (result + 8 + LW_MAP_SYSTEM_NAME_SIZE, readable_name_buffer, LW_MAP_READABLE_NAME_SIZE); /* * And at last we write the map itself */ memcpy (result + 8 + LW_MAP_SYSTEM_NAME_SIZE + LW_MAP_READABLE_NAME_SIZE, temp, size); } free_last_big_data_bottom (); } return result; } /*------------------------------------------------------------------*/ void * lw_map_archive_raw (const char *filename) { BITMAP *bmp; PALETTE pal; void *result = NULL; bmp = load_bitmap (filename, pal); if (bmp) { result = lw_map_archive_raw_bmp (bmp, pal, filename); } return result; } /*------------------------------------------------------------------*/ static void * lw_map_get_safe (int num, int network, int random) { void *raw_map = NULL; if (num > RAW_MAP_NUMBER - 1) num = RAW_MAP_NUMBER - 1; if (num < 0) num = 0; if (network && LW_NETWORK_RAW_MAP != NULL) { raw_map = LW_NETWORK_RAW_MAP; } else { if (random && LW_RANDOM_RAW_MAP != NULL) { raw_map = LW_RANDOM_RAW_MAP; } else { raw_map = RAW_MAP_ORDERED[num]; } } return raw_map; } /*------------------------------------------------------------------*/ BITMAP * lw_map_create_bicolor (int num, int fg, int bg, int network, int random, int min_w, int min_h, int *zoom_factor) { int pos_dst, pos_src, i, j, l, color; short w, h; signed char *data_dst, *data_src; BITMAP *result = NULL; BITMAP *temp; void *raw_map; int size, surface; int x, y; raw_map = lw_map_get_safe (num, network, random); data_src = raw_map; data_src += 8 + LW_MAP_SYSTEM_NAME_SIZE + LW_MAP_READABLE_NAME_SIZE; /* * We read the header (size + geometry) */ lw_serial_get_map_header (raw_map, &size, &w, &h); surface = ((int) w) * ((int) h); *zoom_factor = calc_zoom_factor (w, h, min_w, min_h); pos_src = pos_dst = 0; temp = my_create_bitmap (w, h); if (temp) { data_dst = temp->dat; while (pos_src < size && (l = data_src[pos_src++]) != 0) { if (l > 0) color = CONSIDERED_AS_DARK; else { l = -l; color = CONSIDERED_AS_LIGHT; } for (i = 0; i < l && pos_dst < surface; ++i) data_dst[pos_dst++] = color; } /* * We check if the map is "playable". Normally unplayable maps * should never make it there but... */ if (check_if_playable (temp)) { fill_with_fg_and_bg (temp, fg, bg); result = my_create_bitmap (w * (*zoom_factor), h * (*zoom_factor)); if (result) { /* * Not we magnify the map if needed, so that very small * maps are interesting to play on fast fancy computers */ for (y = 0; y < h; ++y) for (x = 0; x < w; ++x) { color = getpixel (temp, x, y); for (i = 0; i < *zoom_factor; ++i) for (j = 0; j < *zoom_factor; ++j) { putpixel (result, x * *zoom_factor + j, y * *zoom_factor + i, color); } } } } else { log_println_str ("Error: unplayable map!"); } destroy_bitmap (temp); } return result; } /*------------------------------------------------------------------*/ BITMAP * lw_map_create_textured (int num, int fg, int bg, int network, int random, int min_w, int min_h) { int x, y, w, h, color; BITMAP *result = NULL, *bg_tex, *fg_tex; int fg_w, fg_h; int bg_w, bg_h; int zoom_factor, zoom_factor_fg, zoom_factor_bg; bg_tex = lw_texture_create_bg (bg); fg_tex = lw_texture_create_fg (fg); if (bg_tex && fg_tex) { bg_w = bg_tex->w; bg_h = bg_tex->h; fg_w = fg_tex->w; fg_h = fg_tex->h; result = lw_map_create_bicolor (num, 1, 0, network, random, min_w, min_h, &zoom_factor); if (result) { w = result->w; h = result->h; zoom_factor_fg = zoom_factor_bg = zoom_factor; if (w != zoom_factor * fg_tex->w || h != zoom_factor * fg_tex->h) { zoom_factor_fg = 1; } if (w != zoom_factor * bg_tex->w || h != zoom_factor * bg_tex->h) { zoom_factor_bg = 1; } for (y = 0; y < h; ++y) for (x = 0; x < w; ++x) { if (getpixel (result, x, y)) { color = getpixel (fg_tex, (x / zoom_factor_fg) % fg_w, (y / zoom_factor_bg) % fg_h); } else { color = getpixel (bg_tex, (x / zoom_factor_bg) % bg_w, (y / zoom_factor_bg) % bg_h); } putpixel (result, x, y, color); } } } if (bg_tex) destroy_bitmap (bg_tex); if (fg_tex) destroy_bitmap (fg_tex); return result; } /*------------------------------------------------------------------*/ char * lw_map_get_system_name (int num, int network, int random) { static char result[LW_MAP_SYSTEM_NAME_SIZE + 1]; void *raw_map; char *char_ptr; raw_map = lw_map_get_safe (num, network, random); char_ptr = (char *) raw_map; LW_MACRO_MEMSET0 (result); memcpy (result, char_ptr + 8, LW_MAP_SYSTEM_NAME_SIZE); result[sizeof (result) - 1] = '\0'; return result; } /*------------------------------------------------------------------*/ char * lw_map_get_readable_name (int num, int network, int random) { static char result[LW_MAP_READABLE_NAME_SIZE + 1]; void *raw_map; char *char_ptr; raw_map = lw_map_get_safe (num, network, random); char_ptr = (char *) raw_map; LW_MACRO_MEMSET0 (result); memcpy (result, char_ptr + 8 + LW_MAP_SYSTEM_NAME_SIZE, LW_MAP_READABLE_NAME_SIZE); result[sizeof (result) - 1] = '\0'; return result; } /*------------------------------------------------------------------*/ void lw_map_get_res (int num, int *w, int *h, int network, int random, int min_w, int min_h) { void *raw_map; short res_w, res_h; int zoom_factor; raw_map = lw_map_get_safe (num, network, random); lw_serial_get_map_header (raw_map, NULL, &res_w, &res_h); zoom_factor = calc_zoom_factor (res_w, res_h, min_w, min_h); *w = res_w * zoom_factor; *h = res_h * zoom_factor; }