/*
  $Id: gtkmap.c,v 1.36.2.3 2004/11/12 21:57:31 j_ali Exp $
 */
/*
  GTK+ NetHack Copyright (c) Issei Numata 1999-2000
               Copyright (c) Slash'EM Development Team 2000-2004
  GTK+ NetHack may be freely redistributed.  See license for details. 
*/

#include <sys/types.h>
#include <signal.h>
#include "winGTK.h"
#include "dlb.h"
#ifdef SHORT_FILENAMES
#include "patchlev.h"
#else
#include "patchlevel.h"
#endif
#include "decl.h"
#include "proxycb.h"
#include "prxyclnt.h"
#include "gtkprogress.h"

#undef red
#undef green
#undef blue

/*
  if map_click is true, we do gtk_main_quit() when clicking map
 */
static int map_click;
static int map_update;

static GtkWidget *map;
static GdkFont *map_font;
static gchar *map_font_name;
static unsigned char *map_xoffsets;		/* For character mode only */
static unsigned int map_font_width;		/* Maximum width */
static enum xshm_map_mode map_mode;
#ifdef GTK_PROXY
int no_glyph;
static short *map_glyph2colsym = (short *)0;
#endif

static GdkGC *map_color_gc[N_NH_COLORS];

static gint map_button_event(void *map, GdkEventButton *event, gpointer data);
static void nh_map_cliparound(int x, int y, gboolean exact);
static void nh_map_redraw();
static int configure_map(GtkWidget *w, gpointer data);

#ifdef	WINGTK_RADAR
static int use_radar = TRUE;
static int radar_is_created;

static GtkWidget *radar;
static GtkWidget *radar_darea;
/*
 * we use 2 pixmaps for radar.
 *
 * radar_pixmap keeps radar dot
 * radar_pixmap2 keeps radar dot and frame rectangle
 */
static GdkPixmap *radar_pixmap;
static GdkPixmap *radar_pixmap2;
extern GtkAccelGroup *accel_group;

#define RADAR_MONSTER	CLR_YELLOW
#define RADAR_HUMAN	MAP_WHITE
#define RADAR_PET	CLR_GREEN
#define RADAR_OBJECT	CLR_BLUE

#define RADAR_WALL	CLR_GRAY
#define RADAR_FLOOR	MAP_DARK_GREEN
#define RADAR_DOOR	CLR_ORANGE
#define RADAR_LADDER	CLR_MAGENTA
#define RADAR_WATER	CLR_BLUE
#define RADAR_TRAP	CLR_RED
#define RADAR_SWALLOW	CLR_RED
#define RADAR_ICE	CLR_GRAY
#define RADAR_LAVA	CLR_ORANGE
#define RADAR_BRIDGE	CLR_GRAY
#define RADAR_AIR	CLR_CYAN
#define RADAR_CLOUD	CLR_GRAY
#define RADAR_BEAM	CLR_YELLOW

static void		nh_radar_configure();
#endif	/* WINGTK_RADAR */

static GdkGC *map_gc;

/*
 *        BIG3DTILE
 *
 *                 +----------+
 * ^              /          /| 
 * |             /          / | 
 * |            /          /  | 
 * |           /          /   | 
 * *6         /          /    |
 * |         +----------+     +
 * |       ^ |          |    / ^
 * |       | |          |   /  |
 * |      *2 |          |  /   *4
 * |       | |          | /    |
 * v       v |          |/     v
 *           +----------+      
 *            <-- *1 --> <-*3->
 *            <-- *5        -->
 *
 * *1 = 3D_WIDTH	= 32
 * *2 = 3D_HEIGHT 	= 32
 * *3 = 3D_OFSET	= 16
 * *4 = 3D_OFSETY	= 32
 * *5 = WIDTH		= 48
 * *6 = HEIGHT		= 64
 */

TileTab tileTab[MAXNOTILESETS+1] = {
    { "", "" },		/* dummy */
};
int no_tileTab = 0;	/* Not including dummy (index 0) */

static TileTab *Tile;
int map_visual = -1;
int map_clip_dist2 = 0;

/* from tile.c */
extern int tiles_per_row;
extern int tiles_per_col;

#ifdef WINGTK_RADAR
#define NH_RADAR_UNIT	4
#define NH_RADAR_WIDTH	(no_cols * NH_RADAR_UNIT)
#define NH_RADAR_HEIGHT	(no_rows * NH_RADAR_UNIT)
#endif

#ifdef GTK_PROXY
extern short *GTK_glyph2tile;
#define	glyph2tile GTK_glyph2tile
#else
extern short glyph2tile[];
#endif
#define GLYPH2TILE(g)	((g) == NO_GLYPH ? stone_tile : glyph2tile[g])

extern GtkWidget *main_window;

int cursx;
int cursy;
int cursm;

static int c_width;
static int c_height;

static int c_3dwidth;
static int c_3dheight;
static int c_3dofset;

static int c_map_width;
static int c_map_height;

#define TILEMAP_UPDATE	1

static int no_rows = -1, no_cols = -1, no_layers = -1;
static int tilemap_size = 0;	/* sizeof(struct tilemap) */

struct tilemap {
    unsigned int flags;
    int	glyphs[1];
};

static struct tilemap **gtkmap = NULL;

#define GTKMAP_PTR(ptr, col, size)	\
				((struct tilemap *)((char *)(ptr)+(col)*(size)))
#define GTKMAP(row, col)	GTKMAP_PTR(gtkmap[row], col, tilemap_size)

/*
 * fix tile number
 */
static int
fix_tile(int tile)
{
    return tile;
}

/*
 * (Re)configure the map for new dimensions (layers = -1 to free).
 */

