/* Map graphics for the Mac interface to Xconq.
   Copyright (C) 1992-1998 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */


/* This file consists of
		1. Coordinate transforming and calculating functions.
		2. Map creating, destroying and modifying functions.
		3. draw_map and all the functions it calls.
		4. draw_other_maps & draw_related_maps.
		5. draw_unit_blast, draw_selections etc.
		7. update_cell_display & update_cell.

   draw_row and all functions that it calls are in macrow.c.
   All mouse-driven user interaction code is in macmouse.c. */

#include "conq.h"
#include "macconq.h"

static void draw_feature_names(Map *map, Rect drawrect);
static void update_grid_leakage(Map *map);

static void draw_meridians(Map *map, Rect drawrect);
static void draw_info_text(Map *map, int x, int y, int len, char *buf);
static void draw_unit_names_only(Map *map, int x, int y);

extern void draw_gridlines(Map *map, int x, int y);
extern void draw_shorelines(Map *map, int x, int y);
extern void xform_fractional(Map *map, int x, int y, int xf, int yf, int *sxp, int *syp);

Map *frontmap = NULL;

int conwid = 32;
int tophgt;
int mapnum = 1;
int nummaps = 0;
int lastmaph = -1;
int lastmapv = -1;
int animation_pattern_state;
char *mouseover = "something";

/* Handles of the pictures that display all the map control buttons. */
PicHandle tlcontrols = nil;
PicHandle blcontrols = nil;

/* Adjust viewport for offscreen drawing and go offscreen.
   Then call draw_other_maps to erase other-map boxes */

#define GO_OFFSCREEN(m) {  \
	(m)->vp->sx -= (m)->offsetx + (m)->bufx - (m)->conw; \
	(m)->vp->sy -= (m)->offsety + (m)->bufy - (m)->toph; \
	LockPixels(GetGWorldPixMap((m)->gworldPortPtr)); \
	SetPort((GrafPtr)(m)->gworldPortPtr); \
	if ((m)->vp->draw_other_maps) draw_other_maps(m);\
}

/* Call draw_other_maps to redraw other-map boxes, then
   reset viewport for onscreen drawing and return onscreen. */

#define RETURN_ONSCREEN(m) {  \
	if ((m)->vp->draw_other_maps) draw_other_maps(m);\
	(m)->vp->sx += (m)->offsetx + (m)->bufx - (m)->conw; \
	(m)->vp->sy += (m)->offsety + (m)->bufy - (m)->toph; \
	UnlockPixels(GetGWorldPixMap((m)->gworldPortPtr)); \
	SetPort((GrafPtr)(m)->windowPortPtr); \
  }

/* These three macros are used to handle cases where an item stretches across the */
/* edge of a wrapped gworld. It is then necessary to draw it twice, once at each end. */
/* The macros should be applied to all drawing functions that take an sx value, rect */
/* or poly as a parameter. */

/* Note: In at least one case (longitude text in draw_meridians) it is clearly necessary */
/* to do a double UNWRAP (both add and substract sxmax) in order to cover all cases. */
/* This may well turn out be true also for the UNWRAP macros below. */

// --- PERFORM DOUBLE UNWRAP FOR ALL THREE MACROS ---------------------------- 

/* Repeat (function) both if sx < 0 and if sx is within one hex from sxmax. */
#define UNWRAP_SX(map, sx, function) { \
	(function); \
	if (area.xwrap && (sx) > (map)->vp->sxmax - (map)->vp->hw) { \
		(sx) -= (map)->vp->sxmax; \
		(function); \
		(sx) += (map)->vp->sxmax; \
	} else if (area.xwrap && (sx) < 0) { \
		(sx) += (map)->vp->sxmax; \
		(function); \
		(sx) -= (map)->vp->sxmax; \
	} \
}

/* Repeat (function) once using sx shifted one area width to the right. */
/*
#define UNWRAP_SX(map, sx, function) { \
	(function); \
	if (area.xwrap && (sx) < 0) { \
		(sx) += (map)->vp->sxmax; \
		(function); \
		(sx) -= (map)->vp->sxmax; \
	} \
}
*/

/* Repeat (function) both if rect.left < 0 and if rect.right > sxmax. */
#define UNWRAP_RECT(map, rect, function) { \
	(function); \
	if (area.xwrap && (rect).right > (map)->vp->sxmax) { \
		OffsetRect(&(rect), -(map)->vp->sxmax, 0); \
		(function); \
		OffsetRect(&(rect), (map)->vp->sxmax, 0); \
	} else if (area.xwrap && (rect).left < 0) { \
		OffsetRect(&(rect), (map)->vp->sxmax, 0); \
		(function); \
		OffsetRect(&(rect), -(map)->vp->sxmax, 0); \
	} \
}

/* Repeat (function) once using rect offset one area width to the right. */
/*
#define UNWRAP_RECT(map, rect, function) { \
	(function); \
	if (area.xwrap && (rect).left < 0) { \
		OffsetRect(&(rect), (map)->vp->sxmax, 0); \
		(function); \
		OffsetRect(&(rect), -(map)->vp->sxmax, 0); \
	} \
}
*/

/* Repeat (function) both if polyBBox.left < 0 and if polyBBox.right > sxmax. */
#define UNWRAP_POLY(map, poly, function) { \
	(function); \
	if (area.xwrap && (*(*(poly))).polyBBox.right > (map)->vp->sxmax) { \
		OffsetPoly((poly), -(map)->vp->sxmax, 0); \
		(function); \
		OffsetPoly((poly), (map)->vp->sxmax, 0); \
	} else if (area.xwrap && (*(*(poly))).polyBBox.left < 0) { \
		OffsetPoly((poly), (map)->vp->sxmax, 0); \
		(function); \
		OffsetPoly((poly), -(map)->vp->sxmax, 0); \
	} \
}

/* Repeat (function) once using poly offset one area width to the right. */
/*
#define UNWRAP_POLY(map, poly, function) { \
	(function); \
	if (area.xwrap && (*(*(poly))).polyBBox.left < 0) { \
		OffsetPoly((poly), (map)->vp->sxmax, 0); \
		(function); \
		OffsetPoly((poly), -(map)->vp->sxmax, 0); \
	} \
}
*/

// -----------------------------------------------------------------------

/* Given a map and a cell, compute the pixel coords of the cell's UL corner.
   This is the core routine that relates cells and pixels. */

void
xform(map, x, y, sxp, syp)
Map *map;
int x, y, *sxp, *syp;
{
	xform_cell(map->vp, x, y, sxp, syp);
	/* Shift the basic viewport result to account for both origin-shift and map decor. */
	*sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
}

void
xform_fractional(map, x, y, xf, yf, sxp, syp)
Map *map;
int x, y, xf, yf, *sxp, *syp;
{
	xform_cell_fractional(map->vp, x, y, xf, yf, sxp, syp);
	/* Shift the basic viewport result to account for both origin-shift and map decor. */
	*sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
}

void
m_xform_unit(map, unit, sxp, syp, swp, shp)
Map *map;
Unit *unit;
int *sxp, *syp, *swp, *shp;
{
	xform_unit(map->vp, unit, sxp, syp, swp, shp);
	/* Shift the basic viewport result to account for both origin-shift and map decor. */
	*sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
}

void
m_xform_unit_self(map, unit, sxp, syp, swp, shp)
Map *map;
Unit *unit;
int *sxp, *syp, *swp, *shp;
{
	xform_unit_self(map->vp, unit, sxp, syp, swp, shp);
	/* Shift the basic viewport result to account for both origin-shift and map decor. */
	*sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
}

void
m_xform_occupant(map, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp)
Map *map;
Unit *transport, *unit;
int sx, sy, sw, sh, *sxp, *syp, *swp, *shp;
{
	/* Transform the coordinates back to relative values. */
	sx -= map->osx + map->conw;  sy -= map->osy + map->toph;
	xform_occupant(map->vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp);
	/* Shift the basic viewport result to account for both origin-shift and map decor. */
	*sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
}

/* Un-transform screen coordinates (as supplied by mouse perhaps) into
   map coordinates.  */

/* Note that only drawing is affected by SetOrigin - mouse locations are
   always window-relative, and thus only need to be adjusted by map decor
   before going to the generic routines. */

int
m_nearest_cell(Map *map, int sx, int sy, int *xp, int *yp)
{
	return nearest_cell(map->vp, sx - map->conw, sy - map->toph, xp, yp, NULL, NULL);
}

/* Find the closest direction of the closest boundary */

int
m_nearest_boundary(Map *map, int sx, int sy, int *xp, int *yp, int *dirp)
{
	return nearest_boundary(map->vp, sx - map->conw, sy - map->toph, xp, yp, dirp);
}

int
m_nearest_unit(Map *map, int sx, int sy, Unit **unitp)
{
	return nearest_unit(map->vp, sx - map->conw, sy - map->toph, unitp);
}

/* This tests whether the given cell might possibly be visible on the given map.
   Not as precise as a real cliprect calculation. */

int
at_all_visible(Map *map, int x, int y)
{
	return cell_is_visible(map->vp, x, y);
}

/* Decide whether given location is away from the edge of the map's window. */

int
in_middle(Map *map, int x, int y)
{
	return cell_is_in_middle(map->vp, x, y);
}

void
m_focus_on_center(Map *map)
{
	focus_on_center(map->vp);
}

/* Put vcx,vcy in the middle of the map. */

void
m_center_on_focus(Map *map)
{
	center_on_focus(map->vp);
	set_content_rect(map);
}

/* Create a new map window and all its paraphernalia.  It is the responsibility of
   the calling function to pass a new map id and increment mapnum! */

