1 /* SCCS Id: @(#)gnmap.c 3.3 2000/07/16 */
2 /* Copyright (C) 1998 by Erik Andersen <andersee@debian.org> */
3 /* Copyright (C) 1998 by Anthony Taylor <tonyt@ptialaska.net> */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "gnmap.h"
7 #include "gnglyph.h"
8 #include "gnsignal.h"
9 #include "hack.h"
10
11 #ifndef ROWNO
12 #define ROWNO 21
13 #define COLNO 80
14 #endif
15
16 /* globals static to this file go here */
17 struct {
18 GnomeCanvas *canvas;
19 GnomeCanvasImage *map[(ROWNO + 1) * COLNO];
20 GnomeCanvasImage *overlay[(ROWNO + 1) * COLNO];
21 double zoom;
22 GtkWidget *frame;
23 } ghack_map;
24
25 static GdkImlibImage *background;
26 static GdkImlibImage *petmark;
27 static GnomeCanvasGroup *myCanvasGroup;
28
29 /* static function declarations -- local to this file go here */
30 void ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data);
31 void ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data);
32 void ghack_map_print_glyph( GtkObject *win, guint x, guint y, GdkImlibImage *im, gpointer data);
33 void ghack_map_clear( GtkWidget *win, gpointer data);
34 static void ghack_map_display( GtkWidget *win, boolean block, gpointer data);
35 static void ghack_map_cliparound( GtkWidget *win, int x, int y, gpointer data);
36 static void ghack_map_window_zoom( GtkAdjustment *adj, gpointer data);
37
38
39 /* The following XPM is the artwork of Warwick Allison
40 * <warwick@troll.no>. It has been borrowed from
41 * the most excellent NetHackQt, until such time as
42 * we can come up with something better.
43 *
44 * More information about NetHackQt can be had from:
45 * http://www.troll.no/~warwick/nethack/
46 */
47
48 /* XPM */
49 static char *pet_mark_xpm[] = {
50 /* width height ncolors chars_per_pixel */
51 "8 7 2 1",
52 /* colors */
53 ". c None",
54 " c #FF0000",
55 /* pixels */
56 "........",
57 ".. . .",
58 ". ",
59 ". ",
60 ".. .",
61 "... ..",
62 ".... ..."
63 };
64
65
66 /* NAME:
67 * ghack_init_map_window( )
68 *
69 * ARGUMENTS:
70 * NONE
71 *
72 * RETURNS:
73 * GtkWidget*
74 *
75 * PURPOSE:
76 * Create the basic map necessities. Create a canvas;
77 * give it a background. Attach all the right signals
78 * to all the right places. Generally prepare the map
79 * to behave properly.
80 */
81
82 GtkWidget*
ghack_init_map_window()83 ghack_init_map_window ( )
84 {
85 GtkWidget *vbox;
86 GtkWidget *hbox;
87 GtkWidget *table;
88 GtkWidget *frame;
89 GtkWidget *w;
90 GtkWidget *hSeparator;
91 GtkAdjustment *adj;
92 GnomeCanvasImage *bg;
93 double width, height, x, y;
94 int i;
95
96 width = COLNO * ghack_glyph_width();
97 height = ROWNO * ghack_glyph_height();
98
99 vbox = gtk_vbox_new (FALSE, 4);
100 gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
101 gtk_widget_show (vbox);
102
103 /* Add in a horiz seperator */
104 hSeparator = gtk_hseparator_new ();
105 gtk_box_pack_start (GTK_BOX (vbox), hSeparator, FALSE, FALSE, 2);
106 gtk_widget_show ( hSeparator);
107
108 hbox = gtk_hbox_new (FALSE, 4);
109 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
110 gtk_widget_show (hbox);
111
112 /* Create the Zoom spinbutton.
113 */
114 ghack_map.zoom = 1.0;
115 w = gtk_label_new ("Zoom:");
116 gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
117 gtk_widget_show (w);
118 adj = GTK_ADJUSTMENT (gtk_adjustment_new (1.00, 0.5, 3.00, 0.05, 0.50, 0.50));
119 w = gtk_spin_button_new (adj, 0.5, 2);
120 gtk_widget_set_usize (w, 50, 0);
121 gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
122 gtk_widget_show (w);
123
124 /* Canvas and scrollbars
125 */
126 gtk_widget_push_visual (gdk_imlib_get_visual ());
127 gtk_widget_push_colormap (gdk_imlib_get_colormap ());
128 ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new());
129 //gtk_widget_push_visual(gdk_rgb_get_visual());
130 //gtk_widget_push_colormap(gdk_rgb_get_cmap());
131 //ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new_aa());
132
133 gtk_widget_pop_colormap();
134 gtk_widget_pop_visual();
135 gtk_widget_show (GTK_WIDGET(ghack_map.canvas));
136
137 table = gtk_table_new (2, 2, FALSE);
138 gtk_table_set_row_spacings (GTK_TABLE (table), 4);
139 gtk_table_set_col_spacings (GTK_TABLE (table), 4);
140 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
141 gtk_widget_show (table);
142
143 frame = gtk_frame_new (NULL);
144 ghack_map.frame = frame;
145 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
146 gtk_table_attach (GTK_TABLE (table), frame,
147 0, 1, 0, 1,
148 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
149 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
150 0, 0);
151 gtk_widget_show (frame);
152
153 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET(ghack_map.canvas));
154 gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0,
155 width+2*ghack_glyph_width(), height+2*ghack_glyph_height());
156
157 gnome_canvas_set_pixels_per_unit (GNOME_CANVAS(ghack_map.canvas), 1.0);
158
159 w = gtk_hscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->hadjustment);
160 gtk_table_attach (GTK_TABLE (table), w,
161 0, 1, 1, 2,
162 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
163 GTK_FILL,
164 0, 0);
165 gtk_widget_show (w);
166
167 w = gtk_vscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->vadjustment);
168 gtk_table_attach (GTK_TABLE (table), w,
169 1, 2, 0, 1, GTK_FILL,
170 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
171 0, 0);
172 gtk_widget_show (w);
173
174 myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new (
175 gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)),
176 gnome_canvas_group_get_type (),
177 "x", 0.0,
178 "y", 0.0,
179 NULL) );
180
181 /* Tile the map background with a pretty image */
182 background = gdk_imlib_load_image((char *) "mapbg.xpm");
183 gdk_imlib_render( background, background->rgb_width,
184 background->rgb_height);
185 if (background == NULL) {
186 g_warning("Bummer! Failed to load the map background image (mapbg.xpm)!");
187 }
188 else {
189 /* Tile the map background */
190 for (y = 0; y < height+background->rgb_height; y+=background->rgb_height)
191 {
192 for (x = 0; x < width+background->rgb_width; x+=background->rgb_width)
193 {
194 bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new (
195 myCanvasGroup, gnome_canvas_image_get_type (),
196 "x", (double) x,
197 "y", (double) y,
198 "width", (double) background->rgb_width,
199 "height", (double) background->rgb_height,
200 "image", background,
201 "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER,
202 NULL) );
203 gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg));
204 }
205 }
206 }
207
208 /* ghack_map.map is an array of canvas images. Each cell of
209 * the array will contain one tile. Here, we create the
210 * space for the cells and then create the cells for easy
211 * access later.
212 */
213 for (i=0, y = 0; y < height; y+=ghack_glyph_height())
214 {
215 for (x = 0; x < width; x+=ghack_glyph_width())
216 {
217 ghack_map.map[i++] = GNOME_CANVAS_IMAGE(
218 gnome_canvas_item_new (
219 myCanvasGroup,
220 gnome_canvas_image_get_type (),
221 "x", (double) x,
222 "y", (double) y,
223 "width", (double) ghack_glyph_height(),
224 "height", (double) ghack_glyph_width(),
225 "anchor", GTK_ANCHOR_NORTH_WEST,
226 NULL) );
227 }
228 }
229
230 /* Set up the pet mark image */
231 petmark = gdk_imlib_create_image_from_xpm_data( pet_mark_xpm);
232 gdk_imlib_render( petmark, petmark->rgb_width,
233 petmark->rgb_height);
234 if (petmark == NULL) {
235 g_warning("Bummer! Failed to load the pet_mark image!");
236 }
237 else {
238 /* ghack_map.overlay is an array of canvas images used to
239 * overlay tile images...
240 */
241 for (i=0, y = 0; y < height; y+=ghack_glyph_height())
242 {
243 for (x = 0; x < width; x+=ghack_glyph_width())
244 {
245 ghack_map.overlay[i] = GNOME_CANVAS_IMAGE(
246 gnome_canvas_item_new (
247 myCanvasGroup,
248 gnome_canvas_image_get_type (),
249 "x", (double) x,
250 "y", (double) y,
251 "width", (double) petmark->rgb_width,
252 "height", (double) petmark->rgb_height,
253 "image", petmark,
254 "anchor", GTK_ANCHOR_NORTH_WEST,
255 NULL) );
256 gnome_canvas_item_lower_to_bottom (
257 GNOME_CANVAS_ITEM( ghack_map.overlay[i++]));
258 }
259 }
260 }
261
262 /* Resize the canvas when the spinbutton changes
263 */
264 gtk_signal_connect (GTK_OBJECT (adj),
265 "value_changed",
266 (GtkSignalFunc) ghack_map_window_zoom,
267 ghack_map.canvas);
268
269 /* Game signals
270 */
271 gtk_signal_connect (GTK_OBJECT (vbox),
272 "ghack_curs",
273 GTK_SIGNAL_FUNC (ghack_map_cursor_to),
274 NULL);
275 gtk_signal_connect (GTK_OBJECT (vbox),
276 "ghack_putstr",
277 GTK_SIGNAL_FUNC (ghack_map_putstr),
278 NULL);
279 gtk_signal_connect (GTK_OBJECT (vbox),
280 "ghack_print_glyph",
281 GTK_SIGNAL_FUNC (ghack_map_print_glyph),
282 NULL);
283 gtk_signal_connect (GTK_OBJECT (vbox),
284 "ghack_clear",
285 GTK_SIGNAL_FUNC (ghack_map_clear),
286 NULL);
287 gtk_signal_connect (GTK_OBJECT (vbox),
288 "ghack_display",
289 GTK_SIGNAL_FUNC (ghack_map_display),
290 NULL);
291 gtk_signal_connect (GTK_OBJECT (vbox),
292 "ghack_cliparound",
293 GTK_SIGNAL_FUNC (ghack_map_cliparound),
294 NULL);
295 gtk_signal_connect (GTK_OBJECT (ghack_map.canvas),
296 "button_press_event",
297 GTK_SIGNAL_FUNC (ghack_handle_button_press),
298 NULL);
299 gtk_signal_connect(GTK_OBJECT (ghack_map.canvas),
300 "gnome_delay_output",
301 GTK_SIGNAL_FUNC(ghack_delay),
302 NULL);
303
304 return GTK_WIDGET(vbox);
305 }
306
307
308 /* NAME:
309 * ghack_map_window_zoom
310 *
311 * ARGUMENTS:
312 * double zoom -- The zoom factor
313 *
314 * RETURNS:
315 * Nothing.
316 *
317 * PURPOSE:
318 * Zoom the map image in and out. This should allow the user to
319 * dynamically scale the map. Ideally, the background should
320 * *NOT* scale, but this may be impractical.
321 */
322
323 static void
ghack_map_window_zoom(GtkAdjustment * adj,gpointer data)324 ghack_map_window_zoom( GtkAdjustment *adj, gpointer data)
325 {
326 if ( adj->value > 3.0 )
327 adj->value = 3.0;
328 if ( adj->value < 0.5 )
329 adj->value = 0.5;
330 ghack_map.zoom = adj->value;
331 gnome_canvas_set_pixels_per_unit (data, adj->value);
332 }
333
334
335
336 void
ghack_map_cursor_to(GtkWidget * win,int x,int y,gpointer data)337 ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data)
338 {
339 GnomeCanvasGroup *group;
340 static GnomeCanvasRE *cursor = NULL;
341
342 double x1, y1, x2, y2;
343
344 x1 = x * ghack_glyph_width() - 1;
345 y1 = y * ghack_glyph_height() - 1;
346 x2 = x1 + ghack_glyph_width() + 2;
347 y2 = y1 + ghack_glyph_height() + 2;
348
349
350 group = gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas));
351
352 if (!cursor) {
353 cursor = GNOME_CANVAS_RE (gnome_canvas_item_new (group,
354 gnome_canvas_rect_get_type (),
355 "outline_color", "antiquewhite2",
356 "width_units", 1.0, NULL));
357 }
358 gnome_canvas_item_set (GNOME_CANVAS_ITEM (cursor),
359 "x1", x1,
360 "y1", y1,
361 "x2", x2,
362 "y2", y2,
363 NULL);
364
365 gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( cursor));
366 gnome_canvas_item_show( GNOME_CANVAS_ITEM(cursor));
367 }
368
369
370 void
ghack_map_putstr(GtkWidget * win,int attr,const char * text,gpointer data)371 ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data)
372 {
373 g_warning("Fixme!!! ghack_map_putstr is not implemented");
374 }
375
376 /* NAME:
377 * ghack_map_print_glyph( )
378 *
379 * ARGUMENTS:
380 * XCHAR_P x, y -- The coordinates where which to print the glyph
381 * GdkImlibImage* glyph -- The glyph image to print
382 *
383 * RETURNS:
384 * Nothing.
385 *
386 * PURPOSE:
387 * Draw the glyph-tile at the specified coordinates.
388 */
389
390 void
ghack_map_print_glyph(GtkObject * win,guint x,guint y,GdkImlibImage * im,gpointer data)391 ghack_map_print_glyph( GtkObject *win,
392 guint x,
393 guint y,
394 GdkImlibImage *im,
395 gpointer data)
396 {
397 GnomeCanvasGroup *group;
398 int i = y * COLNO + x;
399 int glyph = glyph_at(x,y);
400 GnomeCanvasImage *canvas_image = GNOME_CANVAS_IMAGE( ghack_map.map[i]);
401
402 group = gnome_canvas_root (GNOME_CANVAS (ghack_map.canvas));
403
404 gnome_canvas_item_set (GNOME_CANVAS_ITEM ( canvas_image),
405 "image", im, NULL);
406 gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image));
407
408 canvas_image = GNOME_CANVAS_IMAGE( ghack_map.overlay[i]);
409
410 if (x==u.ux && y==u.uy)
411 ghack_map_cliparound(NULL, x, y, NULL);
412
413 if (glyph_is_pet(glyph)
414 #ifdef TEXTCOLOR
415 && iflags.hilite_pet
416 #endif
417 ) {
418 gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( canvas_image));
419 gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image));
420 }
421 else {
422 gnome_canvas_item_hide( GNOME_CANVAS_ITEM( canvas_image));
423 }
424 }
425
426
427 /* NAME:
428 * ghack_map_clear( )
429 *
430 * ARGUMENTS:
431 * NONE
432 *
433 * RETURNS:
434 * Nothing.
435 *
436 * PURPOSE:
437 * Clear the map by hiding all the map tiles.
438 */
439
440 void
ghack_map_clear(GtkWidget * win,gpointer data)441 ghack_map_clear( GtkWidget *win, gpointer data)
442 {
443 int i;
444
445 for (i = 0; i < ROWNO * COLNO; i++)
446 {
447 if (GNOME_IS_CANVAS_IMAGE(ghack_map.map[i]))
448 {
449 gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.map[i]));
450 }
451 if (GNOME_IS_CANVAS_IMAGE(ghack_map.overlay[i]))
452 {
453 gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.overlay[i]));
454 }
455 }
456 gnome_canvas_update_now ( GNOME_CANVAS(ghack_map.canvas));
457 }
458
459
460 void
ghack_map_display(GtkWidget * win,boolean block,gpointer data)461 ghack_map_display( GtkWidget *win, boolean block, gpointer data)
462 {
463 gtk_widget_show_all( GTK_WIDGET(win));
464 }
465
466
467 void
ghack_map_cliparound(GtkWidget * win,int x,int y,gpointer data)468 ghack_map_cliparound( GtkWidget *win,
469 int x,
470 int y,
471 gpointer data)
472 {
473 int map_width, map_height;
474 int to_x, to_y;
475 int cur_x, cur_y;
476 int width, height, half_width, half_height;
477
478 x *= ghack_glyph_width() * ghack_map.zoom;
479 y *= ghack_glyph_height() * ghack_map.zoom;
480 map_width = COLNO * ghack_glyph_width() * ghack_map.zoom;
481 map_height = ROWNO * ghack_glyph_height() * ghack_map.zoom;
482
483 gdk_window_get_size( GTK_LAYOUT (ghack_map.canvas)->bin_window,
484 &width, &height);
485 gnome_canvas_get_scroll_offsets( ghack_map.canvas, &cur_x, &cur_y);
486
487 half_width = width * 0.5;
488 half_height = height * 0.5;
489
490 if ( ((x - cur_x) < (width * 0.25) ) || ( (x - cur_x) > (width * 0.75) ) ) {
491 to_x = ((x-half_width) > 0)? x - half_width : 0;
492 to_x = ((x+half_width) > map_width)? map_width - 2 * half_width : to_x;
493 }
494 else {
495 to_x = cur_x;
496 }
497
498 if ( ((y - cur_y) < (height * 0.25) ) || ( (y - cur_y) > (height * 0.75) ) ) {
499 to_y = ((y-half_height) > 0)? y - half_height : 0;
500 to_y = ((y+half_height) > map_height)? map_height - 2 * half_height : to_y;
501 }
502 else {
503 to_y = cur_y;
504 }
505
506 if (to_x != cur_x || to_y != cur_y)
507 gnome_canvas_scroll_to( ghack_map.canvas, to_x, to_y);
508 //gnome_canvas_update_now ( ghack_map.canvas);
509
510 }
511
512
513
514 void
ghack_reinit_map_window()515 ghack_reinit_map_window ( )
516 {
517 GnomeCanvasImage *bg;
518 double width, height, x, y;
519 int i;
520
521 /* ghack_map_clear(NULL, NULL); */
522
523 width = COLNO * ghack_glyph_width();
524 height = ROWNO * ghack_glyph_height();
525
526 gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0,
527 width+2*ghack_glyph_width(), height+2*ghack_glyph_height());
528
529 /* remove everything currently in the canvas map */
530 gtk_object_destroy( GTK_OBJECT (myCanvasGroup));
531
532 /* Put some groups back */
533 myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new (
534 gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)),
535 gnome_canvas_group_get_type (),
536 "x", 0.0,
537 "y", 0.0,
538 NULL) );
539
540 /* Tile the map background with a pretty image */
541 if (background != NULL) {
542 /* Tile the map background */
543 for (y = 0; y < height+background->rgb_height; y+=background->rgb_height)
544 {
545 for (x = 0; x < width+background->rgb_width; x+=background->rgb_width)
546 {
547 bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new (
548 myCanvasGroup, gnome_canvas_image_get_type (),
549 "x", (double) x,
550 "y", (double) y,
551 "width", (double) background->rgb_width,
552 "height", (double) background->rgb_height,
553 "image", background,
554 "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER,
555 NULL) );
556 gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg));
557 }
558 }
559 }
560
561 /* ghack_map.map is an array of canvas images. Each cell of
562 * the array will contain one tile. Here, we create the
563 * space for the cells and then create the cells for easy
564 * access later.
565 */
566 for (i=0, y = 0; y < height; y+=ghack_glyph_height()) {
567 for (x = 0; x < width; x+=ghack_glyph_width()) {
568 ghack_map.map[i++] = GNOME_CANVAS_IMAGE(
569 gnome_canvas_item_new (
570 myCanvasGroup,
571 gnome_canvas_image_get_type (),
572 "x", (double) x,
573 "y", (double) y,
574 "width", (double) ghack_glyph_height(),
575 "height", (double) ghack_glyph_width(),
576 "anchor", GTK_ANCHOR_NORTH_WEST,
577 NULL) );
578 }
579 }
580
581 if (petmark != NULL) {
582 /* ghack_map.overlay is an array of canvas images used to
583 * overlay tile images...
584 */
585 for (i=0, y = 0; y < height; y+=ghack_glyph_height()) {
586 for (x = 0; x < width; x+=ghack_glyph_width()) {
587 ghack_map.overlay[i] = GNOME_CANVAS_IMAGE(
588 gnome_canvas_item_new (
589 myCanvasGroup,
590 gnome_canvas_image_get_type (),
591 "x", (double) x,
592 "y", (double) y,
593 "width", (double) petmark->rgb_width,
594 "height", (double) petmark->rgb_height,
595 "image", petmark,
596 "anchor", GTK_ANCHOR_NORTH_WEST,
597 NULL) );
598 gnome_canvas_item_lower_to_bottom (
599 GNOME_CANVAS_ITEM( ghack_map.overlay[i++]));
600 }
601 }
602 }
603
604 ghack_map_cliparound(NULL, u.ux, u.uy, NULL);
605 ghack_map_cursor_to(NULL, u.ux, u.uy, NULL);
606 gnome_canvas_update_now ( ghack_map.canvas);
607 doredraw();
608 }
609
610
611