static int
nh_conf_map_dimens(int rows, int cols, int layers, int preserve)
{
    int i, j, k;
    int glyph = cmap_to_glyph(S_stone);
    int retval = 0;
    int new_size = sizeof(struct tilemap) + (layers - 1) * sizeof(int);
    struct tilemap **new, *tm;
    if (rows == no_rows && cols == no_cols && layers == no_layers)
	return 0;
    if (layers >= 0) {
	/* Allocate new map */
	new = malloc(rows * sizeof(struct tilemap *));
	if (!new) {
	    retval = -1;
	    goto out;
	}
	for(j = 0; j < rows; j++) {
	    new[j] = malloc(cols * new_size);
	    if (!new[j]) {
		retval = -1;
		for(j--; j >= 0; j--)
		    free(new[j]);
		free(new);
		goto out;
	    }
	}
	/* Copy data from old map to new and initialize new areas */
	if (gtkmap && preserve)
	    for(j = 0; j < min(rows, no_rows); j++) {
		if (gtkmap[j])
		    for(i = 0; i < min(cols, no_cols); i++) {
			tm = GTKMAP_PTR(new[j], i, new_size);
			tm->flags = GTKMAP(j, i)->flags;
			for(k = 0; k < min(layers, no_layers); k++)
			    tm->glyphs[k] = GTKMAP(j, i)->glyphs[k];
			for(; k < layers; k++)
			    tm->glyphs[k] = NO_GLYPH;
		    }
		else
		    i = 0;
		for(; i < cols; i++) {
		    tm = GTKMAP_PTR(new[j], i, new_size);
		    tm->flags = TILEMAP_UPDATE;
		    for(k = 0; k < layers; k++)
			tm->glyphs[k] = NO_GLYPH;
		}
	    }
	else
	    j = 0;
	for(; j < rows; j++)
	    for(i = 0; i < cols; i++) {
		tm = GTKMAP_PTR(new[j], i, new_size);
		tm->flags = TILEMAP_UPDATE;
		for(k = 0; k < layers; k++)
		    tm->glyphs[k] = NO_GLYPH;
	    }
	/* Release old map */
	if (gtkmap) {
	    for(j = 0; j < no_rows; j++)
		free(gtkmap[j]);
	    free(gtkmap);
	}
    }
out:
    if (layers < 0 || retval) {
	for(j = 0; j < rows; j++)
	    free(gtkmap[j]);
	gtkmap = NULL;
	no_layers = -1;
	no_rows = -1;
	no_cols = -1;
	tilemap_size = 0;
    } else {
	gtkmap = new;
	map_update = 1;
	no_layers = layers;
	no_rows = rows;
	no_cols = cols;
	tilemap_size = new_size;
	if (map_visual)
	    map = xshm_map_init(map_mode, c_map_width, c_map_height);
	if (GTK_WIDGET_REALIZED(map))
	    configure_map(map, 0);
    }
#ifdef WINGTK_RADAR
    nh_radar_configure();
#endif
    return retval;
}