Map *
create_map(int power, int id)
{
	int m, t, i, x, y, mainwidth, mainheight;
	Rect vscrollrect, hscrollrect;
	WindowPtr win;
	Map *map;
	Unit *unit;

	build_optional_terrain_type_menu();
	DGprintf("Creating map, mag power %d\n", power);
	map = (Map *) xmalloc(sizeof(Map));

	/* Use id passed in function call.  The world map is always map #0. */
	if (id == 0)
	  worldmap = map;
	map->id = id;
	map->vp = new_vp();
	set_map_power(map, power);
	/* Pick an appropriate focus of the view. */
	pick_a_focus(dside, &x, &y);
	set_view_focus(map->vp, x, y);
	/* Set default values for the display controls. */
	map->conw = conwid;
	map->toplineh = (default_draw_topline ? tophgt : 0);
	map->topunith = (default_draw_topunit ? topunithgt : 0);
	map->toph = map->toplineh + map->topunith;
	/* Mac viewport needs different xform style, let the common code know about this. */
	map->vp->wide_viewport = TRUE;
	map->vp->draw_terrain = TRUE;
	/* Display all types of borders and connections normally. */
	/* (aux_terrain_types[t] == 0 implies not optional type - i can't overflow
	   drawauxterrain because at least one ttype must be cell terrain.) */
	/* (Addressing of map->vp->aux_terrain_types is 1-based instead of 0-based
	    because it corresponds to menu items.) */
	i = 1;
	for_all_terrain_types(t) {
		if (!t_is_cell(t)) {
			map->vp->aux_terrain_types[i++] = t;
			map->vp->draw_aux_terrain[t] = TRUE;
		}
	}
	map->vp->draw_cell_pats = TRUE;  /* should get from prefs */
	map->vp->draw_feature_boundaries = TRUE;
	map->vp->draw_units = TRUE;
	map->vp->draw_names = default_draw_names;
	map->vp->draw_people = FALSE;
	map->vp->draw_control = FALSE;
	map->vp->draw_elevations = FALSE;
	for_all_material_types(m) {
		map->vp->draw_materials[m] = FALSE;
	}
	map->vp->num_materials_to_draw = 0;
	map->vp->draw_lighting = TRUE;
	map->vp->draw_temperature = FALSE;
	map->vp->draw_winds = FALSE;
	map->vp->draw_clouds = FALSE;
	map->vp->draw_storms = TRUE;
	map->vp->draw_grid = default_draw_grid;
	
	/* Display AI info by default if there is an AI present. */
	map->vp->draw_ai = side_has_ai(dside);

// --- USER CONTROL OF EMBLEMS -----------------------------------------

	map->draw_emblems = default_draw_emblems;

// ------------------------------------------------------------------

	map->sidecolors = default_sidecolors;
	map->iconmasks = default_iconmasks;
	map->boxmasks = default_boxmasks;
	map->textmasks = default_textmasks;
	map->featureborders = default_featureborders;
	map->simple_borders = default_simple_borders;
	map->featurenames = default_featurenames;
	map->shorelines = default_shorelines;
	map->optimize_fonts = default_optimize_fonts;
	map->max_bufx = default_max_bufx;
	map->max_bufy = default_max_bufy;	
	map->vp->draw_latlong = default_draw_latlong;
	map->solid_color_terrain = pref_solid_color_terrain;
	map->draw_topline = default_draw_topline;
	map->draw_topunit = default_draw_topunit;
	map->erase_names = default_erase_names;
	/* Ovveride default and turn on name erasing if it is really needed. */
	for_all_units(unit) {
		if (mobile(unit->type) && unit->name) {
			map->erase_names = TRUE;
			break;
		}
	}
	map->xwrap = FALSE;
	update_grid_leakage(map);
	map->vp->draw_other_maps = default_drawothermaps;
	/* Override default and turn on other-map boxes for world map. */
	if (map == worldmap)
		map->vp->draw_other_maps = TRUE;
	map->vp->latlong_interval = default_latlong_interval;
	/* Override default and choose larger interval if spacing would make too cluttered. */

// --- FIXES CRASHES WHEN LAUNCHING FLATTOPS ETC -----------------------------------

	if (map->vp->latlong_interval > 0 && world.circumference > 0)

// -------------------------------------------------------------------------

		while (((map->vp->latlong_interval * world.circumference * map->vp->hw) / 21600) < 32)
			map->vp->latlong_interval *= 2;


	map->autoselect = defaultautoselect;
	map->moveonclick = defaultmoveonclick;
	map->drawsizes = default_drawsizes;
	map->numselections = 0;
	map->maxselections = max(100, numunits + numunits / 2);
	map->selections = (Unit **) xmalloc(map->maxselections * sizeof(Unit *));
	/* Newest map goes on the front of the list. */
	map->next = maplist;
	maplist = map;
	if (hasColorQD) {
		win = GetNewCWindow(wMap, nil, (WindowPtr) -1L);
	} else {
		win = GetNewWindow(wMap, nil, (WindowPtr) -1L);
	}
	map->window = win;
	stagger_window(win, &lastmaph, &lastmapv);
	if (first_windows) {
		get_main_screen_size(&mainwidth, &mainheight);
		/* Use the main screen size if full-sized map desired, else
		   leave room for game window at side and notices at bottom. */
		SizeWindow(win,
				   mainwidth - (fullsize_map ? 0 : gamewinw) - 10,
				   mainheight - GetMBarHeight() - 16  - (fullsize_map ? 0 : 150) - 7,
				   FALSE);
	}
	ShowWindow(win);
	SetPort(win);

	/* Use window title "World Map" if world map, else use the map number. */
	if (map == worldmap) {
		worldmapwin = win;
		sprintf(spbuf, "World Map");
	} else {
		sprintf(spbuf, "Map %d", map->id);
	}
	add_window_menu_item(spbuf, win);
	set_content_rect(map);
	m_center_on_focus(map);
	/* Make the scrollbars. */
	vscrollrect = map->window->portRect;
	vscrollrect.top -= 1;
	vscrollrect.bottom -= sbarwid - 1;
	vscrollrect.left = vscrollrect.right - sbarwid;
	vscrollrect.right += 1;
	map->vscrollbar =
		NewControl(win, &vscrollrect, "\p", 1,
			 map->vp->sy, map->vp->symin, map->vp->symax, scrollBarProc, 0L);
	hscrollrect = win->portRect;
	hscrollrect.top = hscrollrect.bottom - sbarwid;
	hscrollrect.bottom += 1;
	hscrollrect.left += map->conw;
	hscrollrect.right -= sbarwid - 1;
	map->hscrollbar =
		NewControl(win, &hscrollrect, "\p", 1,
			 map->vp->sx, map->vp->sxmin, map->vp->sxmax, scrollBarProc, 0L);
	set_map_scrollbars(map);
	++nummaps;

	/* Save the onscreen gworld */
	GetGWorld(&map->windowPortPtr,&map->deviceHdl);

	/* Create an empty gworld of minimal size to be resized and filled in below */
	map->qdErr = NewGWorld(&map->gworldPortPtr,0, &map->contentrect,0, 0, 0);

	/* Alert and close the map window if we ran out of memory */
	if (map->gworldPortPtr == NULL || map->qdErr != noErr ) {
		/* close_window assumes other-map boxes exist */
		draw_related_maps(map);	
		close_window(win);
		StopAlert(aNoMem, NULL);
		/* Return zero to indicate no map was created */
		return NULL;			
	}
	
	/* Optimize gworld size and position and also draw the map */
	update_gworld(map);

	/* Finally draw the new other-map boxes */
	draw_related_maps(map);	

	return map;
}

/* Compute the content part of the map window. */

void
set_content_rect(Map *map)
{
	map->contentrect = map->window->portRect;
	map->contentrect.left += map->conw;  map->contentrect.top += map->toph;
	map->contentrect.right -= sbarwid;  map->contentrect.bottom -= sbarwid;
	set_view_size(map->vp,
				  map->contentrect.right - map->contentrect.left,
				  map->contentrect.bottom - map->contentrect.top);
}

/* Adjust the appearance and thumb of the scroll bars to reflect the map.  This is
   needed whenever the map is adjusted under program control, such as when magnifying
   or scrolling to a specified location. */

void
set_map_scrollbars(Map *map)
{
	int hilite;
	
	/* Scale the scrollbars to circumvent the 32K limit on control values. */

	/* This hack solves the scrolling problem for big maps at high magnification */
	/* Note that all direct access of the scrollbar values from now on first must */
	/* simulate the scaling that is applied below in order to get correct values! */

	int	scaled_sxmin, scaled_sx, scaled_sxmax, scaled_symin, scaled_sy, scaled_symax;

	scaled_sxmin = map->vp->sxmin;
	scaled_sxmax = map->vp->sxmax;
	scaled_sx = map->vp->sx;

	while (scaled_sxmax > 32000) {	/* Keep dividing by 2 as long as necessary */
		scaled_sxmax /= 2;
		scaled_sxmin /= 2;	
		scaled_sx /= 2;
	}

	/* Apply the scaled values */
	SetCtlMin(map->hscrollbar, scaled_sxmin);
	SetCtlMax(map->hscrollbar, scaled_sxmax);
	SetCtlValue(map->hscrollbar, scaled_sx);

	hilite = (map->vp->sxmin != map->vp->sxmax);
	/* Adjust the hiliting of the scrollbar, but only if the window is in front,
	   otherwise the scrollbar should remain unhilited. */
	if (map->window == FrontWindow())
	  HiliteControl(map->hscrollbar, (hilite ? 0 : 255));
	DGprintf("Hscroll (%shilite) is %d -- %d -- %d\n",
			 (hilite ? "" : "no "), GetCtlMin(map->hscrollbar),
	         GetCtlValue(map->hscrollbar), GetCtlMax(map->hscrollbar));

	/* Also scale the vertical scrollbar even though no current games are that high. */
	scaled_symin = map->vp->symin;
	scaled_symax = map->vp->symax;
	scaled_sy = map->vp->sy;

	while (scaled_symax > 32000) {	/* Keep dividing by 2 as long as necessary */
		scaled_symax /= 2;
		scaled_symin /= 2;	
		scaled_sy /= 2;
	}

	/* Apply the scaled values */
	SetCtlMin(map->vscrollbar, scaled_symin);
	SetCtlMax(map->vscrollbar, scaled_symax);
	SetCtlValue(map->vscrollbar, scaled_sy);

	hilite = (map->vp->symin != map->vp->symax);
	if (map->window == FrontWindow())
	  HiliteControl(map->vscrollbar, (hilite ? 0 : 255));
	set_content_rect(map);
	DGprintf("Vscroll (%shilite) is %d -- %d -- %d\n",
			 (hilite ? "" : "no "), GetCtlMin(map->vscrollbar),
	         GetCtlValue(map->vscrollbar), GetCtlMax(map->vscrollbar));
}

/* Given a magnification power, look up and/or calculate the sizes of everything,
   in pixels. */

void
set_map_power(Map *map, int power)
{
	set_view_power(map->vp, power);
	if (power >= 4 && cellrgns[power] == nil)
	  make_cell_clip(power);
}

/* This is the old set_focus from maccmd.c moved here and renamed for consistency. */

void
set_map_focus(Map *map, int x, int y)
{
	int	oldsx, oldsy, newsx, newsy;

	if (!inside_area(x, y)) 
	  return;
	/* Save the old vp. */
	oldsx = map->vp->sx;
	oldsy = map->vp->sy;
	/* Find the new vp. */
	set_view_focus(map->vp, x, y);
	m_center_on_focus(map);
	newsx = map->vp->sx;
	newsy = map->vp->sy;
	/* Restore the old vp. */
	map->vp->sx = oldsx;
	map->vp->sy = oldsy;
	/* Let scroll_map_window do the rest of the updating work. */	
	scroll_map_window(map, newsx - oldsx, newsy - oldsy);
}

/* This is the old content of do_set_view_angle moved here for consistency. */
/* The BeginUpdate/EndUpdate code has been removed to fix a rare display bug. */

