/* Row drawing functions 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.  */

/*	Contains draw_row and all the functions that it calls. */

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

extern int any_thickness;

int tmpdrawlighting;
int tmpdrawcoverage;

static void draw_terrain_row(Map *map, int x0, int y0, int len);
static void draw_cliffs(Map *map, int x, int y);
static void draw_contours(Map *map, int x, int y);
static void draw_shorelines(Map *map, int x, int y);
static void draw_borders(Map *map, int x, int y, int b);
static void draw_connections(Map *map, int x, int y, int c);
static void draw_feature_boundary_simple(Map *map, int x, int y);
static void draw_feature_boundary(Map *map, int x, int y);
static void draw_people_row(Map *map, int x, int y);
static void draw_materials(Map *map, int x, int y);
static void draw_units(Map *map, int x, int y);
static void draw_unit_and_occs(Map *map, Unit *unit, int sx, int sy, int sw, int sh);
static void draw_ai_region(Map *map, int x, int y);
static void draw_region(Map *map, int x, int y);

/* These three macros are used to handle cases where an item stretches across the
   edge of a wrapped world. 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. */

/* 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) 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) 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); \
	} \
}

/* For these accessors, x must already be wrapped. */

#define cell_overlay(x, y)  \
  ((dside->designer && !map->see_all) \
   ? ((terrain_view(dside, x, y) == UNSEEN) ? -2 : 0)  \
   : (tmpdrawlighting  \
      ? (night_at(x, y)  \
         ? -2  \
         : (tmpdrawcoverage ? (cover(dside, x, y) == 0 ? -1 : 0) : 0))  \
      : (tmpdrawcoverage  \
          ? (cover(dside, x, y) == 0 ? -1 : 0)  \
          : 0)))

/* Use BACKTTYPE to draw the window background. */

#define cell_terrain(map, x, y)  \
   ((!in_area(x, y)) ? BACKTTYPE :  \
   ((map->see_all || dside->designer) \
   ? terrain_at(x, y)  \
   : (terrain_visible(dside, x, y)  \
	  ? vterrain(terrain_view(dside, x, y))  \
	  : NONTTYPE)))

/* Always redraw cells outside the area, that is the window background. */

#define cell_style(map, x, y, power)  \
  ((map->see_all || terrain_visible(dside, x, y) || unseen_image != NULL || !in_area(x, y))  \
   ? (power >= 3 ? usepolygons : useblocks)  \
   : dontdraw);

/* x0 may be negative here. */

void
draw_row(Map *map, int x0, int y, int len, int clearit)
{
	int	pwr = map->vp->power; 
	int	ang = map->vp->angle; 
	int	empty = FALSE;
	int	x, xw, sx, sy, t;

	if (!between(0, y, area.height - 1))
	    return;
	if (!map->see_all && !map->vp->draw_ai && unseen_image == NULL) {
		empty = TRUE;
		/* Examine row to see if we can skip it entirely. */
		for (x = x0; x < x0 + len; ++x) {
			if (map->see_all || terrain_visible(dside, x, y)
				|| (numbordtypes > 0 && terrain_visible(dside, x, y + 1))) {
					empty = FALSE;
					break;
			}
		}
	}
	if (empty && !clearit)
	    return;

	/* First do run-length based drawing of the terrain. */
	draw_terrain_row(map, x0, y, len);
	if (between(4, map->vp->power, 6) 
	    && !map->solid_color_terrain)
		draw_terrain_transitions(map, x0, y, len);
	/* Draw either cliffs or contour lines, depending on the angle. */
	if ((elevations_defined() || any_thickness) && map->vp->angle != 90) {
		for (x = x0; x < x0 + len; ++x) {
			if (map->see_all || terrain_visible(dside, x, y)) {
				draw_cliffs(map, x, y);
			}
		}
	} else if (elevations_defined() && map->vp->draw_elevations && map->vp->angle == 90) {
		for (x = x0; x < x0 + len; ++x) {
			if (map->see_all || terrain_visible(dside, x, y)) {
				draw_contours(map, x, y);
			}
		}
	}
	/* Draw grid explicitly if asked to and the quick method using smaller cells doesn't work. */
	if (map->vp->draw_grid && map->leaky_grid) {
		for (x = x0; x < x0 + len + 1; ++x)		/* Draw one extra cell here! */
		  draw_gridlines(map, x, y);
	}
	/* Draw shorelines if desired. */
	if (map->shorelines) {
		for (x = x0; x < x0 + len + 1; ++x) {
			/* (Should modify draw_borders so that all six
			border are drawn for each cell instead). */
			draw_shores(map, x, y);
			draw_shores(map, x, y -1);
		}
	}
	/* Draw connections and borders. */
	if (any_aux_terrain_defined()) {
		if (bords_to_draw(map)) {
			for_all_border_types(t) {
				if (map->vp->draw_aux_terrain[t] 
					&& aux_terrain_defined(t)) {
						/* (Should modify draw_borders so that all six
						border are drawn for each cell instead). */
						for (x = x0; x < x0 + len + 1; ++x) {
  							draw_borders(map, x, y, t);
  							draw_borders(map, x, y -1, t);
						}
				}
			}
		}
		/* Connections should always be drawn on top of borders. */
		if (conns_to_draw(map)) {
			for_all_connection_types(t) {
				if (map->vp->draw_aux_terrain[t] 
					&& aux_terrain_defined(t)) {
						for (x = x0; x < x0 + len; ++x)
							draw_connections(map, x, y, t);
				}
			}
		}
	}
	/* Draw clouds, winds and temperatures. */
	if (clouds_defined() && map->vp->draw_clouds) {
		for (x = x0; x < x0 + len; ++x) {
			xw = wrapx(x);					
			if (in_area(x,y) && draw_clouds_here(xw, y)) {				/* xform crash fix. */	
				xform(map, x, y, &sx, &sy);
				if (map->vp->hh <= 16) {
				    	 UNWRAP_SX(map, sx, 
				    		draw_cloud_block(sx, sy, 1, pwr, cloud_view(dside, xw, y), ang));
				} else UNWRAP_SX(map, sx, 
						draw_clouds(sx, sy, pwr, cloud_view(dside, xw, y)));
			}
		}
	}
	if (winds_defined() && map->vp->draw_winds && map->vp->hh > 10) { 
		for (x = x0; x < x0 + len; ++x) {
			xw = wrapx(x);						
			if (in_area(x,y) && draw_winds_here(dside, xw, y)) {			/* xform crash fix. */
				xform(map, x, y, &sx, &sy);
				UNWRAP_SX(map, sx, 
					draw_winds(sx, sy, pwr, wind_view(dside, xw, y)));
			}
		}
	}
	if (temperatures_defined() && map->vp->draw_temperature && map->vp->hh > 10) {
		for (x = x0; x < x0 + len; ++x) {
			xw = wrapx(x);							
			if (in_area(x,y) && draw_temperature_here(dside, xw, y)) {	/* xform crash fix. */
				xform(map, x, y, &sx, &sy);
				UNWRAP_SX(map, sx, 
					draw_temperature(sx, sy, pwr, temperature_view(dside, xw, y)));
			}
		}
	}

	/* Although we had to draw the terrain on the edge, we can skip everything else,
	   since edge cells have no units, features, etc. */
	/* Skip the top and bottom edge rows. */
	if (!between(1, y, area.height - 2)) 
	  return;
	/* Shorten the row by one cell on each side, if those are edge cells. */
	if (!inside_area(x0 + len - 1, y))
	  --len;
	if (!inside_area(x0, y)) {
		++x0;
		--len;
	}
	if (len <= 0)
	  return;

	/* Draw feature boundaries. */
	if (features_defined() && map->featureborders && map->vp->hw > 4) {
		for (x = x0; x < x0 + len; ++x) {
			if (map->simple_borders) {
				draw_feature_boundary_simple(map, x, y);
			} else {
				draw_feature_boundary(map, x, y);
			}
		}
	}

	/* Draw cell materials. */
	if (any_cell_materials_defined() && map->vp->num_materials_to_draw > 0 && map->vp->hh > 20) {
		for (x = x0; x < x0 + len; ++x)
		  draw_materials(map, x, y);
	}
	/* Draw people rows and country borders. */
	if ((people_sides_defined() || control_sides_defined()) && bwid2[pwr] > 0) {
		for (x = x0; x < x0 + len; ++x)
		  draw_people_row(map, x, y);
	}
	/* Draw units. */
	if (map->vp->draw_units && map->vp->hw > 2) {
		for (x = x0; x < x0 + len; ++x)
		  draw_units(map, x, y);
	}
	/* Draw ai regions. */
	if (map->vp->draw_ai) {
		for (x = x0; x < x0 + len; ++x)
		  draw_ai_region(map, x, y);
	}
	if (DebugG) {
		for (x = x0; x < x0 + len; ++x)
		  draw_region(map, x, y);
	}
	/* If debugging, draw coverage on top of everything else. */
	if (DebugG && !dside->see_all && map->vp->hw >= 16) {
		for (x = x0; x < x0 + len; ++x) {
			xw = wrapx(x);	
			xform(map, x, y, &sx, &sy);		/* No need to test for in_area here. */
			UNWRAP_SX(map, sx, 
				draw_coverage(sx, sy, pwr, cover(dside, xw, y), alt_cover(dside, xw, y)));
		}
	}
}