static int
nh_conf_map_font(void)
{
    int i, j, min_width, width;
#ifdef GTK_PROXY
    int rgb, sym, best;
    double e, err = 0;
    int r, g, b;
    double d;
    double X, Y, Z, up, vp;	/* Tri-stimulus and CIE-1976 UCS values */
    double Ls, us, vs;
    struct { double L, up, vp; } nh_Luv[N_NH_COLORS];
    long *glyph2rgbsym;
    struct proxycb_get_glyph_mapping_res *glyph_map;
#endif
    if (!map_font) {
	if (map_font_name) {
	    PangoFontDescription *desc;
	    desc = pango_font_description_from_string(map_font_name);
	    map_font = gdk_font_from_description(desc);
	    pango_font_description_free(desc);
	    if (!map_font)
		g_warning("Can't load map font \"%s\"", map_font_name);
	}
	if (!map_font) {
	    g_return_val_if_fail(map->style != NULL, 1);
	    map_font = gtk_style_get_font(map->style);
	    g_return_val_if_fail(map_font != NULL, 1);
	    gdk_font_ref(map_font);
	    map_font_name =
	      pango_font_description_to_string(map->style->font_desc);
	}
    }

    /*
     * ALI
     * We might want to consider making the size of this array
     * variable in the future, but for now 8 bits is always enough.
     */
    map_xoffsets = (unsigned char *) alloc(256);
    for(i = 0; i < 256; i++)
	map_xoffsets[i] = 0;
#ifdef GTK_PROXY
    if (!map_glyph2colsym) {
	glyph_map = nh_proxy_cache_get_glyph_mapping(NULL);
	if (!glyph_map) {
	    pline("Cannot get glyph mapping.");
	    return 0;
	}
	glyph2rgbsym = proxy_map_glyph2char(glyph_map);
	no_glyph = glyph_map->no_glyph;
	proxy_cb_free_glyph_mapping(glyph_map);
	map_glyph2colsym = (short *)alloc(no_glyph * sizeof(short));
	for(i = 0; i < no_glyph; i++)
	    map_glyph2colsym[i] = -1;
	/*
	 * The algorithm for determining colour closeness used here is
	 * a sledgehammer to crack a nut. It is based on the CIE 1976
	 * colour difference value (delta-E*(L*u*v*)), but modified
	 * slightly to give much more weight to the chroma information.
	 * Normally one would simply use (r-r0)^2 + (g-g0)^2 + (b-b0)^2
	 * but this gives too much weight to lightness for our purposes
	 * and while one can conceive of a number of ways to modify it
	 * we really need to do some testing before using one of these.
	 * This algorithm will work, although it's a little slow.
	 *
	 * Note that the values in nh_colors are in 16-bit, but are
	 * specified as 8-bit * 257 so we can simply divide by 257
	 * to get 8-bit.
	 */
	for(i = 0; i < N_NH_COLORS; i++) {
	    r = nh_color[i].red/257;
	    g = nh_color[i].green/257;
	    b = nh_color[i].blue/257;
	    if (r | g | b) {
		X = 0.412453 * r + 0.357580 * g + 0.180423 * b;
		Y = 0.212671 * r + 0.715160 * g + 0.072169 * b;
		Z = 0.019334 * r + 0.119193 * g + 0.950227 * b;
		nh_Luv[i].L = Y;
		d = X + 15 * Y + 3 * Z;
		nh_Luv[i].up = 4 * X / d;
		nh_Luv[i].vp = 9 * Y / d;
	    } else {
		nh_Luv[i].L = 0.5;	/* Zero would imply infinite ratio */
		nh_Luv[i].up = nh_Luv[i].vp = 0;
	    }
	}
	for(i = 0; i < no_glyph; i++) {
	    if (map_glyph2colsym[i] == -1) {
		rgb = RGBSYM_RGB(glyph2rgbsym[i]);
		r = rgb>>16;
		g = (rgb>>8)&0xFF;
		b = rgb&0xFF;
		if (r | g | b) {
		    X = 0.412453 * r + 0.357580 * g + 0.180423 * b;
		    Y = 0.212671 * r + 0.715160 * g + 0.072169 * b;
		    Z = 0.019334 * r + 0.119193 * g + 0.950227 * b;
		    d = X + 15 * Y + 3 * Z;
		    up = 4 * X / d;
		    vp = 9 * Y / d;
		} else {
		    Y = 0.5;
		    up = vp = 0;
		}
		best = 0;
		for(j = 0; j < N_NH_COLORS; j++) {
		    Ls = 116 * pow(nh_Luv[j].L / Y, 1/3.) - 16;
		    if (nh_Luv[j].up || up) {
			/* These values are ten times the normal CIE 1976
			 * u* and v* values. (Chroma is much more important
			 * in NetHack than lightness.)
			 */
			us = 130 * Ls * (nh_Luv[j].up - up);
			vs = 130 * Ls * (nh_Luv[j].vp - vp);
		    } else
			us = vs = 0;
		    e = Ls * Ls + us * us + vs * vs;
#ifdef DEBUG
		    fprintf(stderr, "E[(%d,%d,%d), (%d,%d,%d)] = %lg\n",
		      nh_color[j].red/257, nh_color[j].green/257,
		      nh_color[j].blue/257, r, g, b, e);
#endif
		    if (!j || e < err) {
			best = j;
			err = e;
		    }
		}
		map_glyph2colsym[i] = best << 8 | RGBSYM_SYM(glyph2rgbsym[i]);
		for(j = i + 1; j < no_glyph; j++)
		    if (RGBSYM_RGB(glyph2rgbsym[j]) == rgb)
			map_glyph2colsym[j] =
			  best << 8 | RGBSYM_SYM(glyph2rgbsym[j]);
	    }
#ifdef DEBUG
	    fprintf(stderr,"Glyph %d (0x%lX) -> sym %d, colour %d\n",
	      i, glyph2rgbsym[i],
	      map_glyph2colsym[i] & 0xFF, map_glyph2colsym[i] >> 8);
#endif
	}
	free(glyph2rgbsym);
    }
    sym = map_glyph2colsym[0] & 0xFF;
    map_font_width = min_width = gdk_char_width_wc(map_font, (GdkWChar)sym);
    if (c_width > 0)
	map_xoffsets[sym] = c_width;
    for(i = 1; i < no_glyph; i++) {
	sym = map_glyph2colsym[i] & 0xFF;
	width = gdk_char_width_wc(map_font, (GdkWChar)sym);
	if (width > 0)
	    map_xoffsets[sym] = width;
	if (width < min_width)
	    min_width = width;
	if (width > map_font_width)
	    map_font_width = width;
    }
#else
    map_font_width = min_width =
      gdk_char_width_wc(map_font, (GdkWChar)oc_syms[0]);
    if (c_width > 0)
	map_xoffsets[oc_syms[0]] = c_width;
    for(i = 1; i < SIZE(oc_syms); i++) {
	width = gdk_char_width_wc(map_font, (GdkWChar)oc_syms[i]);
	if (width > 0)
	    map_xoffsets[oc_syms[i]] = width;
	if (width < min_width)
	    min_width = width;
	if (width > map_font_width)
	    map_font_width = width;
    }
    for(i = 0; i < SIZE(showsyms); i++) {
	width = gdk_char_width_wc(map_font, (GdkWChar)showsyms[i]);
	if (width > 0)
	    map_xoffsets[showsyms[i]] = width;
	if (width < min_width)
	    min_width = width;
	if (width > map_font_width)
	    map_font_width = width;
    }
    for(i = 0; i < SIZE(monsyms); i++) {
	width = gdk_char_width_wc(map_font, (GdkWChar)monsyms[i]);
	if (width > 0)
	    map_xoffsets[monsyms[i]] = width;
	if (width < min_width)
	    min_width = width;
	if (width > map_font_width)
	    map_font_width = width;
    }
    for(i = 0; i < SIZE(warnsyms); i++) {
	width = gdk_char_width_wc(map_font, (GdkWChar)warnsyms[i]);
	if (width > 0)
	    map_xoffsets[warnsyms[i]] = width;
	if (width < min_width)
	    min_width = width;
	if (width > map_font_width)
	    map_font_width = width;
    }
#endif /* GTK_PROXY */
    if (min_width <= 0)
	pline("Warning: Not all expected glyphs present in map font \"%s\".",
	  map_font_name);

    /* Convert widths to offsets */
    for(i = 0; i < 256; i++)
	if (map_xoffsets[i])
	    map_xoffsets[i] = (map_font_width - map_xoffsets[i]) / 2;
    return 1;
}

/*
 * -1:   deallocate
 * zero: character
 * else: various tiles
 */
int
nh_set_map_visual(int mode)
{
    static int setting_visual = FALSE;		/* Ignore recursive calls */
    int saved_vis = map_visual;
    gchar *buf;
    GdkCursor *cursor;
    GtkWidget *progress;

    if (setting_visual)
	return 0;
    setting_visual++;

    if (mode < -1 || mode > no_tileTab)
	panic("Bad visual!\n");

    if (saved_vis != mode) {
	if (map)
	    gtk_widget_hide(map);

	if (saved_vis > 0)
	    x_tile_destroy();
switch_mode:
	if (mode < 0) {
	    map_visual = mode;
	    setting_visual--;
	    return 1;
	} else if (mode != 0) {      /* mode 0 is handled in configure_map() */
	    buf = g_strdup_printf("Selecting %s", tileTab[mode].ident);
	    progress = nh_gtk_progress_window_new(buf, GTK_WINDOW(main_window));
	    g_free(buf);
	    if (tileTab[mode].ident[0]) {
		Tile = tileTab + mode;
		x_tile_init_add_stages(Tile, NH_GTK_PROGRESS_WINDOW(progress));
	    }
	    gtk_widget_realize(progress);
#if GTK_CHECK_VERSION(2,2,0)
	    cursor = gdk_cursor_new_for_display(
	      gdk_drawable_get_display(progress->window), GDK_WATCH);
#else
	    cursor = gdk_cursor_new(GDK_WATCH);
#endif
	    gdk_window_set_cursor(progress->window, cursor);
	    gdk_cursor_unref(cursor);
	    gtk_widget_show_now(progress);
	    if (!tileTab[mode].ident[0])
		map_mode = XSHM_MAP_NONE;
	    else {
		map_mode = x_tile_init(Tile, NH_GTK_PROGRESS_WINDOW(progress));
		while(gtk_events_pending())
		    gtk_main_iteration();
	    }

	    if (map_mode != XSHM_MAP_NONE) {
		c_width = Tile->unit_width;
		c_height = Tile->unit_height;
		c_3dwidth = Tile->unit_width - Tile->ofsetx_3d;
		c_3dheight = Tile->unit_height - Tile->ofsety_3d;
		c_3dofset = Tile->ofsetx_3d;
		c_map_width = c_3dwidth * (no_cols - 1) + c_3dofset * no_rows +
		  Tile->unit_width;
		c_map_height = c_3dheight * (no_rows - 1) + Tile->unit_height;
	    } else {
		if (saved_vis > 0 && tileTab[saved_vis].ident || !saved_vis) {
		    gtk_widget_destroy(progress);
		    pline("Warning: Switching back to %s.",
		      saved_vis?tileTab[saved_vis].ident:"character mode");
		    mode = saved_vis;
		    saved_vis = -1;
		    goto switch_mode;
		} else if (map_visual > 0) {
		    gtk_widget_destroy(progress);
		    panic("Failed to switch back to previous mode.");
		} else {
		    setting_visual--;
		    gtk_widget_destroy(progress);
		    return 0;
		}
	    }

	    map = xshm_map_init(map_mode, c_map_width, c_map_height);
	    while(gtk_events_pending())
		gtk_main_iteration();
	    gtk_widget_destroy(progress);
	}

	map_visual = mode;
	if (GTK_WIDGET_REALIZED(map))
	    configure_map(map, 0);
	nh_map_check_visibility();
#ifdef WINGTK_RADAR
	nh_radar_update();
#endif
	nh_option_cache_set("tileset", tileTab[map_visual].ident);
	nh_map_redraw();
	nh_map_flush();

	gtk_widget_show(map);
    }

    setting_visual--;
    return 1;
}