void
set_map_angle(Map *map, int angle)
{
	/* Erase other-map boxes in other windows. */
	draw_related_maps(map);

	/* Change the view angle. */
	set_view_angle(map->vp, angle);

	/* Redraw everything. */
	force_map_update(map);

	/* Redraw other-map boxes in other windows. */
	draw_related_maps(map);
	
	/* Focus on active unit(s). */
	do_recenter(dside);
}

void
grow_map(Map *map, int w, int h)	/* Manual resizing by dragging the corner */
{
	Map *map2;

	/* Erase other-map boxes in other windows. */
	draw_related_maps(map);

	/* Set the window to the new size. */
	SizeWindow(map->window, w, h, FALSE);

	/* Resize scrollbars. */
	adjust_map_decor(map);

	/* Dont forget this little guy. */
	DrawGrowIcon(map->window);

	/* Update maps in background immediately. */
	for_all_maps(map2) {
		if (map2 != map)
		  update_window(map2->window);
	}

	/* Update map content from gworld and then redraw gworld. */
	update_resized_map(map);

	/* Redraw other-map boxes in other windows. */
	draw_related_maps(map);
}

void
zoom_map(Map *map, int part)	/* Resizing by clicking in the zoom box */
{
	Map *map2;

	if (part == inZoomOut) {
	      set_standard_state(map->window,
		      area.width  * map->vp->hw  + map->conw + sbarwid + 1,
		      area.height * map->vp->hch + map->vp->hh - map->vp->hch + sbarwid + map->toph + 1);
	}
	
	/* Erase other-map boxes in other windows. */
	draw_related_maps(map);

	/* Zoom the window. */
	ZoomWindow(map->window, part, true);

	/* Resize scrollbars. */
	adjust_map_decor(map);

	/* Dont forget this little guy. */
	DrawGrowIcon(map->window);

	/* Update maps in background immediately. */
	for_all_maps(map2) {
		if (map2 != map)
		  update_window(map2->window);
	}

	/* Update map content from gworld and then redraw gworld. */
	update_resized_map(map);

	/* Redraw other-map boxes in other windows. */
	draw_related_maps(map);
}

/* Move and size the controls to be correct for the map. */

void
adjust_map_decor(Map *map)
{				
	int w, h;
	WindowPtr mapwin = map->window;

	w = mapwin->portRect.right - mapwin->portRect.left;
	h = mapwin->portRect.bottom - mapwin->portRect.top;
	MoveControl(map->hscrollbar, map->conw - 1, h - sbarwid);
	SizeControl(map->hscrollbar, w - map->conw - sbarwid + 2, sbarwid + 1);
	MoveControl(map->vscrollbar, w - sbarwid, -1);
	SizeControl(map->vscrollbar, sbarwid + 1, h - sbarwid + 1 + 1);
}

void
invert_map(Map *map, int x, int y, int r)
{
	int sx, sy, sw, sh;
	Rect tmprect;
	GrafPtr	oldport;
	
	GetPort(&oldport);

	GO_OFFSCREEN(map);

	xform(map, x, y, &sx, &sy);

	SetRect(&tmprect, sx - (r * map->vp->hw) / 2, 
					  sy - (r * map->vp->hch) / 2,
					  sx + (r * map->vp->hw) / 2, 
					  sy + (r * map->vp->hch) / 2);

	InvertRect(&tmprect);

	RETURN_ONSCREEN(map);

	/* Copy tmprect to the screen */
	copy_from_gworld(map, tmprect);

	SetPort(oldport);
}

void
activate_map(map, activate)
Map *map;
int activate;
{
	Rect growRect;

	if (activate) {
		HiliteControl(map->vscrollbar, 0);
		HiliteControl(map->hscrollbar, 0);
		/* Controls need to be redrawn on activation. */
		(*(map->vscrollbar))->contrlVis = 255;
		(*(map->hscrollbar))->contrlVis = 255;
		InvalRect(&(*(map->vscrollbar))->contrlRect);
		InvalRect(&(*(map->hscrollbar))->contrlRect);
		/* The growbox needs to be redrawn on activation. */
		growRect = map->window->portRect;
		/* adjust for the scrollbars */
		growRect.top = growRect.bottom - sbarwid;
		growRect.left = growRect.right - sbarwid;
		InvalRect(&growRect);
		/* Remember this as the map most recently in front, even if it moves behind
		   later. */
		frontmap = map;
	} else {
		/* The scrollbars must be deactivated. */
		HiliteControl(map->vscrollbar, 255);
		HiliteControl(map->hscrollbar, 255);
#if 0  /* We don't want to hide them though, because the window bg is not white. */
		HideControl(map->vscrollbar);
		HideControl(map->hscrollbar);
#endif
		/* The growbox should be changed immediately on deactivation. */
		DrawGrowIcon(map->window);
	}
}

/* Given a window, find the map that it belongs to. */

Map *
map_from_window(WindowPtr window)
{
	Map *map;
	
	if (dside == NULL)
	  return NULL;
	for_all_maps(map) {
		if (map->window == window)
		  return map;
	}
	return NULL;
}

/* Return the frontmost map, whether or not it's the frontmost window. */

Map *
front_map()
{
	Map *map;

	map = map_from_window(FrontWindow());
	if (map != NULL)
	  return map;
	if (frontmap != NULL)
	  return frontmap;
	return NULL;
}

void
force_map_update(map)
Map *map;
{
	update_grid_leakage(map);
	force_update(map->window);
}

void
update_grid_leakage(Map *map)
{
	if (map->erase_names 				/* Leaks into the grid. */
	    || !grid_matches_unseen 				/* Can't use quick method. */
	    || map->vp->angle != 90				/* Can't use quick method.  */
	    || map->vp->draw_plans 				/* Leaks into the grid. */	
	    || map->vp->draw_ai 				/* Leaks into the grid. */
	    || map->vp->draw_elevations)			/* Leaks into the grid. */
		map->leaky_grid = TRUE;
	else	map->leaky_grid = FALSE;
}

void
print_map(map)
Map *map;
{
	/* (should send postscript output from ps.c to printer port) */
}

/* Remove and destroy the map object. */

void
destroy_map(map)
Map *map;
{
	Map *map2;

	/* Erase other-map boxes in other windows */
	/* (must be done before disconnecting from list). */
	draw_related_maps(map);

	/* Disconnect it from the list. */	
	if (maplist == map) {
		maplist = map->next;
	} else {
		for_all_maps(map2) {
			if (map2->next == map) {
				map2->next = map->next;
			}
			/* Clean up the map and window numbers. */
			/* Destroying the World Map does not change the map numbers. */
			if (map != worldmap && map2->id > map->id) {
				remove_window_menu_item(map2->window);
				--(map2->id);
				sprintf(spbuf, "Map %d", map2->id);
				add_window_menu_item(spbuf, map2->window);
			}
		}
	}
	if (frontmap == map)
	  frontmap = NULL;
	free_vp(map->vp);
	free(map->selections);

	/* World map crash fix. */
	if (worldmap == map)
	  worldmap = NULL;
	/* Decrement mapnum, but only if this was normal map. */
	else
	  --mapnum;
	/* Always decrement nummaps. */
	--nummaps;
	/* Trash the gworld. */
	DisposeGWorld(map->gworldPortPtr);
	/* This is NOT done by close_windows! */
	DisposeWindow(map->window);
	free(map);
}

/* Display a map and all of its paraphernalia. */

void
draw_map(Map *map)
{
	GrafPtr	oldport;
	
	GetPort(&oldport);

	set_map_scrollbars(map);		
	set_content_rect(map);

	/* Draw map decor and info boxes */
	if (map->conw > 0)
	  draw_control_panel(map);					
	if (map->toplineh > 0)
	  draw_top_line(map);
	if (map->topunith > 0)
	  draw_unit_info(map);

	GO_OFFSCREEN(map);

	/* Draw grey area outside the world. */
	draw_window_background(map);
	/* Draw the grid/unseen color. */
	draw_area_background(map);
	/* Draw the actual content. */
	draw_map_content(map);

	RETURN_ONSCREEN(map);

	/* Copy the whole map content to the window. */
	copy_from_gworld(map, map->contentrect);				
	
	/* Draw selected units.  This handles offscreen drawing on
	   its own. */	
	draw_selections(map);

	SetPort(oldport);
}

/*
This draws the background color under both the grid/unseen color and the map cells
It is normally visible only outside the map area, where grid/unseen was never drawn
Note: map->contenrect replaced by portRect everywhere in draw_window_background!
*/

void
draw_window_background(Map *map)
{
	/* Replaces contentrect everywhere */
	Rect portRect = map->gworldPortPtr->portRect;

	/* If part of the window is entirely outside the world, we draw its shape on
	   top of gray, otherwise window starts out all white. */
/*
	This is the normal option where a gray background is drawn. It assumes that 
	the grid/unseencolor later is drawn on top of it by draw_area_background.
*/
	if (area.width * map->vp->hw < 32000) {
		if (hasColorQD) {
			RGBForeColor(&backcolor);
			FillRect(&portRect, QDPat(black));
			ForeColor(blackColor);
		} else
		switch (bggray) {
			case blackgray:
				FillRect(&portRect, QDPat(black));  break;
			case darkgray:
				FillRect(&portRect, QDPat(dkGray));  break;
			case mediumgray:
				FillRect(&portRect, QDPat(gray));  break;
			case lightgray:
				FillRect(&portRect, QDPat(ltGray));  break;
			case whitegray:
				FillRect(&portRect, QDPat(white));  break;
		}
/*
	This option is a hack for large maps at max magnification, where draw_area_background fails 
	to draw the grid/unseen color. The latter is therefore drawn as window background instead. 
*/
	} else {
		if (hasColorQD) {
			/* Use gridcolor as background if everything is visible. */
			if (grid_matches_unseen) {
				 RGBForeColor(&gridcolor);
			} else RGBForeColor(&unseencolor);
			PaintRect(&portRect);
			RGBForeColor(&blackcolor);
		} else {
			switch ((grid_matches_unseen ? gridgray : unseengray)) {
				case blackgray:
					FillRect(&portRect, QDPat(black));  break;
				case darkgray:
					FillRect(&portRect, QDPat(dkGray));  break;
				case mediumgray:
					FillRect(&portRect, QDPat(gray));  break;
				case lightgray:
					FillRect(&portRect, QDPat(ltGray));  break;
				case whitegray:
					FillRect(&portRect, QDPat(white));  break;
			}
		}
	}
}

/* Draw the actual map data.  This is basically a matter of drawing n rows of terrain,
   each of an appropriate length, but it's a bit of trouble to get the number and
   lengths right.  Actually, it's easy to get approximate sizes, but it's important
   to try to draw as few cells as humanly possible. */

/* Note: In addition to the minor modification described below, draw_map_content has also been 
extensively trimmed down by deleting a lot of code for drawn-area-minimization which was 
incompatible with the offscreen graphics. */ 