/* Draw an entire row of terrain, possibly with a single rectangle fill. */
/* x0 may be negative. */

void
draw_terrain_row(Map *map, int x0, int y, int len)
{
	int 	x0w = wrapx(x0), x, xw, x1 = x0, sx, sy, i = 0, quasirand;
	int 	style, segstyle, terr, segterr, over, segover;
	int 	dosolid = map->solid_color_terrain;
	int 	dofill = map->vp->draw_cell_pats;
	int 	pwr = map->vp->power;
	int	ang = map->vp->angle;
	int 	dogrid = FALSE;

	/* Don't use quick grid method if it would leak. */ 
	if (map->vp->draw_grid &! map->leaky_grid) 
		dogrid = TRUE;

	/* This stuff is used in the cell_overlay macro. */
	tmpdrawlighting = map->vp->draw_lighting;
	tmpdrawcoverage = (!dside->see_all && map->vp->draw_cover);

	segstyle = cell_style(map, x0w, y, pwr);
	segterr = cell_terrain(map, x0w, y);
	segover = cell_overlay(x0w, y);

	/* Note that in this loop we do NOT wrap x for drawing, just for looking at layer data. */
	for (x = x0; x < x0 + len + 1; ++x) {
		xw = wrapx(x);
		style = cell_style(map, xw, y, pwr);
		terr = cell_terrain(map, xw, y);
		over = cell_overlay(xw, y);

		/* Decide if the run is over and we need to dump some output. */
		if (x == x0 + len
			|| style != segstyle
			|| terr != segterr
			|| over != segover
			|| segstyle == usepictures
			|| segstyle == usepolygons
			|| ang != 90) {

			if (segstyle != dontdraw) {
				/* Never use grid on the window background. */
				if (segterr == BACKTTYPE) 
					dogrid = FALSE;
				xform(map, x1, y, &sx, &sy);
				switch (segstyle) {
					case useblocks:
						UNWRAP_SX(map, sx, 
							draw_cell_block(sx, sy, i, pwr, segterr, segover, ang, dosolid));													
						break;
					case usepictures:
						break;
					case usepolygons:
						/* (quasirand % numsubimages) is used to pick a terrain subimage in 
						draw_hex_region. This will cause a null pointer crash if quasirand < 0. */
						quasirand = abs((11 * x1 + (13 * y) + 7) * 41);
						UNWRAP_SX(map, sx, 
							draw_hex_region(sx, sy, pwr, segterr, segover, ang, dogrid, dosolid, quasirand));
				}
			}
			/* Set up for the next segment. */
			i = 0;
			x1 = x;
			segstyle = style;
			segterr = terr;
			segover = over;
		}
		++i;
	}
}