int
nh_get_map_visual(void)
{
    return map_visual;
}

int
nh_check_map_visual(int mode)
{
    if (mode < 0 || mode > no_tileTab || mode && !tileTab[mode].ident[0])
	return -1;
    else
	return 0;
}

int
nh_set_map_font(gchar *name)
{
    PangoFontDescription *desc;
    g_return_if_fail(name != NULL);
    if (map_font_name) {
	if (!strcmp(name, map_font_name))
	    return 0;
	else
	    g_free(map_font_name);
    }
    map_font_name = g_strdup(name);
    if (map_font) {
	gdk_font_unref(map_font);
	desc = pango_font_description_from_string(name);
	map_font = gdk_font_from_description(desc);
	pango_font_description_free(desc);
	if (map_visual >= 0) {
	    configure_map(map, 0);
	    nh_map_redraw();
	}
    }
    return 0;
}

gchar *
nh_get_map_font(void)
{
    if (map_font_name)
	return g_strdup(map_font_name);
    else if (map && map->style && map->style->font_desc)
	return pango_font_description_to_string(map->style->font_desc);
    else
	return NULL;
}

#ifdef WINGTK_RADAR
static gint
radar_expose_event(GtkWidget *widget, GdkEventExpose *event)
{
    gdk_draw_pixmap(widget->window,
      widget->style->fg_gc[GTK_WIDGET_STATE(widget)], radar_pixmap2,
      event->area.x, event->area.y, event->area.x, event->area.y,
      event->area.width, event->area.height);

    return FALSE;
}

static gint
radar_configure_event(GtkWidget *widget, GdkEventConfigure *event,
  gpointer data)
{
    /*
     * The drawing area has changed size (or is receiving its initial
     * size). This always happens under program control.
     * We need to (re-)create the pixmaps to match.
     */

    GdkPixmap *pixmap;
    GdkGC *gc;
    gint w, h;

    if (radar_pixmap2)
	gdk_pixmap_unref(radar_pixmap2);

    radar_pixmap2 = gdk_pixmap_new(widget->window,
      event->width, event->height, -1);
    gc = gdk_gc_new(radar_pixmap2);
    gdk_gc_set_foreground(gc, &nh_color[CLR_BLACK]);
    gdk_draw_rectangle(radar_pixmap2, gc, TRUE, 0, 0, event->width,
      event->height);
    gdk_gc_unref(gc);
    pixmap = gdk_pixmap_new(widget->window, event->width, event->height, -1);
    gc = gdk_gc_new(pixmap);
    gdk_gc_set_foreground(gc, &nh_color[CLR_BLACK]);
    gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, event->width, event->height);
    if (radar_pixmap) {
	gdk_drawable_get_size(GDK_DRAWABLE(radar_pixmap), &w, &h);
	if (w > event->width)
	    w = event->width;
	if (h > event->height)
	    h = event->height;
	gdk_draw_pixmap(pixmap, gc, radar_pixmap, 0, 0, 0, 0, w, h);
	gdk_pixmap_unref(radar_pixmap);
    }
    gdk_gc_unref(gc);
    radar_pixmap = pixmap;

    return FALSE;
}

static gint
radar_destroy_event(GtkWidget *widget, gpointer data)
{
    nh_radar_set_use(FALSE);

    return TRUE;
}
#endif	/* WINGTK_RADAR */

static gint
map_button_event(void *map, GdkEventButton *event, gpointer data)
{
    int x, y;
    y = event->y / c_3dheight;
    if (y < 0 || y >= no_rows)	/* Click outside area of map */
	return FALSE;
    x = (event->x - (no_rows - y) * c_3dofset ) / c_3dwidth;
    if (x < 0 || x >= no_cols)
	return FALSE;
    GTK_curs(NHW_MAP, x, y);
    if (event->button == 1)
	cursm = CLICK_1;
    else
	cursm = CLICK_2;

    if (map_click)
	quit_hook();
    return FALSE;
}

void
nh_map_check_visibility()
{
    nh_map_cliparound(cursx, cursy, FALSE);
}