void
draw_map_content(map)
Map *map;
{
	int	y1, y2, y, x1, x2, vx, vy, vw, vh;

	/* Compute the size of the region to render as the basic size of the viewport
	   plus the scrolling buffer region. */ 
	vw = (map->vp->pxw + 2 * map->bufx) / map->vp->hw + 2;
	vh = (map->vp->pxh + 2 * map->bufy) / map->vp->hch + 4;

	/* Compute the bottom row to render. */
	vy = ((map->vp->totsh - map->vp->sy + map->toph) / map->vp->hch) - vh + 2;
	/* Now adjust the bottom row so it doesn't go outside the area. */
	if (vy < 0)
	  vy = 0;
	if (vy > area.height - vh)
	  vy = area.height - vh;
	/* Compute the leftmost "column". */
	vx = (map->vp->sx - map->conw) / map->vp->hw - vy / 2 - 1;
	DGprintf("Set %dx%d viewport at %d,%d\n", vw, vh, vx, vy);

	/* Compute top and bottom rows to be displayed. */
	y1 = min(vy + vh, area.height - 1);
	y2 = vy;

	/* Draw the rows, going from top to bottom. */
	for (y = y1; y >= y2; --y) {
		/* Adjust the right and left bounds to fill the viewport as
		   much as possible, without going too far (the drawing code
		   will clip, but clipped drawing is still expensive). */
		/* could test by drawing viewport rect as lines... */

// --- FIX OCCASIONAL UNDRAWN HEXES AT LEFT END OF MAP ------------------------

		x1 = vx - (y - vy) / 2 - 1;

//		x1 = vx - (y - vy) / 2;

// -------------------------------------------------------------------

		x2 = x1 + vw + 2;
		/* If the area doesn't wrap, then we might have to stop
		   drawing before we reach the edge of the viewport. */
		if (area.xwrap) {
			/* (should clip to visrgn, but tricky to avoid wrapping bugs) */
		} else {
			/* Truncate x's to stay within the area. */
			x1 = max(0, min(x1, area.width-1));
			x2 = max(0, min(x2, area.width));
			/* If this row is entirely in the NE corner, don't draw anything. */
			if (x1 + y > area.width + area.halfheight)
			  continue;
			/* If this row is entirely in the SW corner, don't draw anything. */
			if (x2 + y < area.halfheight)
			  continue;
			/* If the row ends up in the NE corner, shorten it. */
			if (x2 + y > area.width + area.halfheight)
			  x2 = area.width + area.halfheight - y;
			/* If the row starts out in the SW corner, shorten it. */
			if (x1 + y < area.halfheight)
			  x1 = area.halfheight - y;
		}
		draw_row(map, x1, y, x2 - x1, FALSE);
	}
	/* Draw feature names if asked to. */
	if (features_defined() && map->featurenames && map->vp->hh > 3)
		draw_feature_names(map, map->gworldPortPtr->portRect);
	/* Now draw the lat-long grid if asked to do so. */
	if (map->vp->draw_latlong && map->vp->latlong_interval > 0)
	 	 draw_meridians(map, map->gworldPortPtr->portRect);
}

/* This feature-based name drawing is more efficient unless thousands of features exist. */
/* Draws any feature names that might extend into the specified rect. */

void
draw_feature_names(Map *map, Rect drawrect)
{
	char 		*str, buf[BUFSIZE];
	Rect		portrect;
	Feature 	*feature;
	int 		sx, sy;

	RGBForeColor(&featurecolor);
	for_all_features(feature) {

		/* This also catches all that junk at (0, 0)! */
		if (!inside_area(feature->x, feature->y))
			continue;
		/* Don't reveal invisible features! */
		if (!terrain_visible(dside, wrapx(feature->x), feature->y))
			continue;
		str= feature_desc(feature, buf); 
		/* Don't write empty names. */
		if (str == NULL)
			continue;
		xform(map, feature->x, feature->y, &sx, &sy);

		/* Wrap sx if necessary. */
		while (area.xwrap && sx < 0)
			sx += map->vp->sxmax;
		while (area.xwrap && sx > map->vp->sxmax)
			sx -= map->vp->sxmax;

		/* Trigger drawing for any names that may extend into drawrect. */
		if (sx > drawrect.left - 100 && sx < drawrect.right + 100 
		    && sy > drawrect.top - map->vp->hh && sy < drawrect.bottom) {
			/* Use sy + hh/4 to prevent drawing on top of unit names. */
			UNWRAP_SX(map, sx, 
				draw_legend_text(sx + map->vp->hw/2, sy + map->vp->hh/4, 2 * map->vp->uh, 
							   str, 0, map->textmasks, map->optimize_fonts));
		}
	}
	ForeColor(blackColor);
}

/* Now only draws meridians that fall within the passed drawrect. */

void
draw_meridians(Map *map, Rect drawrect)
{
	int 	latmin, latcen, latmax, lonmin, loncen, lonmax, lat, lon;
	int 	xfmin, xfmax, yfmin, yfmax, xmin, ymin, xmax, ymax;
	int 	right, left, top, bottom, xend, xcen, ycen, incr, interval;
    	int 	latdeg, latmit, londeg, lonmit, x, y, xf, yf, sx, sy;
    	char 	mitbuf[4];
	Rect 	portrect; 
	
// --- CRASH FIX ---------------------------------------------------------

	/* Don't draw meridians for flat or undefined worlds. */
	if (world.circumference <= 0)
		return;

// --------------------------------------------------------------------

	/* Compute the interval between meridians in pixels. */
	incr = map->vp->latlong_interval;
	interval = (incr * world.circumference * map->vp->hw) / 21600;
	
	/* Return if this interval is too small. */
	if (interval < 16)
		return;
		
	/* Set meridian color if not debugging graphics. */ 
	/* (in which case update_cell passes redColor). */
	if (!DebugG)
		RGBForeColor(&meridiancolor);
	
	/* Limit drawing to the map if it is smaller than the port. */
	portrect = map->gworldPortPtr->portRect;
	portrect.bottom 	= min(portrect.bottom, map->vp->totsh);

	/* Compute min/max x & y values for drawrect. */
	nearest_cell(map->vp, drawrect.left - map->conw, 
					drawrect.bottom -map->toph, 
			   		&xmin, &ymin, &xfmin, &yfmin);
	nearest_cell(map->vp, drawrect.right - map->conw, 
					drawrect.top - map->toph, 
			   		&xmax, &ymax, &xfmax, &yfmax);

	/* Then convert the x & y values to longitudes and latitudes. */
	xy_to_latlong(xmin, ymin, xfmin, yfmin, &latmin, &lonmin);
	xy_to_latlong(xmax, ymax, xfmax, yfmax, &latmax, &lonmax);

	/* Adjust grid so that origin is at Greenwich & Equator. */
	lonmin -= lonmin % incr;
	latmin  -= latmin % incr;
	lonmax -= lonmax % incr;
	latmax  -= latmax % incr;

// --- WRAPPING BUG FIX --------------------------------------------------

	/* Start longitude drawing at Greenwich if the area is wrapped. */
	if (area.xwrap) {

//	/* Start longitude drawing at Greenwich if the map is wrapped. */
//	if (map->xwrap) {

// --------------------------------------------------------------------

		lonmin = 0;
		lonmax = 21600 - 21600 % incr;
	}

	/* Handles non-cylindrical areas that cross the dateline. */
	else if (lonmax < lonmin)
		lonmax += 21600;

	/* Used in clipping code. */
	xend = area.width - 1;
	xcen = area.halfwidth;
	ycen = area.halfheight;
	
	
	/* Compute latitiude and longitude of area center. */
	xy_to_latlong(xcen, ycen, 0, 0, &latcen, &loncen);

	/* Draw the latitiudes. */
	for (lat = latmin; lat <= latmax; lat += incr) {
		/* loncen is used since all latitudes cross it. */
		latlong_to_xy(lat, loncen, &x, &y, &xf, &yf);
		/* Skip if not inside area. */
		if (!inside_area(wrapx(x), y))
			continue;
		xform_fractional(map, x, y, xf, yf, &sx, &sy);
		/* Skip latitudes that fall outside drawrect. */ 
		if (sy < drawrect.top || sy > drawrect.bottom)
			continue;
		/* Don't draw latitudes where the longitude text will be. */
		if (sy - portrect.top < 16 || portrect.bottom - sy < 16)
			continue;
		/* Clip corners to area for non-wrapped worlds. */
		if (!area.xwrap) {
			/* Compute left endpoint. */
			if (in_area(0,y)) { 							
				 xform(map, 0, y, &left, 0);
				 left += 32;
			} else if (in_area(ycen - y, y)) {					
				 xform(map, ycen - y, y, &left, 0);
				 left += 32;
			}
			/* Compute right endpoint. */
			if (in_area(xend + ycen - y, y)) {				
				 xform(map, xend + ycen - y, y, &right, 0);
				 right -= 32 - map->vp->hw;
			} else if (in_area(xend, y)) {					
				 xform(map, xend, y, &right, 0);
				 right -= 32 - map->vp->hw;
			}
			/* Make room for text at left end of gworld. */
			left = max(left, portrect.left + 32);
			/* Make room for text at right end of gworld. */
			right = min(right, portrect.right - 32);
		} else { 
			/* Always make room at left end of gworld. */
			left = portrect.left + 32;
			/* Make room at right end unless the map is wrapped. */
			if (!map->xwrap) 
				right = portrect.right - 32;
			else	right = portrect.right;
		}

		/* Also clip to drawrect. */
		left = max(left, drawrect.left);
		right = min(right, drawrect.right);

		/* Draw the latitude. */
		MoveTo(left, sy);
		LineTo(right, sy);
		
		/* Skip the rest if no latitude text is necessary. */		
		if (left == drawrect.left && right == drawrect.right)
			continue;
		/* Build the text string. */
		latdeg = abs(lat) / 60;
		latmit = abs(lat) % 60;
		mitbuf[0] = '\0';
		if (latmit != 0) sprintf(mitbuf, "%d«", latmit);
		sprintf(tmpbuf, "%d¡%s", latdeg, mitbuf);
		if (!lat) strcat(tmpbuf, "  ");
		else if (lat > 0) strcat(tmpbuf, "N");
		else if (lat < 0) strcat(tmpbuf, "S");
		/* Draw latitude text at left edge if there is room for it. */
		if (left != drawrect.left)
			draw_legend_text(left - 3, sy, 12, tmpbuf, +1, 0, 0);
		/* Draw latitude text at right edge if there is room for it. */
		if (right != drawrect.right)
			draw_legend_text(right + 3, sy, 12, tmpbuf, -1, 0, 0);
	}

	/* Draw the longitudes. */
	for (lon = lonmin; lon <= lonmax; lon += incr) {
		/* Save correct longitude for text if crossing dateline. */
		int loncor = lon;
		/* Make room for top and bottom text. */
		top = portrect.top + 16; 
		bottom = portrect.bottom - 16;
		/* latcen is used since all longitudes cross it. */
		latlong_to_xy(latcen, lon, &x, &y, &xf, &yf);
		/* Skip if not inside area. */
		if (!inside_area(wrapx(x), y))
			continue;
		xform_fractional(map, x, y, xf, yf, &sx, &sy);
		/* Wrap sx if necessary. */
		while (area.xwrap && sx < 0)
			sx += map->vp->sxmax;
		while (area.xwrap && sx > map->vp->sxmax)
			sx -= map->vp->sxmax;
		/* Skip longitudes outside drawrect. */ 
		if ((sx < drawrect.left || sx > drawrect.right))
			continue;
		/* Clip corners to area for non-wrapped maps. */
		if (!area.xwrap) {
			/* Compute top endpoint. */

// --- COSMETIC FIX OF CORNER CLIPPING ----------------------------------------

			if (in_area(0,  ycen + 2 * x - 1))						
			    	 xform(map, 0, ycen + 2 * x - 1, 0, &top);
			else if (in_area(2 * x - xend, ycen + 2 * (xend - x) - 1))	
				 xform(map, 2 * x - xend, ycen + 2 * (xend - x) - 1, 0, &top);

//			if (in_area(0,  ycen + 2 * x))						
//			    	 xform(map, 0, ycen + 2 * x, 0, &top);
//			else if (in_area(2 * x - xend, ycen + 2 * (xend - x)))	
//				 xform(map, 2 * x - xend, ycen + 2 * (xend - x), 0, &top);

// --------------------------------------------------------------------

			/* Compute bottom endpoint. */
			if (in_area(2 * x, ycen - 2 * x))					
				 xform(map, 2 * x, ycen - 2 * x, 0, &bottom);	
			else if (in_area(xend, ycen - 2 * (xend - x)))				
				 xform(map, xend, ycen - 2 * (xend - x), 0, &bottom);
		}

		top = max(drawrect.top, top);
		bottom = min(drawrect.bottom, bottom);

// --- GARBAGE OUTSIDE AREA BUG FIX -----------------------------------------

		if (top > bottom)
			continue;

// --------------------------------------------------------------------

		/* Draw the longitude. */
		MoveTo(sx, top);
		LineTo(sx, bottom);
		
// --- REMOVE - SX IS ALWAYS BETWEEN 0 AND SXMAX ------------------------------

		/* UNWRAP longitude if necessary. */
/*		if (area.xwrap && sx < 0) {
			sx += map->vp->sxmax;
			MoveTo(sx, top);
			LineTo(sx, bottom);
			sx -= map->vp->sxmax;
		}

// --------------------------------------------------------------------		
		
		/* Skip the rest if no longitude text is necessary. */		
		if (top == drawrect.top && bottom == drawrect.bottom)
			continue;
		/* Restore correct longitude if we passed the dateline. */		
		if (lon > 10800)
		     loncor -= 21600;		
		/* Build the text string. */
		londeg = abs(loncor) / 60;
		lonmit = abs(loncor) % 60;
		mitbuf[0] = '\0';
		if (lonmit != 0) sprintf(mitbuf, "%d«", lonmit);
		sprintf(tmpbuf, "%d¡%s", londeg, mitbuf);
		/* Use restored correct longitude. */
		if (loncor > 0) strcat(tmpbuf, "E");
		if (loncor < 0) strcat(tmpbuf, "W");
		/* Draw longitude text at top. */
		if (top == portrect.top + 16) {
			draw_legend_text(sx, top - 6, 12, tmpbuf, 0, 0, 0);
			/* Repeat on BOTH sides (double UNWRAP). */
			if (area.xwrap && sx < 32) 
				draw_legend_text(sx + map->vp->sxmax, top - 6, 12, tmpbuf, 0, 0, 0);
			else if (area.xwrap && sx + 32 > map->vp->sxmax)
				draw_legend_text(sx - map->vp->sxmax, top - 6, 12, tmpbuf, 0, 0, 0);
		}
		/* Draw longitude text at bottom. */
		if (bottom == portrect.bottom - 16){
			draw_legend_text(sx, bottom + 6, 12, tmpbuf, 0, 0, 0);
			/* Repeat on BOTH sides (double UNWRAP). */
			if (area.xwrap && sx < 32) 
				draw_legend_text(sx + map->vp->sxmax, bottom + 6, 12, tmpbuf, 0, 0, 0);
			else if (area.xwrap && sx + 32 > map->vp->sxmax)
				draw_legend_text(sx - map->vp->sxmax, bottom + 6, 12, tmpbuf, 0, 0, 0);
		}
	}
	ForeColor(blackColor);
}