static void
draw_cliffs(Map *map, int x, int y)
{
	int xw, elev, x1, y1, drop, t, t1, drawit, elev1, offset;
	int sx, sy, sx1, sy1;
	PolyHandle poly;
	RGBColor hexcolor, oldcolor;

	xw = wrapx(x);
	/* Prevents crashes when trying to get thickness of BACKTTYPE. */
	if (!in_area(xw, y))
		return;
	t = cell_terrain(map, xw, y);
	elev = (elevations_defined() ? elev_at(xw, y) : 0) + t_thickness(t);
	drawit = FALSE;
	if (point_in_dir(xw, y, SOUTHWEST, &x1, &y1)) {
		t1 = cell_terrain(map, x1, y1);
		if (t1 != NONTTYPE) {
			drop = elev - ((elevations_defined() ? elev_at(wrapx(x1), y1) : 0) + t_thickness(t1));
			if (drop > 0) {
				drawit = TRUE;
				xform(map, x, y, &sx, &sy);
				xform(map, x1, y1, &sx1, &sy1);
			}
		}
	} else {
		drawit = TRUE;
		xform(map, x, y, &sx, &sy);
	    elev1 = elev - (elevations_defined() ? area.minelev : 0);
	    elev1 *= map->vp->vertscale;
	    offset = (elev1 * map->vp->hh) / map->vp->cellwidth;
	    sy1 = sy + map->vp->hch + offset; 
	}
	if (drawit) {
		if (sy1 > sy + map->vp->hch + (map->vp->draw_grid ? 1 : 0)) {
			poly = OpenPoly();
			MoveTo(sx, sy + map->vp->hch);
			LineTo(sx + map->vp->hw / 2, sy + map->vp->hh);
			LineTo(sx + map->vp->hw / 2, sy1 + (map->vp->hh - map->vp->hch));
			LineTo(sx, sy1);
			LineTo(sx, sy + map->vp->hch);
			ClosePoly();
			/* if (timg->colrpat != nil
			    && (minscreendepth > 1 || !timg->patdefined)) {
				FillCPoly(poly, timg->colrpat);
			} else */ if (tcolors[t] != NULL && maxscreendepth > 1) {
				hexcolor.red   = tcolors[t]->color.red / 2;
				hexcolor.green = tcolors[t]->color.green / 2;
				hexcolor.blue  = tcolors[t]->color.blue / 2;
				RGBForeColor(&hexcolor);
				UNWRAP_POLY(map, poly, 
					PaintPoly(poly));
				/* Restore the previous color. */
				RGBForeColor(&blackcolor);
			} else {
				hexcolor.red   = (128 << 8) / 2;
				hexcolor.green = (128 << 8) / 2;
				hexcolor.blue  = (128 << 8) / 2;
				RGBForeColor(&hexcolor);
				UNWRAP_POLY(map, poly, 
					PaintPoly(poly));
				/* Restore the previous color. */
				RGBForeColor(&blackcolor);
			}
			KillPoly(poly);
		}
	}
	drawit = FALSE;
	if (point_in_dir(xw, y, SOUTHEAST, &x1, &y1)) {
		t1 = cell_terrain(map, x1, y1);
		if (t1 != NONTTYPE) {
			drop = elev - ((elevations_defined() ? elev_at(wrapx(x1), y1) : 0) + t_thickness(t1));
			if (drop > 0) {
				drawit = TRUE;
				xform(map, x, y, &sx, &sy);
				xform(map, x1, y1, &sx1, &sy1);
			}
		}
	} else {
		drawit = TRUE;
		xform(map, x, y, &sx, &sy);
	    elev1 = elev - (elevations_defined() ? area.minelev : 0);
	    elev1 *= map->vp->vertscale;
	    offset = (elev1 * map->vp->hh) / map->vp->cellwidth;
	    sy1 = sy + map->vp->hch + offset; 
	}
	if (drawit) {
		if (sy1 > sy + map->vp->hch + (map->vp->draw_grid ? 1 : 0)) {
			poly = OpenPoly();
			MoveTo(sx + map->vp->hw - (map->vp->draw_grid ? 1 : 0), sy + map->vp->hch);
			LineTo(sx + map->vp->hw - (map->vp->draw_grid ? 1 : 0), sy1);
			LineTo(sx + map->vp->hw / 2, sy1 + (map->vp->hh - map->vp->hch));
			LineTo(sx + map->vp->hw / 2, sy + map->vp->hh);
			LineTo(sx + map->vp->hw - (map->vp->draw_grid ? 1 : 0), sy + map->vp->hch);
			ClosePoly();
			/* if (timg->colrpat != nil
			    && (minscreendepth > 1 || !timg->patdefined)) {
				FillCPoly(poly, timg->colrpat);
			} else */ if (tcolors[t] != NULL && maxscreendepth > 1) {
				hexcolor.red   = (3 * tcolors[t]->color.red) / 4;
				hexcolor.green = (3 * tcolors[t]->color.green) / 4;
				hexcolor.blue  = (3 * tcolors[t]->color.blue) / 4;
				RGBForeColor(&hexcolor);
				UNWRAP_POLY(map, poly, 
					PaintPoly(poly));
				/* Restore the previous color. */
				RGBForeColor(&blackcolor);
			} else {
				hexcolor.red   = (3 * 128 << 8) / 4;
				hexcolor.green = (3 * 128 << 8) / 4;
				hexcolor.blue  = (3 * 128 << 8) / 4;
				RGBForeColor(&hexcolor);
				UNWRAP_POLY(map, poly, 
					PaintPoly(poly));
				/* Restore the previous color. */
				RGBForeColor(&blackcolor);
			}
			KillPoly(poly);
		}
	}
}

static void
draw_contours(Map *map, int x, int y)
{
	int i, sx, sy, numlines;
	LineSegment *lines;

	if (map->vp->contour_interval < 1)
	  return;
	xform(map, x, y, &sx, &sy);
	contour_lines_at(map->vp, wrapx(x), y, sx, sy, &lines, &numlines);
	RGBForeColor(&contourcolor);
	for (i = 0; i < numlines; ++i) {

		/* Draw the line. */
		MoveTo(lines[i].sx1, lines[i].sy1); 
		LineTo(lines[i].sx2, lines[i].sy2);

		/* UNWRAP line if necessary. */
		if (area.xwrap && sx < 0) {
			lines[i].sx1 += map->vp->sxmax;
			lines[i].sx2 += map->vp->sxmax;
			MoveTo(lines[i].sx1, lines[i].sy1); 
			LineTo(lines[i].sx2, lines[i].sy2);
		}
		/* Trigger unwrapping already within 1 hex from right edge. */
		else if (area.xwrap && sx > map->vp->sxmax - map->vp->hw) {
			lines[i].sx1 -= map->vp->sxmax;
			lines[i].sx2 -= map->vp->sxmax;
			MoveTo(lines[i].sx1, lines[i].sy1); 
			LineTo(lines[i].sx2, lines[i].sy2);
		}
	}
	ForeColor(blackColor);
}