void
nh_map_clear(int rows, int cols, int layers)
{
    int i, j, k;
    int glyph = cmap_to_glyph(S_stone);
    const char *tileset = nh_option_cache_get("tileset");

    nh_conf_map_dimens(rows, cols, layers, FALSE);

    /*
     * Check if tileset has changed and change map_visual if required.
     * This can happen if tileset changed via doset() and doredraw() was
     * called.  --ALI
     */
    if (strcmp(tileset, tileTab[map_visual].ident)) {
	int i;
	for(i = 0; i <= no_tileTab; i++)
	    if (!strcmp(tileset, tileTab[i].ident)) {
		nh_set_map_visual(i);
		break;
	    }
	if (i > no_tileTab) {
	    pline("Tileset %s not valid.", tileset);
	    nh_option_cache_set("tileset", tileTab[map_visual].ident);
	}
    }

    /* TODO: We should implement a method of quickly clearing large
     * portions of the map to a (repeating) tile to speed this up.
     */
    for(j = 0; j < rows; j++)
	for(i = 0; i < cols; i++)
	    for(k = 0; k < layers; k++)
		if (GTKMAP(j, i)->glyphs[k] != NO_GLYPH) {
		    GTKMAP(j, i)->glyphs[k] = NO_GLYPH;
		    GTKMAP(j, i)->flags = TILEMAP_UPDATE;  /* Reset all flags */
		    map_update = 1;
		}
}

void
nh_map_destroy()
{
    int i;
    nh_conf_map_dimens(-1, -1, -1, FALSE);
    nh_set_map_visual(-1);
    if (map_font) {
	gdk_font_unref(map_font);
	free(map_xoffsets);
    }
#ifdef GTK_PROXY
    if (map_glyph2colsym) {
	free(map_glyph2colsym);
	map_glyph2colsym = (short *)0;
    }
#endif
    gdk_gc_unref(map_gc);
    for (i = 0; i < N_NH_COLORS; i++)
	gdk_gc_unref(map_color_gc[i]);
    x_tile_destroy();
    xshm_map_destroy();
}

static int
configure_map(GtkWidget *w, gpointer data)
{
    int i;
    int width, height;
    guint path_length;
    gchar *path;

    /*
     * Configure for new font metrics
     */
    if (map_visual == 0) {
	nh_conf_map_font();
	c_width = map_font_width;
	c_height = map_font->ascent + map_font->descent;
	c_3dwidth = c_width;
	c_3dheight = c_height;
	c_3dofset = 0;

	c_map_width = no_cols * c_width;
	c_map_height = no_rows * c_height;
	w = map = xshm_map_init(XSHM_MAP_PIXMAP, c_map_width, c_map_height);
    }
    /*
     * set gc 
     */
    if (!map_gc)
	map_gc = gdk_gc_new(w->window);

#define COLOUR_IS_RGB(colour,r,g,b)   \
      ((colour).red==(r) && (colour).green==(g) && (colour).blue==(b))

    if (COLOUR_IS_RGB(w->style->bg[GTK_STATE_NORMAL], 0, 0, 0))
	nh_color[CLR_BLACK] = nh_color[MAP_WHITE];
    else if (COLOUR_IS_RGB(w->style->bg[GTK_STATE_NORMAL], 65535, 65535, 65535))
	nh_color[CLR_WHITE] = nh_color[MAP_BLACK];

#undef COLOUR_IS_RGB

    for(i = 0; i < N_NH_COLORS; i++) {
	if (!map_color_gc[i])
	    map_color_gc[i] = gdk_gc_new(w->window);
	gdk_gc_set_foreground(map_color_gc[i], &nh_color[i]);
	gdk_gc_set_background(map_color_gc[i], &w->style->bg[GTK_STATE_NORMAL]);
    }

    return FALSE;
}

GtkWidget *
nh_map_new(GtkWidget *w)
{
    int i;
    int width, height;
    int visual;

    visual = tile_scan();

    nh_conf_map_dimens(21, 80, 1, FALSE);
    map = xshm_map_init(XSHM_MAP_NONE, 0, 0);
    gtk_widget_set_name(map, "map");
    xshm_map_button_handler(GTK_SIGNAL_FUNC(map_button_event), NULL);
    gtk_signal_connect_after(GTK_OBJECT(map), "realize",
      GTK_SIGNAL_FUNC(configure_map), 0);

    if (!nh_set_map_visual(visual)) {
	for(i = 0; i <= no_tileTab; i++)
	    if (i != visual && nh_set_map_visual(i))
		break;
	if (i > no_tileTab)
	    panic("No valid map modes!");
    }

    nh_map_clear(no_rows, no_cols, no_layers);

    return map;
}

#ifdef WINGTK_RADAR

/*
 * create radar
 */

void
nh_radar_destroy()
{
    if (radar_pixmap)
	gdk_pixmap_unref(radar_pixmap);
    if (radar_pixmap2)
	gdk_pixmap_unref(radar_pixmap2);
    gtk_widget_destroy(radar);
}

GtkWidget *
nh_radar_new()
{
    radar = nh_session_window_new("radar");
#if GTK_CHECK_VERSION(1,3,12)
    gtk_window_add_accel_group(GTK_WINDOW(radar), accel_group);
#else
    gtk_accel_group_attach(accel_group, G_OBJECT(radar));
#endif

    radar_is_created = 1;

    gtk_window_set_title(GTK_WINDOW(radar), DEF_GAME_NAME " Radar");

    radar_darea = nh_gtk_new_and_add(gtk_drawing_area_new(), radar, "");

    gtk_signal_connect(GTK_OBJECT(radar_darea), "expose_event",
      GTK_SIGNAL_FUNC(radar_expose_event), NULL);

    gtk_signal_connect(GTK_OBJECT(radar_darea), "configure_event",
      GTK_SIGNAL_FUNC(radar_configure_event), NULL);

    gtk_signal_connect(GTK_OBJECT(radar), "delete_event",
      GTK_SIGNAL_FUNC(radar_destroy_event), 0);

    if (use_radar && NH_RADAR_WIDTH > 0 && NH_RADAR_HEIGHT > 0) {
	gtk_drawing_area_size(GTK_DRAWING_AREA(radar_darea),
	  NH_RADAR_WIDTH, NH_RADAR_HEIGHT);
	gtk_widget_show(radar);
    }

    return radar;
}

static void
nh_radar_configure()
{
    if (NH_RADAR_WIDTH > 0 && NH_RADAR_HEIGHT > 0) {
	gtk_drawing_area_size(GTK_DRAWING_AREA(radar_darea),
	  NH_RADAR_WIDTH, NH_RADAR_HEIGHT);
	nh_radar_update();
	if (use_radar)
	    gtk_widget_show(radar);
    } else {
	if (radar_pixmap)
	    gdk_pixmap_unref(radar_pixmap);
	if (radar_pixmap2)
	    gdk_pixmap_unref(radar_pixmap2);
	radar_pixmap = NULL;
	radar_pixmap2 = NULL;
    }
}