/* This draws a hexagon or rectangle that covers the totality of the area, whether
   discovered or not. */

void 
draw_area_background(Map *map)
{
	int sx, sy, llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
	PolyHandle poly;
	Rect arearect;

	/* Don't bother if area magnified greatly. */
	/* (should fix to not try to draw giant rects, but still draw something reasonable.
	   note that otherwise grid color may be wrong) */
/*
	LineTo uses shorts and does not work over 32K, so a giant hex cannot be drawn.
	If the area.wrap option is used instead with a large map, xform will freak out!
	The grid/unseen color for large maps is therfore drawn as window background
	instead (see draw_window_background).
*/
	if (area.width * map->vp->hw > 32000)
	  return;
	if (area.xwrap) {
		/* Area is cylinder; draw a rectangle. */
		xform(map, 0, area.height-1, &sx, &sy);
		/* Make sure the whole gworld gets a background. */
	 	arearect.left = map->gworldPortPtr->portRect.left;
        arearect.top = sy;
		xform(map, 0, 0, &sx, &sy);
 		/* Make sure the whole gworld gets a background. */
		arearect.right = map->gworldPortPtr->portRect.right;
		arearect.bottom = sy;
		/* If there's scrolling, then draw background under the extra scrolling
		   area. */
		if (map->vp->sxmin != map->vp->sxmax)
		  arearect.right += map->vp->pxw;
		/* Adjust so that edges of the rect pass through the middles of top/bottom edge cells. */
		OffsetRect(&arearect, 0, map->vp->hh / 2);
		if (hasColorQD) {
			/* If everything is visible, use gridcolor for background. */
			if (grid_matches_unseen) {
				 RGBForeColor(&gridcolor);
			} else {
				RGBForeColor(&unseencolor);
			}
			PaintRect(&arearect);
			RGBForeColor(&blackcolor);
		} else {
			/* Try to use gray patterns if b/w. */
			switch (gridgray) {
				case blackgray:
					FillRect(&arearect, QDPat(black));
					break;
				case darkgray:
					FillRect(&arearect, QDPat(dkGray));
					break;
				case mediumgray:
					FillRect(&arearect, QDPat(gray));
					break;
				case lightgray:
					FillRect(&arearect, QDPat(ltGray));
					break;
				case whitegray:
					FillRect(&arearect, QDPat(white));
					break;
			}
		}
	} else {
		/* Area is hexagon; draw a hexagon. */
		/* (should make once and save?) */
		poly = OpenPoly();		
		xform(map, 0 + area.halfheight, 0, &llx, &lly);
		MoveTo(llx, lly);
		xform(map, area.width-1, 0, &lrx, &lry);
	 	LineTo(lrx, lry);
		xform(map, area.width-1, area.halfheight, &rx, &ry);
		LineTo(rx, ry);
 		xform(map, area.width-1 - area.halfheight, area.height-1, &urx, &ury);
		LineTo(urx, ury);
 		xform(map, 0, area.height-1, &ulx, &uly);
		LineTo(ulx, uly);
 		xform(map, 0, area.halfheight, &lx, &ly);
		LineTo(lx, ly);
		LineTo(llx, lly);
		ClosePoly();
		/* Adjust so that edges of the polygon pass through the middles of edge cells. */
		OffsetPoly(poly, map->vp->hw/2, map->vp->hh/2);
		if (hasColorQD) {
			if (grid_matches_unseen) {
				 RGBForeColor(&gridcolor);
			} else {
				RGBForeColor(&unseencolor);
			}
			PaintPoly(poly);
			RGBForeColor(&blackcolor);
		} else {
			switch (gridgray) {
				case blackgray:
					FillPoly(poly, QDPat(black));
					break;
				case darkgray:
					FillPoly(poly, QDPat(dkGray));
					break;
				case mediumgray:
					FillPoly(poly, QDPat(gray));
					break;
				case lightgray:
					FillPoly(poly, QDPat(ltGray));
					break;
				case whitegray:
					FillPoly(poly, QDPat(white));
					break;
			}
		}
		/* Free up the space for this hexagon. */
		KillPoly(poly);
	}
#if 0  /* The idea of a shaded border seems nice, but it doesn't look very good in practice. */
	for (x = 0; x < area.width; ++x) {
		xform(map, x, 0, &sx, &sy);
		draw_border_line(sx, sy, SW, map->vp->power, -1);
		draw_border_line(sx, sy, SE, map->vp->power, -2);
	}
	PenNormal();
#endif
}

/* Draw the map control panel as a pair of PICTs. */

void
draw_control_panel(map)
Map *map;
{
	int winhgt, basev;
	Rect tmprect;
	GrafPtr oldport;

	GetPort(&oldport);
	SetPort(map->window);
	winhgt = (map->window->portRect).bottom - (map->window->portRect).top;
	SetRect(&tmprect, 0, 0, map->conw, winhgt);
	FillRect(&tmprect, QDPat(white));
	MoveTo(map->conw - 1, 0);  Line(0, winhgt);
	if (tlcontrols == nil) {
		tlcontrols = (PicHandle) GetResource('PICT', pMapControlsTL);
	}
	if (tlcontrols != nil) {
		SetRect(&tmprect, 0, 0,
				picture_width(tlcontrols), picture_height(tlcontrols));
		DrawPicture(tlcontrols, &tmprect);
	}
	if (blcontrols == nil) {
		blcontrols = (PicHandle) GetResource('PICT', pMapControlsBL);
	}
	if (blcontrols != nil) {
		SetRect(&tmprect, 0, winhgt - picture_height(blcontrols) + 1,
				picture_width(blcontrols), winhgt);
		DrawPicture(blcontrols, &tmprect);
	}
	if (map->moveonclick && map->autoselect) {
		SetRect(&tmprect, 4, 5, 26, 26);
		InvertRect(&tmprect);
	}
	/* (should modify appearance of top left arrow buttons to reflect abilities) */
	basev = 32 + 5*15 + 2 + 5/*why?*/ + 1;
	SetRect(&tmprect, 0, basev, 30, basev + 10);
	if (map->vp->draw_grid) {
		InvertRect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->vp->draw_names) {
		InvertRect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->vp->draw_people) {
		InvertRect(&tmprect);
	} else if (!people_sides_defined()) {
		gray_out_rect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->vp->draw_control) {
		InvertRect(&tmprect);
	} else if (!control_sides_defined()) {
		gray_out_rect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->vp->draw_plans) {
		InvertRect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->vp->draw_ai) {
		InvertRect(&tmprect);
	} else if (!side_has_ai(dside)) {
		/* (should ensure that this is updated when side gets an AI) */
		gray_out_rect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->see_all) {
		InvertRect(&tmprect);
	} else if (!dside->may_set_see_all) {
		gray_out_rect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->sidecolors) {
		InvertRect(&tmprect);
	}
	OffsetRect(&tmprect, 0, 11);
	if (map->iconmasks) {
		InvertRect(&tmprect);
	}
	/* Draw state of bottom left control buttons. */
	if (map->vp->power <= 0) {
		SetRect(&tmprect, 0, winhgt - 15, 15, winhgt);
		gray_out_rect(&tmprect);
	}
	if (map->vp->power >= NUMPOWERS - 1) {
		SetRect(&tmprect, 16, winhgt - 15, 30, winhgt);
		gray_out_rect(&tmprect);
	}
	SetPort(oldport);
}