/* This function draws the grid explicitly on top of the map. It is used when
   gridcolor differs from unseencolor, and also when map->erase_names is on,
   since erased names otherwise will leak into the grid. Drawing the grid
   explicitly is about 30% slower than using the quick method based on the
   background color and smaller cells. */

void 
draw_gridlines(Map *map, int x, int y)
{
	int xw, dir, x1, y1, wid, dx1, dy1, dx2, dy2, sx, sy, cliffs = FALSE;
	int pwr = map->vp->power;
	int ang = map->vp->angle;
	
	/* (should use generic drawing style test) */
	if (pwr < 4)
	  return;

	xw = wrapx(x);
	/* Dont draw outside the area (causes crashes) */
	if (!in_area(xw, y))
	  return;
	/* Dont draw grid if (xw, y) is invisible */
	if (!terrain_visible(dside, xw, y))
	  return;
	/* Very important to use UNWRAPPED x here! */
	xform(map, x, y, &sx, &sy);

	/* Check if we are dealing with cliffs. */
	if ((elevations_defined() || any_thickness) && ang != 90)
	  cliffs = TRUE;

	RGBForeColor(&gridcolor);
	PenPat(QDPat(black));
	PenSize(1, 1);

	for_all_directions(dir) {
		/* Draw SE, SW and E gridlines only if we are dealing with cliffs */
		if ((dir == EAST || dir == SOUTHEAST || dir == SOUTHWEST) &! cliffs)
		  continue;
		/* Dont draw grid lines on the edge or to invisible hexes */
		if (!point_in_dir(xw, y, dir, &x1, &y1))
		  continue;
		if (!terrain_visible(dside, wrapx(x1), y1))
		  continue;

		/* Calculate start point. */
		dx1 = bsx[pwr][dir]; 
		dy1 = bsy[pwr][dir];
		if (ang == 30)
		  dy1 /= 2;
		if (ang == 15)
		  dy1 /= 4;
		if (dir == WEST || dir == EAST)
		  dx1 -=1;

		/* Calculate end point. */
		dx2 = bsx[pwr][dir+1]; 
		dy2 = bsy[pwr][dir+1];
		if (ang == 30)
		  dy2 /= 2;
		if (ang == 15)
		  dy2 /= 4;
		if (dir == WEST || dir == EAST)
		  dx2 -=1;

		/* Draw the line. */
		MoveTo(sx + dx1, sy + dy1 - 1);
		LineTo(sx + dx2, sy + dy2 - 1);

		/* UNWRAP line if necessary. */
		if (area.xwrap && sx < 0) {
			sx += map->vp->sxmax;
			MoveTo(sx + dx1, sy + dy1 - 1);
			LineTo(sx + dx2, sy + dy2 - 1);
			sx -= map->vp->sxmax;
		}
		/* Trigger unwrapping already within 1 hex from right edge. */
		else if (area.xwrap && sx > map->vp->sxmax - map->vp->hw) {
			sx -= map->vp->sxmax;
			MoveTo(sx + dx1, sy + dy1 - 1);
			LineTo(sx + dx2, sy + dy2 - 1);
			sx += map->vp->sxmax;
		}
	}
	PenNormal();
	ForeColor(blackColor);
}

void 
draw_shores(Map *map, int x, int y)
{
	int	sx, sy, power;
	int	xw = wrapx(x);
	Image	*timg;

	power = map->vp->power;
	if (!inside_area(xw, y))
	  return;
	/* Dont draw shores if (xw, y) is invisible */
	if (!map->see_all && !terrain_visible(dside, xw, y))
	  return;
	  
	/* Use border images if available and asked for. */
	timg = best_image(beach, hws[power], hhs[power]);
	if (between(4, power, 6)
	      && timg->isborder
	      && timg->subimages != NULL
	      && map->vp->angle == 90				
	      && !map->solid_color_terrain) {
		 draw_shore_images(map, x, y);
	} else draw_shorelines(map, x, y);
}

/* This function draws shorelines in 3D relief, using shorecolor. */