void
nh_print_radar(int x, int y, struct tilemap *tmap)
{
    int k, glyph;
    int c;
    if (!radar_pixmap)
	return;

    glyph = cmap_to_glyph(S_stone);
    for(k = 0; k < no_layers; k++)
	if (tmap->glyphs[k] != NO_GLYPH) {
	    glyph = tmap->glyphs[k];
	    break;
	}

    c = CLR_BLACK;
    if (glyph < PM_ARCHEOLOGIST)
	c = RADAR_MONSTER;
    else if (glyph_is_monster(glyph))
	c = RADAR_HUMAN;
    else if (glyph_is_pet(glyph))
	c = RADAR_PET;
    else if (glyph_is_object(glyph))
	c = RADAR_OBJECT;
    else if (glyph_is_trap(glyph))
	c = RADAR_TRAP;
    else if (glyph_is_swallow(glyph))
	c = RADAR_SWALLOW;
    else if( glyph_is_cmap(glyph)) {
	if (glyph == GLYPH_CMAP_OFF)
	    ;
	else if (glyph <= GLYPH_CMAP_OFF + S_trwall)
	    c = RADAR_WALL;
	else if (glyph <= GLYPH_CMAP_OFF + S_hcdoor)
	    c = RADAR_DOOR;
	else if (glyph <= GLYPH_CMAP_OFF + S_litcorr)
	    c = RADAR_FLOOR;
	else if (glyph <= GLYPH_CMAP_OFF + S_dnladder)
	    c = RADAR_LADDER;
	else if (glyph <= GLYPH_CMAP_OFF + S_pool)
	    c = RADAR_WATER;
	else if (glyph <= GLYPH_CMAP_OFF + S_ice)
	    c = RADAR_ICE;	/* fountain sink ice */
	else if (glyph <= GLYPH_CMAP_OFF + S_lava)
	    c = RADAR_LAVA;	/* lava */
	else if (glyph <= GLYPH_CMAP_OFF + S_hcdbridge)
	    c = RADAR_BRIDGE;	/* bridge */
	else if (glyph <= GLYPH_CMAP_OFF + S_air)
	    c = RADAR_AIR;	/* air */
	else if (glyph <= GLYPH_CMAP_OFF + S_cloud)
	    c = RADAR_CLOUD;	/* cloud */
	else if (glyph <= GLYPH_CMAP_OFF + S_water)
	    c = RADAR_WATER;	/* water */
	else if (glyph < GLYPH_CMAP_OFF + S_explode9)
	    c = RADAR_BEAM;	/* beam */
    }
    else
	c = RADAR_WALL;

    gdk_draw_rectangle(radar_pixmap, map_color_gc[c], TRUE, 
      x * NH_RADAR_UNIT, y * NH_RADAR_UNIT, NH_RADAR_UNIT, NH_RADAR_UNIT);
}
#endif	/* WINGTK_RADAR */

#ifndef GTK_PROXY
#ifdef TEXTCOLOR
#define zap_color(n)	(zapcolors[n])
#define cmap_color(n)	(defsyms[n].color)
#define obj_color(n)	(objects[n].oc_color)
#define mon_color(n)	(mons[n].mcolor)
#define pet_color(n)	(mons[n].mcolor)
#else
#define zap_color(n)	(n)
#define cmap_color(n)	(n)
#define obj_color(n)	(n)
#define mon_color(n)	(n)
#define pet_color(n)	(n)
#endif
#endif	/* GTK_PROXY */

static void
nh_map_print_glyph_traditional(XCHAR_P x, XCHAR_P y, struct tilemap *tmap)
{
    static GdkRectangle update_rect;
    int color;
    int k, glyph;
#ifndef GTK_PROXY
    int offset;
#endif
    GdkWChar ch[2];

    color = 0;
    glyph = cmap_to_glyph(S_stone);
    for(k = 0; k < no_layers; k++)
	if (tmap->glyphs[k] != NO_GLYPH) {
	    glyph = tmap->glyphs[k];
	    break;
	}

#ifdef GTK_PROXY
    ch[0] = map_glyph2colsym[glyph] & 0xFF;
    color = map_glyph2colsym[glyph] >> 8 & 0xFF;
#else
    if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {	/* swallow */
	ch[0] = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
	color = mon_color(offset>>3);
    } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
	ch[0] = showsyms[S_vbeam + (offset & 0x3)];
	color = zap_color(offset>>2);
    } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) {/* cmap */
	ch[0] = showsyms[offset];
	color = cmap_color(offset);
    } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {	/* object */
	ch[0] = oc_syms[(int)objects[offset].oc_class];
	color = obj_color(offset);
    } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {/* a corpse */
	ch[0] = oc_syms[(int)objects[CORPSE].oc_class];
	color = mon_color(offset);
    } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {	/* a pet */
	ch[0] = monsyms[(int)mons[offset].mlet];
	color = pet_color(offset);
    } else{						/* a monster */
	ch[0] = monsyms[(int)mons[glyph].mlet];
	color = mon_color(glyph);
    }
#endif

    ch[1] = '\0';

    gdk_draw_rectangle(xshm_map_pixmap,
      map->style->bg_gc[GTK_WIDGET_STATE(map)],
      TRUE, x * c_width, y * c_height -  map_font->ascent, c_width, c_height);
    
    gdk_draw_text_wc(xshm_map_pixmap, map_font, copts.use_color ?
      map_color_gc[color] : map->style->fg_gc[GTK_WIDGET_STATE(map)],
      x * c_width + map_xoffsets[ch[0]], y * c_height, ch, 1);
    
    if (glyph_is_pet(glyph) && copts.hilite_pet) {
	gdk_draw_rectangle(xshm_map_pixmap,
	  map_color_gc[copts.use_color ? CLR_RED : CLR_WHITE], FALSE,
	  x * c_width, y * c_height - map_font->ascent,
	  c_width - 1, c_height - 1);
    }
    else if (x == cursx && y == cursy)
	gdk_draw_rectangle(xshm_map_pixmap, map_color_gc[CLR_WHITE], FALSE,
	  x * c_width, y * c_height - map_font->ascent,
	  c_width - 1, c_height - 1);

    update_rect.x = x * c_width;
    update_rect.y = y * c_height - map_font->ascent;
    update_rect.width = c_width;
    update_rect.height = c_height;
    xshm_map_draw(&update_rect);
}

