Crossfire Client, Branch
R11627
|
00001 const char * const rcsid_gtk_map_c = 00002 "$Id: map.c 9218 2008-06-03 13:45:46Z anmaster $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2001 Mark Wedel & Crossfire Development Team 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 The author can be reached via e-mail to crossfire-devel@real-time.com 00023 */ 00024 00025 /* This file handles the map related code - both in terms of allocation, 00026 * insertion of new objects, and actual rendering (although the 00027 * sdl rendering is in the sdl file 00028 */ 00029 00030 #include <config.h> 00031 #include <stdlib.h> 00032 #include <sys/stat.h> 00033 #ifndef WIN32 00034 #include <unistd.h> 00035 #endif 00036 #include <png.h> 00037 00038 /* Pick up the gtk headers we need */ 00039 #include <gtk/gtk.h> 00040 #ifndef WIN32 00041 #include <gdk/gdkx.h> 00042 #else 00043 #include <time.h> 00044 #include <gdk/gdkwin32.h> 00045 #endif 00046 #include <gdk/gdkkeysyms.h> 00047 00048 #include "client-types.h" 00049 #include "gx11.h" 00050 #include "client.h" 00051 #include "gtkproto.h" 00052 #include "mapdata.h" 00053 00054 00055 /* Start of map handling code. 00056 * For the most part, this actually is not window system specific, 00057 * but certainly how the client wants to store this may vary. 00058 */ 00059 00060 extern struct Map the_map; 00061 00062 /* 00063 * Added for fog of war. Current size of the map structure in memory. 00064 * We assume a rectangular map so this is the length of one side. 00065 * command.c needs to know about this so not static 00066 * FIX ME: Don't assume rectangle 00067 */ 00068 00069 PlayerPosition pl_pos; 00070 00076 void reset_map(void) 00077 { 00078 } 00079 00080 static void draw_pixmap(int srcx, int srcy, int dstx, int dsty, int clipx, int clipy, 00081 void *mask, void *image, int sizex, int sizey) 00082 { 00083 gdk_gc_set_clip_mask(mapgc, mask); 00084 gdk_gc_set_clip_origin(mapgc, clipx, clipy); 00085 gdk_draw_pixmap(mapwindow, mapgc, image, srcx, srcy, dstx, dsty, sizex, sizey); 00086 } 00087 00088 int display_mapscroll(int dx, int dy) 00089 { 00090 #ifdef HAVE_SDL 00091 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) 00092 return sdl_mapscroll(dx,dy); 00093 else 00094 #endif 00095 return 0; 00096 } 00097 00098 /* Draw anything in adjacent squares that could smooth on given square 00099 * mx,my square to smooth on. you should not call this function to 00100 * smooth on a 'completly black' square. 00101 * layer layer to examine (we smooth only one layer at a time) 00102 * picx,picy place on the mapwindow to draw 00103 */ 00104 void drawsmooth (int mx,int my,int layer,int picx,int picy){ 00105 static int dx[8]={0,1,1,1,0,-1,-1,-1}; 00106 static int dy[8]={-1,-1,0,1,1,1,0,-1}; 00107 static int bweights[8]={2,0,4,0,8,0,1,0}; 00108 static int cweights[8]={0,2,0,4,0,8,0,1}; 00109 static int bc_exclude[8]={ 00110 1+2,/*north exclude northwest (bit0) and northeast(bit1)*/ 00111 0, 00112 2+4,/*east exclude northeast and southeast*/ 00113 0, 00114 4+8,/*and so on*/ 00115 0, 00116 8+1, 00117 0 00118 }; 00119 int partdone[8]={0,0,0,0,0,0,0,0}; 00120 int slevels[8]; 00121 int sfaces[8]; 00122 int i,lowest,weight,weightC; 00123 int emx,emy; 00124 int smoothface; 00125 int hasFace = 0; 00126 for (i=0;i<=layer;i++) 00127 hasFace |= the_map.cells[mx][my].heads[i].face; 00128 if (!hasFace 00129 || !CAN_SMOOTH(the_map.cells[mx][my], layer)) { 00130 return; 00131 } 00132 00133 for (i=0;i<8;i++){ 00134 emx=mx+dx[i]; 00135 emy=my+dy[i]; 00136 if ( (emx<0) || (emy<0) || (the_map.x<=emx) || (the_map.y<=emy)){ 00137 slevels[i]=0; 00138 sfaces[i]=0; /*black picture*/ 00139 } 00140 else if (the_map.cells[emx][emy].smooth[layer]<=the_map.cells[mx][my].smooth[layer]){ 00141 slevels[i]=0; 00142 sfaces[i]=0; /*black picture*/ 00143 }else{ 00144 slevels[i]=the_map.cells[emx][emy].smooth[layer]; 00145 sfaces[i]=pixmaps[the_map.cells[emx][emy].heads[layer].face]->smooth_face; 00146 } 00147 } 00148 /* ok, now we have a list of smoothlevel higher than current square. 00149 * there are at most 8 different levels. so... let's check 8 times 00150 * for the lowest one (we draw from botto to top!). 00151 */ 00152 lowest=-1; 00153 while (1){ 00154 lowest = -1; 00155 for (i=0;i<8;i++){ 00156 if ( (slevels[i]>0) && (!partdone[i]) && 00157 ((lowest<0) || (slevels[i]<slevels[lowest])) 00158 ) 00159 lowest=i; 00160 } 00161 if (lowest<0) 00162 break; /*no more smooth to do on this square*/ 00163 /*printf ("hey, must smooth something...%d\n",sfaces[lowest]);*/ 00164 /*here we know 'what' to smooth*/ 00165 /* we need to calculate the weight 00166 * for border and weight for corners. 00167 * then we 'markdone' 00168 * the corresponding squares 00169 */ 00170 /*first, the border, which may exclude some corners*/ 00171 weight=0; 00172 weightC=15; /*works in backward. remove where there is nothing*/ 00173 /*for (i=0;i<8;i++) 00174 cornermask[i]=1;*/ 00175 for (i=0;i<8;i++){ /*check all nearby squares*/ 00176 if ( (slevels[i]==slevels[lowest]) && 00177 (sfaces[i]==sfaces[lowest])){ 00178 partdone[i]=1; 00179 weight=weight+bweights[i]; 00180 weightC&=~bc_exclude[i]; 00181 }else{ 00182 /*must rmove the weight of a corner if not in smoothing*/ 00183 weightC&=~cweights[i]; 00184 } 00185 00186 } 00187 /*We can't do this before since we need the partdone to be adjusted*/ 00188 if (sfaces[lowest]<=0) 00189 continue; /*Can't smooth black*/ 00190 smoothface=sfaces[lowest]; 00191 if (smoothface<=0){ 00192 continue; /*picture for smoothing not yet available*/ 00193 } 00194 /* now, it's quite easy. We must draw using a 32x32 part of 00195 * the picture smoothface. 00196 * This part is located using the 2 weights calculated: 00197 * (32*weight,0) and (32*weightC,32) 00198 */ 00199 if ( (!pixmaps[smoothface]->map_image) || 00200 (pixmaps[smoothface] == pixmaps[0])) 00201 continue; /*don't have the picture associated*/ 00202 if (weight>0){ 00203 draw_pixmap( 00204 weight*map_image_size, 0, 00205 picx, picy, 00206 picx-weight*map_image_size, picy, 00207 pixmaps[smoothface]->map_mask, pixmaps[smoothface]->map_image, map_image_size, map_image_size); 00208 } 00209 if (weightC>0){ 00210 draw_pixmap( 00211 weightC*map_image_size, map_image_size, 00212 picx, picy, 00213 picx-weightC*map_image_size, picy-map_image_size, 00214 pixmaps[smoothface]->map_mask, pixmaps[smoothface]->map_image, map_image_size, map_image_size); 00215 } 00216 }/*while there's some smooth to do*/ 00217 } 00218 00219 static void display_mapcell(int ax, int ay, int mx, int my) 00220 { 00221 int layer; 00222 int face; 00223 00224 /* First, we need to black out this space. */ 00225 for (layer=0; layer<MAXLAYERS; layer++) { 00226 face = mapdata_face(ax, ay, layer); 00227 if ((face > 0) && (!pixmaps[face]->map_mask)) 00228 break; 00229 } 00230 /* Only draw rectangle if all faces have transparency */ 00231 if (layer==MAXLAYERS) 00232 gdk_draw_rectangle(mapwindow, drawingarea->style->black_gc, TRUE, ax*map_image_size, ay*map_image_size, map_image_size, map_image_size); 00233 00234 00235 /* now draw the different layers. Only draw if using fog of war or the 00236 * space isn't clear. 00237 */ 00238 if (use_config[CONFIG_FOGWAR] || !the_map.cells[mx][my].cleared) { 00239 for (layer=0; layer<MAXLAYERS; layer++) { 00240 int sx, sy; 00241 00242 /* draw single-tile faces first */ 00243 face = mapdata_face(ax, ay, layer); 00244 if (face > 0 && pixmaps[face]->map_image != NULL) { 00245 int w = pixmaps[face]->map_width; 00246 int h = pixmaps[face]->map_height; 00247 draw_pixmap( 00248 w-map_image_size, h-map_image_size, 00249 ax*map_image_size, ay*map_image_size, 00250 ax*map_image_size+map_image_size-w, ay*map_image_size+map_image_size-h, 00251 pixmaps[face]->map_mask, pixmaps[face]->map_image, map_image_size, map_image_size); 00252 } 00253 /* 00254 * Sometimes, it may happens we need to draw the smooth while there 00255 * is nothing to draw at that layer (but there was something at 00256 * lower layers). This is handled here. The else part is to take 00257 * into account cases where the smooth as already been handled 2 00258 * code lines before 00259 */ 00260 if ( use_config[CONFIG_SMOOTH]) 00261 drawsmooth (mx, my, layer, ax*map_image_size, ay*map_image_size); 00262 00263 /* draw big faces last (should overlap other objects) */ 00264 face = mapdata_bigface(ax, ay, layer, &sx, &sy); 00265 if (face > 0 && pixmaps[face]->map_image != NULL) { 00266 /* This is pretty messy, because images are not required to be 00267 * an integral multiplier of the image size. There 00268 * are really 4 main variables: 00269 * source[xy]: From where within the pixmap to start grabbing pixels. 00270 * off[xy]: Offset from space edge on the visible map to start drawing pixels. 00271 * off[xy] also determines how many pixels to draw (map_image_size - off[xy]) 00272 * clip[xy]: Position of the clipmask. The position of the clipmask is always 00273 * at the upper left of the image as we drawn it on the map, so for any 00274 * given big image, it will have the same values for all the pieces. However 00275 * we need to re-construct that location based on current location. 00276 * 00277 * For a 32x72 image, it would be drawn like follows: 00278 * sourcey offy 00279 * top space: 0 24 00280 * middle space: 8 0 00281 * bottom space: 40 0 00282 */ 00283 int dx, dy, sourcex, sourcey, offx, offy, clipx, clipy; 00284 00285 dx = pixmaps[face]->map_width % map_image_size; 00286 offx = dx?(map_image_size -dx):0; 00287 clipx = (ax - sx)*map_image_size + offx; 00288 00289 if (sx) { 00290 sourcex = sx * map_image_size - offx ; 00291 offx=0; 00292 } else { 00293 sourcex=0; 00294 } 00295 00296 dy = pixmaps[face]->map_height % map_image_size; 00297 offy = dy?(map_image_size -dy):0; 00298 clipy = (ay - sy)*map_image_size + offy; 00299 00300 if (sy) { 00301 sourcey = sy * map_image_size - offy; 00302 offy=0; 00303 } else { 00304 sourcey=0; 00305 } 00306 00307 draw_pixmap( 00308 sourcex, sourcey, 00309 ax*map_image_size+offx, ay*map_image_size + offy, 00310 clipx, clipy, 00311 pixmaps[face]->map_mask, pixmaps[face]->map_image, 00312 map_image_size - offx, map_image_size - offy); 00313 } 00314 } /* else for processing the layers */ 00315 } 00316 00317 /* If this is a fog cell, do darknening of the space. 00318 * otherwise, process light/darkness - only do those if not a 00319 * fog cell. 00320 */ 00321 if (use_config[CONFIG_FOGWAR] && the_map.cells[mx][my].cleared) { 00322 draw_pixmap(0, 0, ax*map_image_size, ay*map_image_size, ax*map_image_size, ay*map_image_size, dark1, dark, map_image_size, map_image_size); 00323 } 00324 else if (the_map.cells[mx][my].darkness > 192) { /* Full dark */ 00325 gdk_draw_rectangle (mapwindow, drawingarea->style->black_gc, 00326 TRUE,map_image_size*ax, map_image_size*ay, 00327 map_image_size, map_image_size); 00328 } else if (the_map.cells[mx][my].darkness> 128) { 00329 draw_pixmap(0, 0, ax*map_image_size, ay*map_image_size, ax*map_image_size, ay*map_image_size, dark1, dark, map_image_size, map_image_size); 00330 } else if (the_map.cells[mx][my].darkness> 64) { 00331 draw_pixmap(0, 0, ax*map_image_size, ay*map_image_size, ax*map_image_size, ay*map_image_size, dark2, dark, map_image_size, map_image_size); 00332 } else if (the_map.cells[mx][my].darkness> 1) { 00333 draw_pixmap(0, 0, ax*map_image_size, ay*map_image_size, ax*map_image_size, ay*map_image_size, dark3, dark, map_image_size, map_image_size); 00334 } 00335 } 00336 00337 void gtk_draw_map(int redraw) { 00338 int mx, my; 00339 int x, y; 00340 struct timeval tv1, tv2,tv3; 00341 long elapsed1, elapsed2; 00342 00343 if (time_map_redraw) 00344 gettimeofday(&tv1, NULL); 00345 00346 for(x = 0; x < use_config[CONFIG_MAPWIDTH]; x++) { 00347 for(y = 0; y < use_config[CONFIG_MAPHEIGHT]; y++) { 00348 /* mx,my represent the spaces on the 'virtual' map (ie, the_map structure). 00349 * x and y (from the for loop) represent the visable screen. 00350 */ 00351 mx = pl_pos.x+x; 00352 my = pl_pos.y+y; 00353 if (redraw 00354 || the_map.cells[mx][my].need_update 00355 || the_map.cells[mx][my].need_resmooth) { 00356 display_mapcell(x, y, mx, my); 00357 the_map.cells[mx][my].need_update=0; 00358 the_map.cells[mx][my].need_resmooth=0; 00359 } 00360 } 00361 } 00362 00363 if (time_map_redraw) 00364 gettimeofday(&tv2, NULL); 00365 00366 gdk_draw_pixmap(drawingarea->window, drawingarea->style->black_gc, mapwindow, 00367 0, 0, 0, 0, use_config[CONFIG_MAPWIDTH] * map_image_size, use_config[CONFIG_MAPHEIGHT] * map_image_size); 00368 if (time_map_redraw) { 00369 gettimeofday(&tv3, NULL); 00370 elapsed1 = (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec); 00371 elapsed2 = (tv3.tv_sec - tv2.tv_sec)*1000000 + (tv3.tv_usec - tv2.tv_usec); 00372 00373 /* I care about performance for 'long' updates, so put the check in to make 00374 * these a little more noticable */ 00375 if ((elapsed1 + elapsed2)>10000) 00376 LOG(LOG_INFO,"gtk::sdl_gen_map","gen took %7ld, flip took %7ld, total = %7ld", 00377 elapsed1, elapsed2, elapsed1 + elapsed2); 00378 } 00379 }