/* Draw a single line of the most important info - the current date or turn,
   and a textual description of what the mouse is over. */

void
draw_top_line(Map *map)
{
	int numchars, strwid;
	Rect tmprect;

	/* It would be safest to set the grafport explicitly here, but in
	   the interests of efficiency, don't bother until somebody can
	   prove it's necessary. */
	/* Set up to draw the current date. */
	TextSize(small_font_size);
	TextFont(small_font_id);
	numchars = strlen(curdatestr);
	strwid = TextWidth(curdatestr, 0, numchars);
	/* Clear the whole topline area. */
	SetRect(&tmprect, map->conw, 0, map->conw + map->vp->pxw, map->toplineh - 1);
	FillRect(&tmprect, QDPat(white));
	/* Now draw the date. */
	MoveTo(tmprect.right - strwid - 3, tmprect.top + 11);
	DrawText(curdatestr, 0, numchars);
	if (mouseover != NULL) {
		/* Draw description of what the mouse is over. */
		/* (should clip to avail space) */
		numchars = strlen(mouseover);
		MoveTo(map->conw + 3, tmprect.top + 11);
		DrawText(mouseover, 0, numchars);
	}
	/* Draw a line dividing this from other map content. */
	MoveTo(tmprect.left, tmprect.bottom);
	Line(tmprect.right - tmprect.left, 0);
}

void
draw_unit_info(Map *map)
{
	int mrow, u, m;
	Rect tmprect;
	Unit *unit;
	char infobuf[100];
	GrafPtr oldport;
	RgnHandle tmprgn;

	if (map->topunith <= 0)
	  return;
 	GetPort(&oldport);
	SetPort(map->window);
	/* Save the current clipping region. */
	tmprgn = NewRgn();
	GetClip(tmprgn);
	SetRect(&tmprect, map->conw, map->toplineh, map->conw + map->vp->pxw, map->toph);
	ClipRect(&tmprect);
	/* Clear the whole area. */
	FillRect(&tmprect, QDPat(white));
	if (map->numselections == 1) {
		unit = map->selections[0];
		if (in_play(unit)) {
			u = unit->type;
			sprintf(infobuf, "%s", unit_handle(dside, unit));
			draw_info_text(map, 0, 0, 0, infobuf);
			/* Describe its current location. */
			location_desc(infobuf, dside, unit, unit->type, unit->x, unit->y);
			draw_info_text(map, 0, 1, 0, infobuf);
			/* Very briefly list the numbers and types of the occupants. */
			occupants_desc(infobuf, unit);
			draw_info_text(map, 0, 2, 0, infobuf);
			/* Describe the "important" parameters. */
    		hp_desc(infobuf, unit, TRUE);
    		strcat(infobuf, "   ");
    		acp_desc(infobuf + strlen(infobuf), unit, TRUE);
			draw_info_text(map, 50, 0, 0, infobuf);
			/* List other stack members that are here. */
			others_here_desc(infobuf, unit);
			draw_info_text(map, 50, 1, 0, infobuf);
			mrow = 0;
			while (supply_desc(infobuf, unit, mrow)) {
				draw_info_text(map, 50, mrow + 2, 0, infobuf);
				++mrow;
			}
		    /* Describe the current plan and task agenda. */
		    if (unit->plan) {
				int row = 3, n;
				Task *task;

				plan_desc(infobuf, unit);
				draw_info_text(map, 0, row++, 0, infobuf);
				n = 0;
				infobuf[0] = '\0';
				for_all_tasks(unit->plan, task) {
					if (n > 0 && n % 2 == 0) {
						draw_info_text(map, 0, row, 0, infobuf);
						infobuf[0] = '\0';
						row++;
					}
					task_desc(infobuf+strlen(infobuf), unit->side, task);
					if (task->next != NULL)
					  strcat(infobuf, "; ");
					++n;
				}
				if (strlen(infobuf) > 0)
				  draw_info_text(map, 0, row, 0, infobuf);
		    }
		}
	} else if (map->numselections > 1) {
		draw_info_text(map, 0, 0, 0, "(multiple units)");
	}
	/* Draw a line dividing this from the map content. */
	MoveTo(tmprect.left, tmprect.bottom - 1);
	Line(tmprect.right - tmprect.left, 0);
	/* Restore clipping region. */
	SetClip(tmprgn);
	DisposeRgn(tmprgn);
	/* Restore grafport. */
	SetPort(oldport);
}

static void
draw_info_text(map, x, y, len, buf)
Map *map;
int x, y, len;
char *buf;
{
	int sx, sy;
	Rect tmprect;

    if (empty_string(buf))
      return;
	SetRect(&tmprect, map->conw, map->toplineh, map->conw + map->vp->pxw, map->toph);
    /* Translate a 0-100 value for x to pixels. */
    if (x == 50)
      sx = tmprect.left + (tmprect.right - tmprect.left) / 2;
    else
      sx = tmprect.left + 3;
    sy = tmprect.top + small_line_spacing + y * (small_line_spacing + 1);
    if (len > 0 && strlen(buf) > len)
      buf[len-1] = '\0';
	MoveTo(sx, sy);
	TextFont(small_font_id);
	TextSize(small_font_size);
	DrawText(buf, 0, strlen(buf));
}

/* This function is now only called when the current port is an offscreen gworld. It it called by  GO_OFFSCREEN, to 
   erase the other-map boxes, and then again by RETURN_ONSCREEN, to redraw the other-map boxes. This makes 
   sure that the other-map boxes always are preserved during offscreen drawing. */

void
draw_other_maps(map)
Map *map;
{
	Map *map2;

	for_all_maps(map2) {
		if (map != map2 /* && reasonable to show? */) {
			draw_other_map(map, map2);
		}
	}
}

/* Draws both offscreen and on screen! */

/* This and the selection animation are the only two cases where things are drawn directly
   to the screen. draw_related_maps now also clips drawing correctly to the map content. */

void
draw_related_maps(Map *map)
{
	Map *map2;
	RgnHandle tmprgn;
	Rect tmpRect;
	GrafPtr oldport;

	/* Very important here (unlike most other drawing functions) */
	/* because of the SetPort(map2->window) call below! */
	GetPort(&oldport);

	for_all_maps(map2) {
		if (map != map2 && map2->vp->draw_other_maps) {

			SetPort(map2->window);

			/* Clip related map box to content of other window */
			tmprgn = NewRgn();
			GetClip(tmprgn);
			ClipRect(&map2->contentrect);

			/* Draw related map box onscreen */
			draw_other_map(map2, map);

			GO_OFFSCREEN(map2);

			/* Always draw it offscreen as well! */
			draw_other_map(map2, map);

			RETURN_ONSCREEN(map2);

			SetClip(tmprgn);
			DisposeRgn(tmprgn);
		}
	}
	SetPort(oldport);
}

void
draw_other_map(Map *map, Map *map2)
{
	int sx, sy, sw, sh, wsx;
	Rect tmpRect;

	/* Scale the viewports */
	scale_vp(map->vp, map2->vp, &sx, &sy, &sw, &sh);
	/* Handle non-xwrapped world */
	wsx = sx;
	/* Now draw the other map's box */
	SetRect(&tmpRect, wsx, sy, wsx + sw, sy + sh);
	OffsetRect(&tmpRect, map->conw, map->toph);
	if (map->vp->hw < 8)
	  PenSize(2, 2);
	PenMode(patXor);
	FrameRect(&tmpRect);

	/* UNWRAP the box. */
	if (area.xwrap) {
		/* Draw one extra copy to the left if necessary. */
		if (tmpRect.right > map->vp->sxmax) {
			OffsetRect(&tmpRect, -map->vp->sxmax, 0);
			FrameRect(&tmpRect);
			OffsetRect(&tmpRect, map->vp->sxmax, 0);
		}		
		/* Draw one extra copy to the right if necessary. */
		if (tmpRect.left < 0) {
			OffsetRect(&tmpRect, map->vp->sxmax, 0);
			FrameRect(&tmpRect);
		}		
		/* Draw more copies to the right ON SCREEN if necessary. */
		if (QD(thePort) == map->window) {
			int rest = map->vp->pxw - tmpRect.left;
			while (rest > 0) {
				OffsetRect(&tmpRect, map->vp->sxmax, 0);
				FrameRect(&tmpRect);
				rest -= map->vp->sxmax;
			} 
		}
	}
	PenNormal();
}

void
draw_unit_blast(Map *map, Unit *unit, int blast)
{
	int sx, sy, sw, sh;
	Rect destRect;
	GrafPtr	oldport;
	
	/* Skip this for undrawn units. */
	if (!cell_is_in_gworld(map, unit->x, unit->y))
		return;

	GetPort(&oldport);

	GO_OFFSCREEN(map);

	/* Find offscreen coordinates of the unit rect */
	m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

	draw_blast_image(map->window, sx, sy, sw, sh, blast);

	RETURN_ONSCREEN(map);
	
	/* Dont try to copy a blasted unit outside the map! */
	if (!in_area(unit->x, unit->y))
	  return;
	/* Find onscreen coordinates of the unit rect */
	m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

	/* Set copy destination to the unit rect */
	SetRect(&destRect, sx, sy, sx + sw, sy + sh);
	/* Now copy offscreen map to window */
	copy_from_gworld(map, destRect);			

	SetPort(oldport);
}

/* Draw all the selections of all the units. */

void
draw_selections(Map *map)
{
	int sx, sy, sw, sh, i;
	Unit *unit;
	Rect destRect;
	GrafPtr	oldport;

	GetPort(&oldport);

	GO_OFFSCREEN(map);

	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		draw_selected_unit(map, unit);
	}

	RETURN_ONSCREEN(map);
	
	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];

		/* Skip this also for undrawn units within the map. */
		if (!cell_is_in_gworld(map, unit->x, unit->y))
			break;
		/* Find the pixel coordinates of (x, y). */
		xform(map, unit->x, unit->y, &sx, &sy);
		/* Copy the whole cell in case this was an occ within a big box. */
		SetRect(&destRect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
		/* Now copy offscreen map to window. */
		copy_from_gworld(map, destRect);			
	}
	/* Update the unit info row. */
	if (map->numselections > 0) {
		draw_unit_info(map);
	}
	SetPort(oldport);
}