void 
draw_shorelines(Map *map, int x, int y)
{
	int	dir, x1, y1, dx1, dy1, dx2, dy2, sx, sy;
	int	t, elev, offset = 0, cliffs = FALSE;
	int	pwr = map->vp->power;
	int	ang = map->vp->angle;
	int	wid = bwid[pwr];
	int	wid2 = wid / 2;
	int	xw = wrapx(x);

	if (wid == 0)
	  return;
	/* Very important to use UNWRAPPED x here! */
	xform(map, x, y, &sx, &sy);

	/* Check if we have cliffs and calculate offset imposed by xform. */
	if ((elevations_defined() || any_thickness) && ang != 90) {
		cliffs = TRUE;
		t = cell_terrain(map, xw, y);
		elev   = (elevations_defined() ? elev_at(xw, y) : 0) + t_thickness(t);
		elev -= (elevations_defined() ? area.minelev : 0);
		elev *= map->vp->vertscale;
		offset = (elev * map->vp->hh) / map->vp->cellwidth;
	}
	
	RGBForeColor(&shorecolor);
	PenPat(QDPat(black));

	for_all_directions(dir) {
		/* Used to kill xform offset. */
		int adjust = 0;

		/* Never draw SE and SW shores of a given hex */
		if (dir == SOUTHEAST || dir == SOUTHWEST)
		  continue;
		/* Only draw E shore if we have cliffs. */
		if (dir == EAST &! cliffs)
		  continue;
		/* Dont draw shores on the edge or to invisible hexes */
		if (!point_in_dir(xw, y, dir, &x1, &y1))
		  continue;
		if (!terrain_visible(dside, wrapx(x1), y1))
		  continue;

		/* Use narrow pen */
		PenSize(wid2, wid2);

		/* WATER HEX */
		if (t_liquid(terrain_at(xw, y))) {
			/* Dont draw shores to other water hexes */
			if (t_liquid(terrain_at(wrapx(x1), y1)))
			  continue;
			/* Draw NE and NW shores using wide pen. */
			if (dir == NORTHWEST || dir == NORTHEAST)
			  PenSize(wid2, wid + wid2);
		/* LAND HEX */
		} else {
			/* Dont draw shores to other land hexes. */ 
			if (!t_liquid(terrain_at(wrapx(x1), y1)))
		  	  continue;
			if (offset) {
				/* Decide if imposed offset should be removed. */
				if ((dir == WEST && in_area (wrapx(x - 1), y + 1) 
				  &! t_liquid(terrain_at(wrapx(x - 1), y + 1)))
				  || (dir == EAST && in_area (xw, y + 1) 
				  &! t_liquid(terrain_at(xw, y + 1)))) {
					adjust = offset;
				}
			}
		}

		/* Calculate start point. */
		dx1 = bsx[pwr][dir]; 
		dy1 = bsy[pwr][dir];
		if (ang == 30) 
		    dy1 /= 2;
		if (ang == 15) 
		    dy1 /= 4;
		if (dir == NORTHWEST || dir == NORTHEAST) {
			if (!t_liquid(terrain_at(xw, y)))
			  dx1 += 1; 
			else if (pwr > 2)
			  dy1 -= 1;
		}

		/* Calculate end point. */
		dx2 = bsx[pwr][right_dir(dir)]; 
		dy2 = bsy[pwr][right_dir(dir)];
		if (ang == 30) 
		    dy2 /= 2;
		if (ang == 15) 
		    dy2 /= 4;
		if (dir == NORTHWEST /*|| dir == NORTHEAST*/) {
			if (!t_liquid(terrain_at(xw, y)))
			  dx2 += 1; 
			else if (pwr > 2)
			if (pwr > 2)
			  dy2 -= 1;
		}

		/* Draw the line. */
		MoveTo(sx + dx1 - wid2, sy + dy1 - 1 + adjust);
		LineTo(sx + dx2 - wid2, sy + dy2 - 1 + adjust);

		/* UNWRAP line if necessary. */
		if (area.xwrap && sx < 0) {
			sx += map->vp->sxmax;
			MoveTo(sx + dx1 - wid2, sy + dy1 - 1 + adjust);
			LineTo(sx + dx2 - wid2, sy + dy2 - 1 + adjust);
			sx -= map->vp->sxmax;
		}
		/* Trigger unwrapping already within 1 hex from right edge. */
		else if (area.xwrap && sx > map->vp->sxmax - map->vp->hw) {
			sx -= map->vp->sxmax;
			MoveTo(sx + dx1 - wid2, sy + dy1 - 1 + adjust);
			LineTo(sx + dx2 - wid2, sy + dy2 - 1 + adjust);
			sx += map->vp->sxmax;
		}
	}
	PenNormal();
	ForeColor(blackColor);
}

static void
draw_borders(Map *map, int x, int y, int b)
{
	int 		xw = wrapx(x), dir, sx, sy, bitmask = 0, over;
	int		power = map->vp->power;	
	Image	*timg;
	
	if (!inside_area(xw, y))
	  return;
 	if (!map->see_all && !terrain_visible(dside, xw, y))
	    return;
	/* Use border images if available and asked for. */
	timg = best_image(timages[b], hws[power], hhs[power]);
	if (between(4, power, 6)
	      && timg->isborder
	      && timg->subimages != NULL
	      && map->vp->angle == 90				
	      && !map->solid_color_terrain) {
		draw_border_images(map, x, y, b);
		return;
	}
	/* This check must be done after image drawing since it relies on cells
	without any_borders_at for the endpoints. */ 
	if (!any_borders_at(xw, y, b))
	    return;

	/* Else use line drawing. */
	for_all_directions(dir) {
		if (border_at(xw, y, dir, b) && borders_visible(dside, xw, y, dir)) {
			bitmask |= 1 << dir;
		}
	}
	if (bitmask != 0) {
		tmpdrawlighting = map->vp->draw_lighting;
		tmpdrawcoverage = (!dside->see_all && map->vp->draw_cover);
		over = cell_overlay(xw, y);
		xform(map, x, y, &sx, &sy);
		/* (should compute and pass in overlay) */
		UNWRAP_SX(map, sx, 
			draw_border_line_multiple(map->window, sx, sy, bitmask, map->vp->power, b, 
								map->vp->angle, over, map->solid_color_terrain));		
	}
}

/* Draw all the connections of the given cell. */

static void
draw_connections(Map *map, int x, int y, int c)
{
	int xw = wrapx(x), dir, sx, sy, bitmask = 0, over;
	
	if (!(map->see_all || terrain_visible(dside, xw, y)) || !any_connections_at(xw, y, c) || !tt_drawable(c, terrain_at(xw, y)))
	  return;
	for_all_directions(dir) {
		if (connection_at(xw, y, dir, c)) {
			bitmask |= 1 << dir;
		}
	}
	if (bitmask != 0) {
		tmpdrawlighting = map->vp->draw_lighting;
		tmpdrawcoverage = (!dside->see_all && map->vp->draw_cover);
		over = cell_overlay(xw, y);
		xform(map, x, y, &sx, &sy);
		/* (should compute and pass in overlay) */
		UNWRAP_SX(map, sx, 
			draw_connection_line_multiple(map->window, sx, sy, bitmask, map->vp->power, c, 
									map->vp->angle, over, map->solid_color_terrain));
	}
}

/* This function draws feature borders as a single (not double) lines, and only
   between land areas.  It is an alternative to draw_feature_boundaries which is
   invoked by chosing map->simple_borders. */

