Crossfire Client, Branch  R11627
map.c
Go to the documentation of this file.
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 }