/* Draw all the selected units in the given cell. */

void
draw_selections_at(Map *map, int x, int y)
{
	int xw, sx, sy, i;
	Unit *unit;
	Rect destRect;
	GrafPtr	oldport;

	/* Skip this for undrawn units. */
	if (!cell_is_in_gworld(map, unit->x, unit->y))
		return;

	xw = wrapx(x);
	GetPort(&oldport);

	GO_OFFSCREEN(map);

	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		if (unit && unit->x == xw && unit->y == y) {
			draw_selected_unit(map, unit);
		}
	}

	RETURN_ONSCREEN(map);
	
	/* Find the pixel coordinates of (x, y) */
	xform(map, x, y, &sx, &sy);
	/* Set destRect to enclose the redrawn cell */
	SetRect(&destRect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
	/* Now copy offscreen map to window */
	copy_from_gworld(map, destRect);			

	/* Update unit info line */
	if (map->numselections > 0) {
		unit = map->selections[0];
		if (in_play(unit) && unit->x == xw && unit->y == y) {
			draw_unit_info(map);
		}
	}
	SetPort(oldport);
}

/*

The code now handles case with transport + occ and also third unit correctly, by using the transports 
enclosing box rather than the whole cell as selection box. Moreover, the selected occ is drawn at normal
magnification, instead of filling up the whole box (the latter caused a clutter of icons on top of each other).
The fact that a specific occ is selected is instead indicated by drawing the emblem for that particular occ, 
in addition to the transports emblem. The magnification lines have been removed since they are not needed
anymore, and since they caused problems with leakage into the grid. 

*/

void
draw_selected_unit(map, unit)
Map *map;
Unit *unit;
{
	int sx, sy, sw, sh, size, wholecell = FALSE, drawmag = FALSE;
	int sx1, sy1, sw1, sh1;
	Rect tmprect;

	if (!in_play(unit))
	  return; /* unselect it too? */
	if (map->vp->uw >= 32) {

		/* First draw the image in smaller ÒselfÓ box */
		m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);

		/* Be sure the selected unit is drawn (MOVED FROM BELOW) */
		UNWRAP_SX(map, sx, 
			draw_unit_image(map->window, sx, sy, sw, sh, unit->type, side_number(unit->side), 
														!completed(unit), TRUE));

		/* Draw unit size on top of advanced unit. */
		if (u_advanced(unit->type) && map->drawsizes) { 
			UNWRAP_SX(map, sx, 
				draw_unit_size(unit, map->window, sx, sy, sw, sh));
		}

		/* Then use bigger box enclosing also occs as for selection box */
		m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
		
		if (map->numselections == 1
			&& sw < 16
			&& (unit->transport != NULL) ) {

			/* Also use the box enclosing transport + occs for selected occ, NOT the whole cell */
			m_xform_unit(map, unit->transport, &sx, &sy, &sw, &sh);

			sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
		}
	} else {
		wholecell = TRUE;
	}
	if (wholecell) {
		xform(map, unit->x, unit->y, &sx, &sy);
		/* Adjust to unit part of cell. */
		sx += (map->vp->hw - map->vp->uw) / 2;  
		sy += (map->vp->hh - map->vp->uh) / 2;
		sw = map->vp->uw;  sh = map->vp->uh;
		/* Be sure the selected unit is drawn. */
		UNWRAP_SX(map, sx, 
			draw_unit_image(map->window, sx, sy, sw, sh, unit->type, side_number(unit->side), 
														!completed(unit), TRUE));
													
		/* Draw unit size on top of advanced unit. */
		if (u_advanced(unit->type) && map->drawsizes) { 
			UNWRAP_SX(map, sx, 
				draw_unit_size(unit, map->window, sx, sy, sw, sh));
		}
	}
	if (0 /* not actually within visible area */)
	  return;
	/* Indicate a unit's plans/tasks in some useful fashion. */
	if (map->vp->draw_plans
		&& unit->plan
		&& unit->plan->tasks) {
		int sx2, sy2, sx3, sy3, rad;
		Task *task = unit->plan->tasks, *nexttask;

		if (task != NULL) {
			if ((nexttask = task->next) != NULL) {
				/* Draw the next task first so it can be overwritten by info
				   about the current task. */
				switch (nexttask->type) {
					case TASK_MOVE_TO:
					case TASK_HIT_UNIT:
						if (in_area(nexttask->args[0], nexttask->args[1])) {
							xform(map, nexttask->args[0], nexttask->args[1], &sx3, &sy3);
							if (task->type == TASK_MOVE_TO) {
								xform(map, task->args[0], task->args[1], &sx2, &sy2);
								sx2 += map->vp->hw/2;  sy2 += map->vp->hh/2;
							} else {
								sx2 = sx + sw/2;  sy2 = sy + sh/2;
							}
							PenPat(QDPat(ltGray));
							MoveTo(sx2, sy2);
							LineTo(sx3 + map->vp->hw/2, sy3 + map->vp->hh/2);
							rad = nexttask->args[3];
							if (nexttask->type == TASK_MOVE_TO && rad > 1) {
								Rect tmprect;

								MoveTo(sx3 - rad * map->vp->hh + map->vp->hw/2, sy3 + map->vp->hh/2);
								LineTo(sx3 + rad * map->vp->hh + map->vp->hw/2, sy3 + map->vp->hh/2);
								MoveTo(sx3 + map->vp->hw/2, sy3 - rad * map->vp->hw + map->vp->hh/2);
								LineTo(sx3 + map->vp->hw/2, sy3 + rad * map->vp->hw + map->vp->hh/2);
								tmprect.left  = sx3 - rad * map->vp->hh;  tmprect.top    = sy3 - rad * map->vp->hw;
								tmprect.right = sx3 + rad * map->vp->hh;  tmprect.bottom = sy3 + rad * map->vp->hw;
								OffsetRect(&tmprect, map->vp->hw/2, map->vp->hh/2);
								UNWRAP_RECT(map, tmprect, 
									FrameOval(&tmprect));
							}
							PenNormal();
						}
				}
			}
			switch (task->type) {
				case TASK_MOVE_TO:
				case TASK_HIT_UNIT:
					if (in_area(task->args[0], task->args[1])) {
						xform(map, task->args[0], task->args[1], &sx2, &sy2);
						PenPat(QDPat(dkGray));
						MoveTo(sx + sw/2, sy + sh/2);
						LineTo(sx2 + map->vp->hw/2, sy2 + map->vp->hh/2);
						rad = task->args[3];
						if (task->type == TASK_MOVE_TO && rad > 1) {
							Rect tmprect;

							MoveTo(sx2 - rad * map->vp->hh + map->vp->hw/2, sy2 + map->vp->hh/2);
							LineTo(sx2 + rad * map->vp->hh + map->vp->hw/2, sy2 + map->vp->hh/2);
							MoveTo(sx2 + map->vp->hw/2, sy2 - rad * map->vp->hw + map->vp->hh/2);
							LineTo(sx2 + map->vp->hw/2, sy2 + rad * map->vp->hw + map->vp->hh/2);
							tmprect.left  = sx2 - rad * map->vp->hh;  tmprect.top    = sy2 - rad * map->vp->hw;
							tmprect.right = sx2 + rad * map->vp->hh;  tmprect.bottom = sy2 + rad * map->vp->hw;
							OffsetRect(&tmprect, map->vp->hw/2, map->vp->hh/2);
							UNWRAP_RECT(map, tmprect, 
								FrameOval(&tmprect));
						}
						PenNormal();
					}
			}
		}
	}

	/* Note: not used any longer, drawmag always false. Remove? */
	/* Draw magnification lines pointing to the true location of the unit. */
	if (drawmag) {
		/* PenPat should already be black. */
		MoveTo(sx,      sy);       LineTo(sx1,       sy1);
		MoveTo(sx + sw, sy);       LineTo(sx1 + sw1, sy1);
		MoveTo(sx,      sy + sh);  LineTo(sx1,       sy1 + sh1);
		MoveTo(sx + sw, sy + sh);  LineTo(sx1 + sw1, sy1 + sh1);
	}

	/* Draw a highlighting rectangle. */
	SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
	/* A hack to prevent leakage into the grid. */
	if (map->vp->draw_grid && map->vp->power == 5)
	  --tmprect.bottom;
	/* First, draw an outer frame, for contrast. */
	if (map->autoselect && unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
		PenPat(&animation_patterns[animation_pattern_state]);
	} else {
		PenPat(QDPat(white));
	}
	UNWRAP_RECT(map, tmprect, 
		FrameRect(&tmprect));
	InsetRect(&tmprect, 1, 1);
	/* Black is for units that can still act, dark gray for actors, gray if the
	   unit can't do anything. */
	PenPat((unit->act && unit->act->initacp > 0) ?
			((unit->act->acp > 0) ? QDPat(black) : QDPat(dkGray)) : QDPat(gray));
	/* Wide border if awake, narrow if asleep or napping. */
	size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
	PenSize(size, size);
	UNWRAP_RECT(map, tmprect, 
		FrameRect(&tmprect));
	PenNormal();
	DGprintf("draw selection of %s at %d,%d\n", unit_desig(unit));
}

/* The code now handles case with transport + occ and also third unit correctly,
   by using the transports enclosing box rather than the whole cell as selection
   box when either transport or occ is selected. The new definition of contentrect
   is also handled. The black enclosing box is now drawn here as well. */