void 
draw_feature_boundary_simple(Map *map, int x, int y)
{
	int 	xw, dir, x1, y1, pwr, wid, wid2, dx1, dy1, dx2, dy2, sx, sy;

	pwr = map->vp->power;
	wid = bwid2[pwr];
	if (wid == 0)
	  return;
	xw = wrapx(x);
	if (!terrain_visible(dside, xw, y))
	  return;
	if (t_liquid(terrain_at(xw, y)))
	  return;
	PenSize(wid, wid);
	RGBForeColor(&featurecolor);
	PenPat(QDPat(black));
	wid2 = wid / 2;
	/* Very important to use UNWRAPPED x here! */
	xform(map, x, y, &sx, &sy);

	for_all_directions(dir) {
		if (!interior_point_in_dir(xw, y, dir, &x1, &y1))
		  continue;
		if (t_liquid(terrain_at(wrapx(x1), y1)))
		  continue;
		if (!terrain_visible(dside, wrapx(x1), y1))
		  continue;
		if (feature_at(xw, y) == feature_at(wrapx(x1), y1))
		  continue;

		dx1 = bsx[pwr][dir]; dy1 = bsy[pwr][dir];
		if (map->vp->angle == 30)
		  dy1 /= 2;
		if (map->vp->angle == 15)
		  dy1 /= 4;

		dx2 = bsx[pwr][dir+1]; dy2 = bsy[pwr][dir+1];
		if (map->vp->angle == 30)
		  dy2 /= 2;
		if (map->vp->angle == 15)
		  dy2 /= 4;

		/* Draw the line. */
		MoveTo(sx + dx1 - wid2, sy + dy1 - wid2);
		LineTo(sx + dx2 - wid2, sy + dy2 - wid2);

		/* UNWRAP line if necessary. */
		if (area.xwrap && sx < 0) {
			sx += map->vp->sxmax;
			MoveTo(sx + dx1 - wid2, sy + dy1 - wid2);
			LineTo(sx + dx2 - wid2, sy + dy2 - wid2);
			sx -= map->vp->sxmax;
		}
		/* Trigger unwrapping already within 1 hex from right edge. */
		else if (area.xwrap && sx > map->vp->sxmax - map->vp->hw) {
			sx -= map->vp->sxmax;
			MoveTo(sx + dx1 - wid2, sy + dy1 - wid2);
			LineTo(sx + dx2 - wid2, sy + dy2 - wid2);
			sx += map->vp->sxmax;
		}
	}
	PenNormal();
	ForeColor(blackColor);
}

static void
draw_feature_boundary(Map *map, int x, int y)
{
	int xw, fid, fid1, dir, x1, y1, bitmask, sx, sy;
	Feature *feature;

    xw = wrapx(x);
	if (terrain_visible(dside, xw, y)) {
		fid = raw_feature_at(xw, y);
		if (fid == 0)
		  return;
		bitmask = 0;
		/* Decide which edges are borders of the feature. */
		for_all_directions(dir) {
			/* Don't do anything about edge cells. */
			if (interior_point_in_dir(xw, y, dir, &x1, &y1)) {
				fid1 = raw_feature_at(wrapx(x1), y1);
				if (fid != fid1) {
					bitmask |= 1 << dir;
				}
			}
		}
		if (bitmask != 0) {
			xform(map, x, y, &sx, &sy);
			if (bitmask != 0) {
			  UNWRAP_SX(map, sx, 
				draw_feature_borders(map->window, sx, sy, bitmask, map->vp->power));
			}
		}
	}
}

/* Indicate what kind of people are living in the given row. */

static void
draw_people_row(Map *map, int x, int y)
{
	int 	pop, xw, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
	int 	con, con1, bitmask1, bitmask2, bitmask3, drawemblemhere;
	int	pwr = map->vp->power; 
	int	ang = map->vp->angle; 

	xw = wrapx(x);
	if (!terrain_visible(dside, xw, y))
	  return;
	pop = (people_sides_defined() ? people_side_at(xw, y) : NOBODY);
	con = (control_sides_defined() ? control_side_at(xw, y) : NOCONTROL);
	bitmask1 = bitmask2 = bitmask3 = 0;
	drawemblemhere = FALSE;
	/* Decide which edges are borders of the country. */
	for_all_directions(dir) {
		/* Don't do anything about edge cells. */
		if (interior_point_in_dir(xw, y, dir, &x1, &y1)) {
			if (terrain_visible(dside, wrapx(x1), y1)) {
				pop1 = (people_sides_defined() ? people_side_at(wrapx(x1), y1) : NOBODY);
				con1 = (control_sides_defined() ? control_side_at(wrapx(x1), y1) : NOCONTROL);
				if (con != con1) {
					if (con == NOCONTROL || con1 == NOCONTROL) {
						bitmask2 |= 1 << dir;
					} else {
						bitmask1 |= 1 << dir;
					}
				} else if (pop != pop1) {
					/* Borders with uninhabitated regions are drawn differently. */
					if (pop == NOBODY || pop1 == NOBODY) {
						bitmask2 |= 1 << dir;
					} else if (control_sides_defined()) {
						bitmask3 |= 1 << dir;
					} else {
						bitmask1 |= 1 << dir;
					}
				}
			} else {
				/* Draw just people in the cells right at the edge of the known world. */
				drawemblemhere = TRUE;
			}
		}
	}
	/* Now draw both the edges and an emblem for the cell. */
	if ((bitmask1 | bitmask2 | bitmask3) != 0
		|| ((map->vp->draw_people || map->vp->draw_control) && drawemblemhere)) {
		/* Important to use unwrapped x! */
		xform(map, x, y, &sx, &sy);
		RGBForeColor(&frontcolor);		/* Use customized frontline color */
		if (bitmask1 != 0) {
		  UNWRAP_SX(map, sx, 
		  	draw_country_borders(map->window, sx, sy, bitmask1, pwr, 0, ang));
		}
		/* Do not use shaded border to sea and empty land if simple_borders is on. */
		if (bitmask2 != 0 && !map->simple_borders) {
		  UNWRAP_SX(map, sx, 
		  	draw_country_borders(map->window, sx, sy, bitmask2, pwr, 2, ang));
		}
		if (bitmask3 != 0 && !map->simple_borders) {
		  UNWRAP_SX(map, sx, 
		  	draw_country_borders(map->window, sx, sy, bitmask3, pwr, 1, ang));
		}
		ForeColor(blackColor);					/* Restore Fore Color */

		/* Draw an emblem for the people in the cell. */
		if (map->vp->draw_people && pop != NOBODY) {
			sw = map->vp->uw;  sh = map->vp->uh;
			ew = min(sw, max(8, sw / 2));  eh = min(sh, max(8, sh / 2));
			ex = sx + (map->vp->hw - map->vp->uw) / 2 + sw / 2 - ew / 2;  
			ey = sy + (map->vp->hh - map->vp->uh) / 2 + sh / 2 - eh / 2;
			/* Maybe draw emblem so it will be visible underneath control emblem. */
			if (map->vp->draw_control && con != pop) {
				ex += 2;  ey += 2;
			}
			UNWRAP_SX(map, ex, 
				draw_side_emblem(map->window, ex, ey, ew, eh, pop, plain_emblem));
		}
		if (map->vp->draw_control && con != NOCONTROL && (bitmask1 | bitmask2) != 0) {
			sw = map->vp->uw;  sh = map->vp->uh;
			ew = min(sw, max(8, sw / 2));  eh = min(sh, max(8, sh / 2));
			ex = sx + (map->vp->hw - map->vp->uw) / 2 + sw / 2 - ew / 2;  
			ey = sy + (map->vp->hh - map->vp->uh) / 2 + sh / 2 - eh / 2;
			UNWRAP_SX(map, ex, 
				draw_side_emblem(map->window, ex, ey, ew, eh, con, plain_emblem));
		}
	}
}

