Crossfire Client, Trunk  R20507
map.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, see the
9  * 'LICENSE' and 'COPYING' files.
10  *
11  * The authors can be reached via e-mail to crossfire-devel@real-time.com
12  */
13 
20 #include "client.h"
21 
22 #include <gtk/gtk.h>
23 
24 #include "image.h"
25 #include "main.h"
26 #include "mapdata.h"
27 #include "gtk2proto.h"
28 
29 static gboolean map_updated = FALSE;
30 
31 // Declarations for local event-handling functions.
32 static gboolean map_button_event(GtkWidget *widget,
33  GdkEventButton *event, gpointer user_data);
34 static gboolean map_expose_event(GtkWidget *widget,
35  GdkEventExpose *event, gpointer user_data);
36 
38 
41 
42 static GtkWidget *map_drawing_area;
43 
44 GtkWidget *map_notebook;
45 
46 /*
47  * This should really be one of the CONFIG values, or perhaps a checkbox
48  * someplace that displays frame rate.
49  */
50 gboolean time_map_redraw = FALSE;
51 
55 static void map_check_resize() {
56  GtkAllocation size;
57  gtk_widget_get_allocation(map_drawing_area, &size);
58  int w = size.width / map_image_size + 1;
59  int h = size.height / map_image_size + 1;
60  w = (w > MAP_MAX_SIZE) ? MAP_MAX_SIZE : w;
61  h = (h > MAP_MAX_SIZE) ? MAP_MAX_SIZE : h;
62 
66  client_mapsize(w, h);
67  }
68 }
69 
75 void map_init(GtkWidget *window_root) {
76  map_drawing_area = GTK_WIDGET(gtk_builder_get_object(
77  window_xml, "drawingarea_map"));
78  map_notebook = GTK_WIDGET(gtk_builder_get_object(
79  window_xml, "map_notebook"));
80 
81  g_signal_connect(map_drawing_area, "configure_event",
82  G_CALLBACK(map_check_resize), NULL);
83  g_signal_connect(map_drawing_area, "expose_event",
84  G_CALLBACK(map_expose_event), NULL);
85 
86  // Enable event masks and set callbacks to handle mouse events.
87  gtk_widget_add_events(map_drawing_area,
88  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
89  g_signal_connect(map_drawing_area, "event",
90  G_CALLBACK(map_button_event), NULL);
91 
92  // Set map size based on window size and show widget.
94  gtk_widget_show(map_drawing_area);
95 
96  switch (use_config[CONFIG_DISPLAYMODE]) {
97 #ifdef HAVE_SDL
98  case CFG_DM_SDL:
100  break;
101 #endif
102 
103 #ifdef HAVE_OPENGL
104  case CFG_DM_OPENGL:
106  break;
107 #endif
108  }
109 }
110 
116 static void draw_pixmap(cairo_t *cr, PixmapInfo *pixmap, int ax, int ay) {
117  cairo_set_source_surface(cr, pixmap->map_image,
118  ax * map_image_size, ay * map_image_size);
119  cairo_paint(cr);
120 }
121 
122 int display_mapscroll(int dx, int dy) {
123 #ifdef HAVE_SDL
125  return sdl_mapscroll(dx,dy);
126  } else
127 #endif
128  return 0;
129 }
130 
141 static void drawsmooth(cairo_t *cr, int mx, int my, int layer, int picx, int picy) {
142  static int dx[8]= {0,1,1,1,0,-1,-1,-1};
143  static int dy[8]= {-1,-1,0,1,1,1,0,-1};
144  static int bweights[8]= {2,0,4,0,8,0,1,0};
145  static int cweights[8]= {0,2,0,4,0,8,0,1};
146  static int bc_exclude[8]= {
147  1+2,/*north exclude northwest (bit0) and northeast(bit1)*/
148  0,
149  2+4,/*east exclude northeast and southeast*/
150  0,
151  4+8,/*and so on*/
152  0,
153  8+1,
154  0
155  };
156  int partdone[8]= {0,0,0,0,0,0,0,0};
157  int slevels[8];
158  int sfaces[8];
159  int i,weight,weightC;
160  int emx,emy;
161  int smoothface;
162  int hasFace = 0;
163  for (i=0; i<=layer; i++) {
164  hasFace |= mapdata_cell(mx, my)->heads[i].face;
165  }
166  if (!hasFace || !mapdata_can_smooth(mx, my, layer)) {
167  return;
168  }
169  for (i=0; i<8; i++) {
170  emx=mx+dx[i];
171  emy=my+dy[i];
172  if (!mapdata_contains(emx, emy)) {
173  slevels[i]=0;
174  sfaces[i]=0; /*black picture*/
175  } else if (mapdata_cell(emx, emy)->smooth[layer] <= mapdata_cell(mx, my)->smooth[layer]) {
176  slevels[i]=0;
177  sfaces[i]=0; /*black picture*/
178  } else {
179  slevels[i]=mapdata_cell(emx, emy)->smooth[layer];
180  sfaces[i]=pixmaps[mapdata_cell(emx, emy)->heads[layer].face]->smooth_face;
181  }
182  }
183  /*
184  * Now we have a list of smoothlevel higher than current square. There are
185  * at most 8 different levels. so... check 8 times for the lowest one (we
186  * draw from bottom to top!).
187  */
188  while (1) {
189  int lowest = -1;
190  for (i=0; i<8; i++) {
191  if ( (slevels[i]>0) && (!partdone[i]) &&
192  ((lowest<0) || (slevels[i]<slevels[lowest]))
193  ) {
194  lowest=i;
195  }
196  }
197  if (lowest<0) {
198  break; /*no more smooth to do on this square*/
199  }
200  /*printf ("hey, must smooth something...%d\n",sfaces[lowest]);*/
201  /* Here we know 'what' to smooth
202  *
203  * Calculate the weight for border and weight for corners. Then
204  * 'markdone' the corresponding squares
205  *
206  * First, the border, which may exclude some corners
207  */
208  weight=0;
209  weightC=15; /*works in backward. remove where there is nothing*/
210  /*for (i=0;i<8;i++)
211  cornermask[i]=1;*/
212  for (i=0; i<8; i++) { /*check all nearby squares*/
213  if ( (slevels[i]==slevels[lowest]) &&
214  (sfaces[i]==sfaces[lowest])) {
215  partdone[i]=1;
216  weight=weight+bweights[i];
217  weightC&=~bc_exclude[i];
218  } else {
219  /*must rmove the weight of a corner if not in smoothing*/
220  weightC&=~cweights[i];
221  }
222  }
223  /*We can't do this before since we need the partdone to be adjusted*/
224  if (sfaces[lowest]<=0) {
225  continue; /*Can't smooth black*/
226  }
227  smoothface=sfaces[lowest];
228  if (smoothface<=0) {
229  continue; /*picture for smoothing not yet available*/
230  }
231  /*
232  * now, it's quite easy. We must draw using a 32x32 part of the picture
233  * smoothface. This part is located using the 2 weights calculated:
234  * (32*weight,0) and (32*weightC,32)
235  */
236  if ( (!pixmaps[smoothface]->map_image) ||
237  (pixmaps[smoothface] == pixmaps[0])) {
238  continue; /*don't have the picture associated*/
239  }
240 
241  // @todo Fix smoothing here.
242  if (weight > 0) {
243  draw_pixmap(cr, pixmaps[smoothface], picx, picy);
244 
245  /*
246  draw_pixmap(
247  weight*map_image_size, 0,
248  picx, picy,
249  picx-weight*map_image_size, picy,
250  pixmaps[smoothface]->map_mask, pixmaps[smoothface]->map_image, map_image_size, map_image_size);
251  */
252  }
253 
254  if (weightC > 0) {
255  draw_pixmap(cr, pixmaps[smoothface], picx, picy);
256 
257  /*
258  draw_pixmap(
259  weightC*map_image_size, map_image_size,
260  picx, picy,
261  picx-weightC*map_image_size, picy+map_image_size,
262  pixmaps[smoothface]->map_mask, pixmaps[smoothface]->map_image, map_image_size, map_image_size);
263  */
264  }
265  }
266 }
267 
271 static void map_draw_layer(cairo_t *cr, int layer) {
272  for (int x = 0; x < use_config[CONFIG_MAPWIDTH]; x++) {
273  for (int y = 0; y < use_config[CONFIG_MAPHEIGHT]; y++) {
274  // Translate on-screen coordinates to virtual map coordinates.
275  int mx = pl_pos.x + x, my = pl_pos.y + y;
276 
277  // Skip current cell if not visible and not using fog of war.
278  if (!use_config[CONFIG_FOGWAR] && mapdata_cell(mx, my)->cleared) {
279  continue;
280  }
281 
282  int dx, dy, face = mapdata_face_info(mx, my, layer, &dx, &dy);
283  if (face > 0 && pixmaps[face]->map_image != NULL) {
284  draw_pixmap(cr, pixmaps[face], x + dx, y + dy);
285  }
286  /*
287  * Sometimes, it may happens we need to draw the smooth while there
288  * is nothing to draw at that layer (but there was something at
289  * lower layers). This is handled here. The else part is to take
290  * into account cases where the smooth as already been handled 2
291  * code lines before
292  */
293  if (use_config[CONFIG_SMOOTH]) {
294  drawsmooth(cr, mx, my, layer, x * map_image_size, y * map_image_size);
295  }
296  }
297  }
298 }
299 
303 static void mapcell_draw_darkness(cairo_t *cr, int ax, int ay, int mx, int my) {
304  cairo_rectangle(cr, ax * map_image_size, ay * map_image_size,
305  map_image_size, map_image_size);
306 
307  double opacity = mapdata_cell(mx, my)->darkness / 192.0 * 0.6;
308 
309  if (use_config[CONFIG_FOGWAR] && mapdata_cell(mx, my)->cleared) {
310  opacity += 0.15;
311  }
312 
313  cairo_set_source_rgba(cr, 0, 0, 0, opacity);
314  cairo_fill(cr);
315 }
316 
320 static void gtk_map_redraw(gboolean redraw) {
321  if (!redraw && !map_updated) {
322  return;
323  }
324 
325  GtkAllocation size;
326  gtk_widget_get_allocation(map_drawing_area, &size);
327  const int width = size.width;
328  const int height = size.height;
329 
330  // Create double buffer and associated graphics context.
331  cairo_surface_t *cst =
332  cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
333  cairo_t *cr = cairo_create(cst);
334 
335  // Blank graphics context with a solid black background.
336  cairo_set_source_rgb(cr, 0, 0, 0);
337  cairo_rectangle(cr, 0, 0, width, height);
338  cairo_fill(cr);
339 
340  for (int layer = 0; layer < MAXLAYERS; layer++) {
341  map_draw_layer(cr, layer);
342  }
343 
344  for (int x = 0; x < use_config[CONFIG_MAPWIDTH]; x++) {
345  for (int y = 0; y < use_config[CONFIG_MAPHEIGHT]; y++) {
346  // Determine the 'virtual' map coordinates.
347  int mx = pl_pos.x + x, my = pl_pos.y + y;
348  mapcell_draw_darkness(cr, x, y, mx, my);
349  mapdata_cell(mx, my)->need_update = 0;
350  mapdata_cell(mx, my)->need_resmooth = 0;
351  }
352  }
353 
354  cairo_destroy(cr);
355 
356  // Copy the double buffer on the map drawing area.
357  cairo_t *map_cr = gdk_cairo_create(gtk_widget_get_window(map_drawing_area));
358  cairo_set_source_surface(map_cr, cst, 0, 0);
359  cairo_paint(map_cr);
360  cairo_destroy(map_cr);
361 
362  cairo_surface_destroy(cst);
363 }
364 
371 void resize_map_window(int x, int y) {
372  /* We do an implicit clear, since after a resize, there may be some
373  * left over pixels at the edge which will not get drawn on by map spaces.
374  */
375  gdk_window_clear(gtk_widget_get_window(map_drawing_area));
376  draw_map(TRUE);
377 }
378 
383 void draw_map(gboolean redraw) {
384  gint64 t_start, t_end;
385 
386  if (time_map_redraw) {
387  t_start = g_get_monotonic_time();
388  }
389 
390  switch (use_config[CONFIG_DISPLAYMODE]) {
391 #ifdef HAVE_SDL
392  case CFG_DM_SDL:
393  sdl_gen_map(redraw);
394  break;
395 #endif
396 
397 #ifdef HAVE_OPENGL
398  case CFG_DM_OPENGL:
399  opengl_gen_map(redraw);
400  break;
401 #endif
402 
403  default:
404  gtk_map_redraw(redraw);
405  break;
406  }
407 
408  if (time_map_redraw) {
409  t_end = g_get_monotonic_time();
410  gint64 elapsed = t_end - t_start;
411  printf("%"G_GINT64_FORMAT"\n", elapsed);
412  }
413 }
414 
415 static gboolean map_expose_event(GtkWidget *widget, GdkEventExpose *event,
416  gpointer user_data) {
417  draw_map(TRUE);
418  return FALSE;
419 }
420 
427 static int relative_direction(int dx, int dy) {
428  if (dx == 0 && dy == 0) {
429  return 0;
430  } else if (dx == 0 && dy < 0) {
431  return 1;
432  } else if (dx > 0 && dy < 0) {
433  return 2;
434  } else if (dx > 0 && dy == 0) {
435  return 3;
436  } else if (dx > 0 && dy > 0) {
437  return 4;
438  } else if (dx == 0 && dy > 0) {
439  return 5;
440  } else if (dx < 0 && dy > 0) {
441  return 6;
442  } else if (dx < 0 && dy == 0) {
443  return 7;
444  } else if (dx < 0 && dy < 0) {
445  return 8;
446  } else {
447  g_assert_not_reached();
448  }
449 }
450 
454 static gboolean map_button_event(GtkWidget *widget,
455  GdkEventButton *event, gpointer user_data) {
456  // Determine the tile of the mouse event, relative to the player.
457  int dx = ((int)event->x - 2) / map_image_size - (use_config[CONFIG_MAPWIDTH] / 2);
458  int dy = ((int)event->y - 2) / map_image_size - (use_config[CONFIG_MAPHEIGHT] / 2);
459  int dir = relative_direction(dx, dy);
460 
461  switch (event->button) {
462  case 1:
463  if (event->type == GDK_BUTTON_PRESS) {
464  look_at(dx,dy);
465  }
466  break;
467  case 2:
468  if (event->type == GDK_BUTTON_RELEASE) {
469  clear_fire();
470  } else {
471  fire_dir(dir);
472  }
473  break;
474  case 3:
475  if (event->type == GDK_BUTTON_RELEASE) {
476  stop_run();
477  } else {
478  run_dir(dir);
479  }
480  break;
481  }
482 
483  return FALSE;
484 }
485 
493 void display_map_doneupdate(int redraw, int notice) {
494  map_updated |= redraw || !notice;
495 }
guint32 width
Definition: image.c:42
void resize_map_window(int x, int y)
Resize_map_window is a NOOP for the time being - not sure if it will in fact need to do something...
Definition: map.c:371
GtkBuilder * window_xml
Definition: main.c:86
gint16 use_config[CONFIG_NUMS]
Definition: init.c:40
#define DEFAULT_IMAGE_SIZE
Definition: image.h:40
void run_dir(int dir)
Definition: player.c:133
#define CONFIG_DISPLAYMODE
Definition: client.h:194
void sdl_gen_map(int redraw)
struct MapCell * mapdata_cell(int x, int y)
Get the stored map cell at the given map coordinate.
Definition: mapdata.c:119
#define MAP_MAX_SIZE
Map size the client will request the map to be.
Definition: client.h:475
int map_image_size
Definition: map.c:39
static void drawsmooth(cairo_t *cr, int mx, int my, int layer, int picx, int picy)
Draw anything in adjacent squares that could smooth on given square.
Definition: map.c:141
guint16 smooth[MAXLAYERS]
Definition: mapdata.h:53
static void map_check_resize()
Calculate and set desired map size based on map window size.
Definition: map.c:55
gboolean time_map_redraw
Definition: map.c:50
GtkWidget * map_notebook
Definition: map.c:44
static gboolean map_button_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Handle a mouse event in the drawing area.
Definition: map.c:454
void draw_map(gboolean redraw)
Draw the map window using the appropriate backend.
Definition: map.c:383
void * map_image
Definition: image.h:51
Contains various global definitions and XML file name and path defaults.
void look_at(int x, int y)
Definition: player.c:65
static gboolean redraw(gpointer data)
Redraw the map.
Definition: main.c:128
guint16 smooth_face
A face used for smoothing with this face.
Definition: image.h:54
Pixmap data.
GtkWidget * window_root
In main.c.
Definition: main.c:87
bool mapdata_can_smooth(int x, int y, int layer)
Definition: mapdata.c:134
int sdl_mapscroll(int dx, int dy)
void clear_fire()
Definition: player.c:98
guint32 height
Definition: image.c:42
#define CONFIG_MAPHEIGHT
Definition: client.h:204
guint8 need_resmooth
Definition: mapdata.h:57
gint16 mapdata_face_info(int mx, int my, int layer, int *dx, int *dy)
Return the face number of the pixmap in the given map cell and set the offset pointers to indicate wh...
Definition: mapdata.c:1021
void display_map_doneupdate(int redraw, int notice)
This is called after the map has been all digested.
Definition: map.c:493
gint16 want_config[CONFIG_NUMS]
Definition: init.c:40
static gboolean map_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
Definition: map.c:415
bool mapdata_contains(int x, int y)
Determine whether the map data contains the given cell.
Definition: mapdata.c:126
static void gtk_map_redraw(gboolean redraw)
Redraw the entire map using GTK.
Definition: map.c:320
void init_opengl(GtkWidget *drawingarea)
#define MAXLAYERS
The protocol supports 10 layers, so set MAXLAYERS accordingly.
Definition: mapdata.h:6
guint8 need_update
Definition: mapdata.h:55
static void map_draw_layer(cairo_t *cr, int layer)
Draw a single map layer to the given cairo context.
Definition: map.c:271
#define CFG_DM_SDL
Definition: client.h:240
#define CONFIG_SMOOTH
Definition: client.h:210
void opengl_gen_map(int redraw)
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: image.c:47
int display_mapscroll(int dx, int dy)
Definition: map.c:122
PlayerPosition pl_pos
Definition: map.c:37
Includes various dependencies header files needed by most everything.
void init_SDL(GtkWidget *sdl_window, int just_lightmap)
static gboolean map_updated
Definition: map.c:29
static void draw_pixmap(cairo_t *cr, PixmapInfo *pixmap, int ax, int ay)
Draw a pixmap to the given map tile on screen.
Definition: map.c:116
static int relative_direction(int dx, int dy)
Given a relative tile coordinate, determine its compass direction.
Definition: map.c:427
gint16 face
Definition: mapdata.h:19
static GtkWidget * map_drawing_area
Definition: map.c:42
struct MapCellLayer heads[MAXLAYERS]
Definition: mapdata.h:51
#define CFG_DM_OPENGL
Definition: client.h:241
#define CONFIG_FOGWAR
Definition: client.h:190
void stop_run()
Definition: player.c:128
static void mapcell_draw_darkness(cairo_t *cr, int ax, int ay, int mx, int my)
Draw darkness layer to a location on screen.
Definition: map.c:303
int map_image_half_size
Definition: map.c:40
void map_init(GtkWidget *window_root)
This initializes the stuff we need for the map.
Definition: map.c:75
guint8 darkness
Definition: mapdata.h:54
void fire_dir(int dir)
Definition: player.c:112
#define CONFIG_MAPWIDTH
Definition: client.h:203
void client_mapsize(int width, int height)
Ask the server for the given map size.
Definition: client.c:170