void
draw_selection_animation(map, unit)
Map *map;
Unit *unit;
{
	int sx, sy, sw, sh, wholecell = FALSE, drawmag = FALSE;
	int sx1, sy1, sw1, sh1, size;
	Rect tmprect, tmpRect;
	GrafPtr oldport;
	RgnHandle tmprgn;

	if (!in_play(unit))
	  return; /* unselect it too? */
 	GetPort(&oldport);
	SetPort(map->window);
	tmprgn = NewRgn();
	GetClip(tmprgn);

	/* Clip to map content */
	tmpRect = map->contentrect;

	/* Nessary to do the offset the since we are drawing directly to the screen */
	OffsetRect(&tmpRect, map->vp->sx - map->conw, map->vp->sy - map->toph);

	ClipRect(&tmpRect);

	/* Old SHIFT_ORIGIN macro, now used only here. */
	map->osx = map->vp->sx - map->conw;
	map->osy = map->vp->sy - map->toph;
	SetOrigin(map->osx, map->osy);

	if (map->vp->uw >= 32) {

		/* Use bigger box enclosing also occs for selection box */
		m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

		if (map->numselections == 1
			&& sw < 16
			&& (unit->transport != NULL) ) {

			m_xform_unit(map, unit->transport, &sx, &sy, &sw, &sh);

			sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
		}
	} else {
		xform(map, unit->x, unit->y, &sx, &sy);
		/* Adjust to unit part of cell. */
		sx += (map->vp->hw - map->vp->uw) / 2;  sy += (map->vp->hh - map->vp->uh) / 2;
		sw = map->vp->uw;  sh = map->vp->uh;
	}
	/* Draw a highlighting rectangle. */
	SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
	/* A hack to prevent leakage into the grid. */
	if (map->vp->draw_grid && map->vp->power == 5)
	  --tmprect.bottom;
	/* First, draw an outer white frame, for contrast. */
	if (unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
		PenPat(&animation_patterns[animation_pattern_state]);
	} else {
		PenPat(QDPat(white));
	}
	FrameRect(&tmprect);
	if (area.xwrap) {
		int rest = map->vp->pxw - tmprect.left;
		/* Draw one extra copy to the left if necessary */
		if (tmprect.right > map->vp->sxmax) {
			OffsetRect(&tmprect, -map->vp->sxmax, 0);
			FrameRect(&tmprect);
			OffsetRect(&tmprect, map->vp->sxmax, 0);
		}		
		/* Draw more copies to the right if necessary*/
		while (rest > 0) {
			OffsetRect(&tmprect, map->vp->sxmax, 0);
			FrameRect(&tmprect);
			rest -= map->vp->sxmax;
		} 
	}
	/* This is important or the black box will disappear for transports at low mag. */
	InsetRect(&tmprect, 1, 1);
	/* Black is for units that can still act, dark gray for actors, gray if the unit can't do anything. */
	PenPat((unit->act && unit->act->initacp > 0) ?
			((unit->act->acp > 0) ? QDPat(black) : QDPat(dkGray)) : QDPat(gray));
	/* Wide border if awake, narrow if asleep or napping. */
	size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
	PenSize(size, size);
	FrameRect(&tmprect);
	if (area.xwrap) {
		int rest = map->vp->pxw - tmprect.left;
		/* Draw one extra copy to the left if necessary */
		if (tmprect.right > map->vp->sxmax) {
			OffsetRect(&tmprect, -map->vp->sxmax, 0);
			FrameRect(&tmprect);
			OffsetRect(&tmprect, map->vp->sxmax, 0);
		}		
		/* Draw more copies to the right if necessary*/
		while (rest > 0) {
			OffsetRect(&tmprect, map->vp->sxmax, 0);
			FrameRect(&tmprect);
			rest -= map->vp->sxmax;
		} 
	}
	PenNormal();
	PenPat(QDPat(black));

	/* Old RESET_ORIGIN macro, now used only here. */
	map->osx = 0;  map->osy = 0;
	SetOrigin(map->osx, map->osy);

	SetClip(tmprgn);
	DisposeRgn(tmprgn);
	SetPort(oldport);
}

/* This function is called by the kernel whenever something needs to be updated. */

void  
update_cell_display(Side *side, int x, int y, int rightnow)
{
	Map *map;
	
	if (!active_display(side))
	  return;

	for_all_maps(map) {
		/* If update was only to report changes in undrawn info, maybe don't bother. */
		if ((rightnow == 34 && !map->vp->draw_temperature) 
		    || (rightnow == 37 && !map->vp->draw_clouds)
		    || (rightnow == 35 && !map->vp->draw_winds)
		    || (rightnow == 36 && !map->vp->draw_cover))
			continue;
		update_cell(map, x, y);
	}
}

/* This consolidated function now does all the offscreen drawing formerly done by 
   clear_unit_blast, erase_selection, erase_selections and update_cell_display.
   The new name-ersing algorithm is faster and also catches units that just died. */

void
update_cell(Map *map, int x, int y)
{
	int namelength = 1, cells = 1, sx, sy, i, j;		
	Rect destRect, sourceRect;
	GrafPtr	oldport;
	Unit *unit;

	/* Don't update cells outside the gworld. */ 
	/* Also prevents erase-dead-unit crashes! */
	if (!cell_is_in_gworld(map, x, y))
	   return;

	/* Save the current port. */
	GetPort(&oldport);
	
	/* Find max length of a unit name (skip at 4 x 4 or below). */
	if (map->vp->draw_names && map->vp->power > 2) {
		/* This formula works for most magnifications. */
		namelength = 9 - map->vp->power;
		/* Special case for 8 x 8 magnification. */
		if (map->vp->power == 3)
			namelength = 8;
	}

	/* Then check if an erasing row really is needed. */
	if (namelength > 1 && map->erase_names) {
		for_all_units(unit) {
			/* Only check named units. */
			if (!unit->name)
				continue;
			/* First check if the unit is located at (x, y).*/
			if ((unit->x == wrapx(x) && unit->y == y) || 
			/* Also check if it just left (x, y). Catches unit that died!*/
			     (unit->prevx == wrapx(x) && unit->prevy == y)) {
				/* Make an erasing row if either is true. */
				cells = namelength;
				break;
			}
		}
	}

	/* Find the pixel coordinates of (x, y). */
	xform(map, x, y, &sx, &sy);

	/* Set destRect to enclose the redrawn row of cells. */
	SetRect(&destRect, 
			sx - 2, sy - 2,				/* Add 2 for W & N shorelines. */ 	
			sx + cells * map->vp->hw, 
			sy + map->vp->hh + 4);		/* Add 4 for SE & SW shorelines. */

	GO_OFFSCREEN(map);

	/* First draw a row starting at (x, y) of length cells to
	   erase the name of any unit that moved out of the cell. */
	draw_row(map, x, y, cells, TRUE);

	/* Draw grid explicitly if asked to and the quick method would leak. */
	if (map->vp->draw_grid && map->leaky_grid)
		for (i = x; i < x + cells + 1; i++)
			draw_gridlines(map, i, y - 1);
	/* Redraw partially erased shorelines for cells below the row. */				
	if (map->shorelines) {
		for (i = x; i < x + cells + 1; i++)
			draw_shorelines(map, i, y - 1);
	}
	/* Redraw any partially erased names of units just W of (x, y). */
	/* Note: this is done even if erase_names is not on! */
	if (map->vp->draw_names && map->vp->power > 2) {
		for (i = x - namelength + 1; i <  x; i++) {
			for_all_stack(wrapx(i), y, unit) {
				if (unit->name)	{							
					draw_unit_names_only(map, i, y);
					/* Only do this once for each cell. */
					break;
				}
			}
		}
	}					
	/* Redraw any selected units within the updated row. */
	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		for (j = 0; j < cells; j++) {
			if (unit && unit->x ==  wrapx(x + j) && unit->y == y) {
				draw_selected_unit(map, unit);
			}						
		}
	}
	/* Use gworld source for destRect. */
	sourceRect = destRect;
	/* Find its correct position in the gworld. */
	OffsetRect(&sourceRect, map->offsetx + map->bufx - map->conw, 
			        	             map->offsety + map->bufy - map->toph);

	/* Adjust sourcerect to be at least partially within the gworld. */
	while (area.xwrap && sourceRect.right < 0)
		OffsetRect(&sourceRect, map->vp->sxmax, 0);
	while (area.xwrap && sourceRect.left > map->vp->sxmax)
		OffsetRect(&sourceRect, -map->vp->sxmax, 0);

	/* Redraw any partially erased feature names. */
	if (features_defined() && map->featurenames && map->vp->hh > 3)
		draw_feature_names(map, sourceRect);

	/* Redraw any meridians that will fall within destRect. */ 
	if (map->vp->draw_latlong && map->vp->latlong_interval > 0) {
		/* Use red color if debugging the graphics. */
		/* (black color is always restored by draw_meridians). */
		if (DebugG)
			ForeColor(redColor);
		draw_meridians(map, sourceRect);
	}
	
	RETURN_ONSCREEN(map);

	/* Now copy redrawn rect to the window. */
	if (DebugG)
		/* Make whole gworld visible when debugging. */
		copy_from_gworld(map, map->contentrect);			
	else	copy_from_gworld(map, destRect);			

	/* Update unit info line. */
	if (map->numselections > 0) {
		unit = map->selections[0];
		if (in_play(unit) && unit->x == wrapx(x) && unit->y == y) {
			draw_unit_info(map);
		}
	}
	/* Restore the port. */
	SetPort(oldport);
}

/* Called by update_cell. Redraws the names only for any named units in (x, y).
   Based on merged draw_units & draw_unit_and_occs that have been trimmed down
   Unlike draw_unit_and_occs there is no recursion since names of occs are not shown. */
		
void
draw_unit_names_only(Map *map, int x, int y)
{
	int xw = wrapx(x), sx, sy, sw, sh, sx2, sy2, sw2, sh2;
	Rect tmprect;
	Unit *unit;

	if (!map->see_all &! units_visible(dside, xw, y))
	  return;
	unit = unit_at(xw, y);
	if (unit == NULL)
	  return;

// --- CRASHING BUG FIX ------------------------------------------------

	if (!inside_area(x, y))
	  return;

// -----------------------------------------------------------------

	xform(map, x, y, &sx, &sy);

	/* Draws unit names at small mags where contents of grouping boxes is not shown */
	if (map->vp->uw <= 16) {
		/* Adjust to unit part of cell. */
		sw = map->vp->uw;  sh = map->vp->uh;
		sx += (map->vp->hw - sw) / 2; sy += (map->vp->hh - sw) / 2;
		UNWRAP_SX(map, sx,
			draw_unit_name(unit, sx, sy, sw, sh, 
						 map->textmasks, map->optimize_fonts));
	} else {
		/* Draw unit names at medium and high mags. */
		for_all_stack(xw, y, unit) {
			m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

			/* Draws the name of a boxed carrier with visible occupants */
			if (unit->occupant
				&& sw > 8
				&& (dside->see_all
					|| unit->side == dside
					|| u_see_occupants(unit->type)
					|| side_owns_occupant(dside, unit))) {
				/* Transform carrier to the UL quarter of the box */
				m_xform_occupant(map, unit, unit, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
				/* Always draw city and town names at uniform size */
				if (!mobile(unit->type)) {
				  UNWRAP_SX(map, sx,
					draw_unit_name(unit, sx, sy, map->vp->uw, map->vp->uh, 
				  				 map->textmasks, map->optimize_fonts));  
				} else {
				  /* sw2 doubled moves the name out of the box so that it can be read more easily */
				  UNWRAP_SX(map, sx2,
				  	draw_unit_name(unit, sx2, sy2, 2 * sw2, sh2,
				  				  map->textmasks, map->optimize_fonts)); 
				}
			} else {
				/* Draw the names of all other units. */
				/* Always draw city and town names at uniform size */
				if (!mobile(unit->type)) {
				  UNWRAP_SX(map, sx,
				  	draw_unit_name(unit, sx, sy, map->vp->uw, map->vp->uh, 
				  				 map->textmasks, map->optimize_fonts));  
				} else {
				  UNWRAP_SX(map, sx,
				  	draw_unit_name(unit, sx, sy, sw, sh, 
				  				 map->textmasks, map->optimize_fonts));
				}
			}
		}
	}
}