/* This draws a small set of bar charts, one for each material type. */

static void
draw_materials(Map *map, int x, int y)
{
	int m, t, sx, sy, mx, my, mw, mh, amt, maxamt, h;
	Rect graphrect;
	
	if (nummtypes == 0)
	  return;
	mw = map->vp->uw / nummtypes /* should be count of displayable materials... */;
	mh = map->vp->uh;
	if (mw <= 2 || mh <= 2)
	  return;
	/* (should this be apparent instead of real terrain?) */
	t = cell_terrain(map, wrapx(x), y);
	xform(map, x, y, &sx, &sy);
	mx = sx + (map->vp->hw - map->vp->uw) / 2;  my = sy + (map->vp->hh - map->vp->uh) / 2;
	for_all_material_types(m) {
		if (map->vp->draw_materials[m] && (maxamt = tm_storage_x(t, m)) > 0) {
			SetRect(&graphrect, mx + m * mw, my, mx + (m + 1) * mw, my + map->vp->uh);
			UNWRAP_RECT(map, graphrect, 
				FrameRect(&graphrect));
			amt = material_view(dside, wrapx(x), y, m);
			h = (amt * mh) / maxamt;
			graphrect.top -= (mh - h);
			/* (should use a per-material color/pattern if available) */
			UNWRAP_RECT(map, graphrect, 
				FillRect(&graphrect, QDPat(black)));
		}
	}
}

/* This function draws units at a small magnification */
/* Draw all the units visible in the given cell. */
/* (x is not wrapped) */

static void
draw_units(Map *map, int x, int y)
{
	int xw = wrapx(x), sx, sy, sw, sh, u, s;
	Unit *unit;
	UnitView *uview;
	Rect tmprect;
	extern PicHandle dotdotdotpicture;

	if (map->see_all || units_visible(dside, xw, y)) {
		unit = unit_at(xw, y);
		if (unit != NULL) {
			xform(map, x, y, &sx, &sy);
			if (map->vp->uw <= 16 
			    && g_bigicons() != TRUE) {
				/* 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;
				if (unit->occupant != NULL
				      && sw >= 8
	    			      && occupants_visible(dside, map->vp, unit)) {
					/* Draw a "grouping box", in white, but with no occs drawn. */
					SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
					/* Increase top and right margin by one pixel each. */
					tmprect.top -= 1;
					tmprect.right += 1;
					s = side_number(unit->side);
					if (map->boxmasks) {
						if (map->sidecolors && icon_mask_color[s]) {
							RGBBackColor(&(get_sideColor(s, icon_mask_color[s])));
						} else {
							RGBBackColor(&maskcolor);
						}
						UNWRAP_RECT(map, tmprect, 
							FillRect(&tmprect, QDPat(white)));
						BackColor(whiteColor);
					}
					UNWRAP_RECT(map, tmprect, 
						FrameRect(&tmprect));
	    		}
				UNWRAP_SX(map, sx, 
					draw_unit_image(map->window, sx, sy, sw, sh, unit->type, side_number(unit->side), 
								   map->sidecolors, 0, !completed(unit), map->draw_emblems));

				/* 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));
				}

				/* Indicate if more than one stacked here.  Since this may cover up the unit's
				   picture, adjust it down as far as possible while remaining inside the
				   "cell minus grid" area. */
				if (unit->nexthere != NULL && sw > 8) {
					SetRect(&tmprect, sx + sw/2 - 6, sy + sh - 3,
									  sx + sw/2 + 6, sy + sh + 1);
					/* (should clip to fit in cell) */
					/* (should do a copybits) */
					UNWRAP_RECT(map, tmprect, 
						DrawPicture(dotdotdotpicture, &tmprect));
				}
				if (map->vp->draw_names) {
					UNWRAP_SX(map, sx, 
						draw_unit_name(unit, sx, sy, sw, sh, 
									  map->textmasks, map->optimize_fonts));
				}
			} else {
				for_all_stack(xw, y, unit) {
					if (1 /* doesnt work right? - side_sees_unit(dside, unit) */) {
						m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
						draw_unit_and_occs(map, unit, sx, sy, sw, sh);
					}
				}
			}
		}
	} else {
		uview = unit_view_at(dside, xw, y);
		if (uview != NULL) {
			u = view_type(uview);  s = view_side(uview)->id;
			xform(map, x, y, &sx, &sy);
			sw = map->vp->uw;  sh = map->vp->uh;
			/* Adjust to unit part of cell. */
			sx += (map->vp->hw - sw) / 2;  sy += (map->vp->hh - sh) / 2;
			UNWRAP_SX(map, sx, 
				draw_unit_image(map->window, sx, sy, sw, sh, u, s, 
							   map->sidecolors, 0, 0, map->draw_emblems));
		}
	}
}

/* This function draws units at a normal or high magnification */