/*
 * The assumption that the glyph in the layer furthest from the viewer is
 * the only surface glyph present is flawed. However, it's the best we can
 * do without knowing details of the tileset in use. This might well be a
 * sensible addition, but for now, this suffices.
 */

static void
nh_map_print_glyph_tmp(struct tilemap *tmap, int ofsx, int ofsy, int dosurface)
{
    int k, tile;

    if (Tile->transparent) {
	if (dosurface) {
	    tile = GLYPH2TILE(tmap->glyphs[no_layers - 1]);
	    x_tile_tmp_draw_tile(tile, ofsx, ofsy);
	} else
	    for(k = no_layers - 2; k >= 0; k--)
		if (tmap->glyphs[k] != NO_GLYPH) {
		    tile = glyph2tile[tmap->glyphs[k]];
		    x_tile_tmp_draw_tile(tile, ofsx, ofsy);
		}
    } else if (!dosurface) {
	for(k = 0; k < no_layers - 1; k++)
	    if (tmap->glyphs[k] != NO_GLYPH)
		break;
	tile = GLYPH2TILE(tmap->glyphs[k]);
	x_tile_tmp_draw_tile(tile, ofsx, ofsy);
    }
    if (!dosurface) {
	if (tmap == GTKMAP(cursy, cursx))
	    x_tile_tmp_draw_rectangle(ofsx, ofsy, MAP_WHITE);
	else if (copts.hilite_pet)
	    for(k = 0; k < no_layers; k++)
		if (glyph_is_pet(tmap->glyphs[k])) {
		    x_tile_tmp_draw_rectangle(ofsx, ofsy, CLR_RED);
		    break;
		}
    }
}

#define NH_MAP_PRINT_GLYPH_TMP(y, x, j, i, k) \
	nh_map_print_glyph_tmp(GTKMAP((y)+(j), (x)+(i)), \
	(i)*c_3dwidth - (j)*c_3dofset, (j)*c_3dheight, k)

/*
 * The ordering of glyph drawing here is very complex in order to
 * meet the following rules:
 *	- Surface glyphs should never obscure anything.
 *	- Glyphs in locations towards the bottom of the screen should
 *	  obscure glyphs higher up.
 *	- Glyphs in locations towards the right of the screen should
 *	  obscure glyphs to the left.
 *
 * Note that these rules are not perfect. For example, a dragon standing
 * in a doorway of a horizontal wall will correctly have part of the
 * right hand of its body obscured by the wall to its right, but its
 * feet stick out under the wall and are thus visible. Where possible,
 * tiles should be drawn with these problems in mind so that the
 * experience is not spoiled for the player.
 */

static void
nh_map_print_glyph_tile(XCHAR_P x, XCHAR_P y, struct tilemap *tmap)
{
    int k;
    x_tile_tmp_clear();

    if (Tile->spread) {
	for(k = 1; k >= 0; k--) {
	    if (y > 0) {
		if (x > 0)
		    NH_MAP_PRINT_GLYPH_TMP(y, x, -1, -1, k);
		NH_MAP_PRINT_GLYPH_TMP(y, x, -1, 0, k);
	    }
	    if (x > 0)
		NH_MAP_PRINT_GLYPH_TMP(y, x, 0, -1, k);
	    NH_MAP_PRINT_GLYPH_TMP(y, x, 0, 0, k);
	    if (x < no_cols - 1)
		NH_MAP_PRINT_GLYPH_TMP(y, x, 0, 1, k);
	    if (y < no_rows - 1) {
		NH_MAP_PRINT_GLYPH_TMP(y, x, 1, 0, k);
		if (x < no_cols - 1)
		    NH_MAP_PRINT_GLYPH_TMP(y, x, 1, 1, k);
	    }
	}
    } else {
	NH_MAP_PRINT_GLYPH_TMP(y, x, 0, 0, 1);
	NH_MAP_PRINT_GLYPH_TMP(y, x, 0, 0, 0);
    }

    x_tile_draw_tmp(x * c_3dwidth + (no_rows - y) * c_3dofset, y * c_3dheight);
}

static void
nh_map_print_glyph_simple_tile(XCHAR_P x, XCHAR_P y, struct tilemap *tmap)
{
    int k;
    for(k = 0; k < no_layers - 1; k++)
	if (tmap->glyphs[k] != NO_GLYPH)
	    break;
    x_tile_draw_tile(GLYPH2TILE(tmap->glyphs[k]), x * c_width, y * c_height);
    if (x == cursx && y == cursy)
	x_tile_draw_rectangle(x * c_width, y * c_height, &nh_color[MAP_WHITE]);
    else if (copts.hilite_pet)
	for(k = 0; k < no_layers; k++)
	    if (glyph_is_pet(tmap->glyphs[k])) {
		x_tile_draw_rectangle(x * c_width, y * c_height,
		  &nh_color[CLR_RED]);
		break;
	    }
}

static void
nh_map_print_glyph(XCHAR_P x, XCHAR_P y, struct tilemap *tmap)
{
#ifdef WINGTK_RADAR
    nh_print_radar(x, y, tmap);
#endif
    
    if (map_visual == 0)
	nh_map_print_glyph_traditional(x, y, tmap);
    else if (!Tile->transparent && !Tile->spread)
	nh_map_print_glyph_simple_tile(x, y, tmap);
    else
	nh_map_print_glyph_tile(x, y, tmap);
}

void
GTK_ext_print_glyph_layered(winid id, int nl, struct proxy_glyph_layer *layers)
{
    int i, j, k, x, y;
    struct proxy_glyph_layer *l;

    for(k = 0, l = layers; k < nl; k++, l++)
	for(j = 0, y = l->start; j < l->nr; j++, y++)
	    for(i = 0, x = l->rows[j].start; i < l->rows[j].ng; i++, x++)
		if (GTKMAP(y, x)->glyphs[k] != l->rows[j].glyphs[i]) {
		    GTKMAP(y, x)->glyphs[k] = l->rows[j].glyphs[i];
		    GTKMAP(y, x)->flags |= TILEMAP_UPDATE;
		    map_update = 1;
		}
}