static void
draw_unit_and_occs(Map *map, Unit *unit, int sx, int sy, int sw, int sh)
{
	int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
	int emblem = map->draw_emblems;
	Unit *occ;
	Rect tmprect;
	
	/* If an occupant's side is the same as its transport's, then there's
	   really no need to draw its side emblem, since the transport's emblem
	   will also be visible. */
	if (unit->transport && unit->side == unit->transport->side)
	  emblem = FALSE;
	/* Don't draw grouping box and occs with big icons. */
	if (sw > 8 
	    && occupants_visible(dside, map->vp, unit)
	    && g_bigicons() != TRUE) {
		/* Draw a sort of "grouping box", in white. */
		SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
		 /*Make box 1 pixel less tall to avoid leakage of its lower left corner into the grid.
		 The grouping box can also be 1 pixel wider without problems. This makes things
		 look much better since the unit images now fit entirely within the box. */
		tmprect.bottom -= 1;
		tmprect.right += 1;
		/* Set mask color for the grouping box. */
		if (map->boxmasks) {
			if (map->sidecolors && icon_mask_color[s]) {
				RGBBackColor(&(get_sideColor(s, icon_mask_color[s])));
			} else {
				RGBBackColor(&maskcolor);
			}
			UNWRAP_RECT(map, tmprect, 
				FillRect(&tmprect, QDPat(white)));
			BackColor(whiteColor);
		}
		UNWRAP_RECT(map, tmprect, 
			FrameRect(&tmprect));
			
		/* Draw the transport in the UL quarter of the box. */
		m_xform_occupant(map, unit, unit, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
		UNWRAP_SX(map, sx2, 
			draw_unit_image(map->window, sx2, sy2, sw2, sh2, u, s, map->sidecolors, 0, 
												!completed(unit), emblem));

		/* Draw unit size on top of advanced unit. */
		if (u_advanced(unit->type) && map->drawsizes) { 
			UNWRAP_SX(map, sx2, 
				draw_unit_size(unit, map->window, sx2, sy2, sw2, sh2));
		}
		/* Draw the unit name, but not for an occ within a transport. */
		if (map->vp->draw_names 
		      && !unit->transport) {
			UNWRAP_SX(map, sx, 
				draw_unit_name(unit, sx, sy, sw, sh, 
						 	 map->textmasks, map->optimize_fonts));  
		}
		/* Draw all the occupants, in the bottom half of the box. */
		for_all_occupants(unit, occ) {
			m_xform_occupant(map, unit, occ, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
			draw_unit_and_occs(map, occ, sx2, sy2, sw2, sh2);	/* RECURSION! */
		}
	} else { /* Just draw the one unit. */
	    
		/* Tweak sy so that occs are moved inside the smaller grouping box. */
		if (unit->transport)
		    sy -= 2;
		UNWRAP_SX(map, sx, 
			draw_unit_image(map->window, sx, sy, sw, sh, u, s, map->sidecolors, 0, 
											!completed(unit), emblem));

		/* 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, map->vp->uh));
		}
		/* Draw the unit name, but not for an occ within a transport. */
		if (map->vp->draw_names 
		      && !unit->transport) {
			UNWRAP_SX(map, sx, 
				draw_unit_name(unit, sx, sy, sw, sh, 
						 	  map->textmasks, map->optimize_fonts));
		}
	}
}

static void
draw_ai_region(Map *map, int x, int y)
{
	int xw, thid, sx, sy, dir, x1, y1, thid1, bitmask = 0;

	xw = wrapx(x);
	thid = ai_region_at(dside, xw, y);
	/* Decide which edges are borders of the theater. */
	for_all_directions(dir) {
		/* Don't do anything about edge cells. */
		if (interior_point_in_dir(xw, y, dir, &x1, &y1)) {
			thid1 = ai_region_at(dside, wrapx(x1), y1);
			if (thid != thid1) {
				bitmask |= 1 << dir;
			}
		}
	}
	if (bitmask != 0) {
		xform(map, x, y, &sx, &sy);
		if (bitmask != 0) {
		    UNWRAP_SX(map, sx, 
			draw_ai_region_borders(map->window, sx, sy, bitmask, map->vp->power));
		}
	}
}


void draw_region_borders(WindowPtr win, int sx, int sy, int bitmask, int power);

void
draw_region_borders(WindowPtr win, int sx, int sy, int bitmask, int power)
{
	int wid, wid2, dir;
	Rect tmprect;

	if (bwid[power] > 0) {
		wid = 2;
		wid2 = 1;
		PenSize(wid, wid);
		for_all_directions(dir) {
			if (bitmask & (1 << dir)) {
				MoveTo(sx + bsx[power][dir] - wid2 + 1, sy + bsy[power][dir] - wid2 + 1);
				LineTo(sx + bsx[power][dir+1] - wid2 + 1, sy + bsy[power][dir+1] - wid2 + 1);
			}
		}
		PenMode(notPatCopy);
		for_all_directions(dir) {
			if (bitmask & (1 << dir)) {
				MoveTo(sx + bsx[power][dir] - wid2, sy + bsy[power][dir] - wid2);
				LineTo(sx + bsx[power][dir+1] - wid2, sy + bsy[power][dir+1] - wid2);
			}
		}
		PenNormal();
	} else {
		SetRect(&tmprect, sx, sy, sx + hws[power], sy + hhs[power]);
		OffsetRect(&tmprect, 1, 1);
		FillRect(&tmprect, QDPat(black));
		OffsetRect(&tmprect, -1, -1);
		FillRect(&tmprect, QDPat(white));
	}
}

static void
draw_region(Map *map, int x, int y)
{
	int xw, sx, sy, dir, x1, y1, bitmask = 0;
	struct a_region *reg, *reg1;

	xw = wrapx(x);
	reg = aref(area.regions, xw, y);
	/* Decide which edges are borders of the theater. */
	for_all_directions(dir) {
		/* Don't do anything about edge cells. */
		if (interior_point_in_dir(xw, y, dir, &x1, &y1)) {
			reg1 = aref(area.regions, wrapx(x1), y1);
			if (reg != reg1) {
				bitmask |= 1 << dir;
			}
		}
	}
	if (bitmask != 0) {
		xform(map, x, y, &sx, &sy);
		if (bitmask != 0) {
		    UNWRAP_SX(map, sx, 
			draw_region_borders(map->window, sx, sy, bitmask, map->vp->power));
		}
	}
}