void
GTK_ext_print_glyph(winid id, int x, int y, int glyph)
{
    int k;

    if (GTKMAP(y, x)->glyphs[0] != glyph || GTKMAP(y, x)->flags != 0) {
	GTKMAP(y, x)->glyphs[0] = glyph;
	GTKMAP(y, x)->flags = TILEMAP_UPDATE;
	map_update = 1;
    }
    for (k = 1; k < no_layers; k++)
	if (GTKMAP(y, x)->glyphs[k] != NO_GLYPH) {
	    GTKMAP(y, x)->glyphs[k] = NO_GLYPH;
	    GTKMAP(y, x)->flags |= TILEMAP_UPDATE;
	    map_update = 1;
	}
}

void
GTK_curs(winid id, int x, int y)
{
    if (id != NHW_MAP)
	return;

    if (cursx != x || cursy != y) {
	map_update = 1;
	GTKMAP(y, x)->flags |= TILEMAP_UPDATE;
	GTKMAP(cursy, cursx)->flags |= TILEMAP_UPDATE;
	cursx = x;
	cursy = y;
    }
}

#ifdef WINGTK_RADAR
static void
nh_radar_redraw()
{
    int i, j;
    for(j = 0; j < no_rows; j++)
	for(i = 0; i < no_cols; i++)
	    nh_print_radar(i, j, GTKMAP(j, i));
    nh_radar_update();
}

boolean nh_radar_get_use(void)
{
    return use_radar;
}

void nh_radar_set_use(boolean use)
{
    int old = use_radar;
    use_radar = use;
    if (radar_is_created) {
	if (!use)
	    gtk_widget_hide(radar);
	else {
	    gtk_window_present(GTK_WINDOW(radar));
	    if (!old)
		nh_radar_redraw();
	}
    }
}

void
nh_radar_update()
{
    GdkRectangle update_rect;
    GtkAdjustment *hadj, *vadj;

    if (use_radar && radar_pixmap2 && map && map_gc) {
	hadj = xshm_map_get_hadjustment();
	vadj = xshm_map_get_vadjustment();

	gdk_draw_pixmap(radar_pixmap2, map_gc, radar_pixmap, 0, 0, 0, 0, 
	  NH_RADAR_WIDTH, NH_RADAR_HEIGHT);
	
	gdk_draw_rectangle(radar_pixmap2, map_color_gc[CLR_WHITE], FALSE,
	  hadj->value / c_3dwidth * NH_RADAR_UNIT,
	  vadj->value / c_3dheight * NH_RADAR_UNIT,
	  hadj->page_size / c_3dwidth * NH_RADAR_UNIT,
	  vadj->page_size / c_3dheight * NH_RADAR_UNIT);
	
	update_rect.x = 0;
	update_rect.y = 0;
	update_rect.width = NH_RADAR_WIDTH;
	update_rect.height = NH_RADAR_HEIGHT;
	
	gtk_widget_draw(radar, &update_rect);
	gdk_window_raise(radar->window);
    }
}
#endif	/* WINGTK_RADAR */

static void
nh_map_cliparound(int x, int y, gboolean exact)
{
    static int sx = -1, sy;
    if (!map || !exact && sx >= 0 &&
      (x - sx) * (x - sx) + (y - sy) * (y - sy) <= map_clip_dist2)
	return;
    sx = x;
    sy = y;
    xshm_map_cliparound(x * c_3dwidth + (no_rows - y) * c_3dofset,
      y * c_3dheight);
}

#undef DIFF

void
GTK_cliparound(int x, int y)
{
    nh_map_cliparound(x, y, FALSE);
}

/*
 * [ALI]
 * This function returns a pixmap of size 3dwidth x 3dheight pixels.
 * In non-3D mode, this is the full tile size, but in 3D mode this
 * strips out the 3D part. A complete algorithm for extracting the
 * non-3D part of the tile is quite complex (see bigtile.c for a
 * starting point), but since this routine is currently only used
 * for objects, we can safely assume that all tiles are padded,
 * ie., that the non-3D part is centered in the tile.
 */

GdkPixmap *
GTK_glyph_to_gdkpixmap(int glyph)
{
    int tile;
    int width, height;
    GdkPixmap *pix;

    width = Tile->unit_width - Tile->ofsetx_3d;
    height = Tile->unit_height - Tile->ofsety_3d;
    pix = gdk_pixmap_new(NULL, width, height, gdk_visual_get_system()->depth);
    if (!pix)
	return NULL;

    tile = fix_tile(GLYPH2TILE(glyph));

    if (!x_tile_render_to_drawable(pix, map_gc, tile,
      Tile->ofsetx_3d/2, Tile->ofsety_3d/2, 0, 0, width, height)) {
	gdk_pixmap_unref(pix);
	pix = NULL;
    }

    return pix;
}

void
nh_map_click(int f)
{
    map_click = f;
}

void
nh_map_pos(int *x, int *y, int *mod)
{
    *x = cursx;
    *y = cursy;
    *mod = cursm;
}

static void
nh_map_redraw()
{
    int i, j;
    map_update = 1;
    for(j = 0; j < no_rows; j++)
	for(i = 0; i < no_cols; i++)
	    GTKMAP(j, i)->flags |= TILEMAP_UPDATE;
}

void
nh_map_color_changed(boolean new_value)
{
    if (map_visual == 0)
	nh_map_redraw();
}

void
nh_map_hilite_pet_changed(boolean new_value)
{
    int i, j, k;
    map_update = 1;
    for(j = 0; j < no_rows; j++)
	for(i = 0; i < no_cols; i++)
	    for(k = 0; k < no_layers; k++)
		if (glyph_is_pet(GTKMAP(j, i)->glyphs[k]))
		    GTKMAP(j, i)->flags |= TILEMAP_UPDATE;
}

void
nh_map_flush()
{
    int i, j;

    if (!xshm_map_pixmap && !xshm_map_image && !xshm_map_pixbuf)
	return;		/* Map not realized yet - pend flush */

    if (map_update) {
	map_update = 0;

	for(j = 0; j < no_rows; j++)
	    for(i = 0; i < no_cols; i++)
		if (GTKMAP(j, i)->flags & TILEMAP_UPDATE) {
		    GTKMAP(j, i)->flags &= ~TILEMAP_UPDATE;
		    nh_map_print_glyph(i, j, GTKMAP(j, i));
		}
	nh_map_check_visibility();
	xshm_map_flush();
#ifdef WINGTK_RADAR
	nh_radar_update();
#endif
    }
    GTK_curs(NHW_MAP, cursx, cursy);
}

int
nh_tile_height()
{
    return c_height;
}

int
nh_tile_3dheight()
{
    return c_3dheight;
}
