All pastes #36652 Raw Edit

cavehippo

public text v1 · immutable
#36652 ·published 2006-01-13 02:23 UTC
rendered paste body
Index: MapWin.java
===================================================================
RCS file: /cvsroot/jcrossclient/jcrossclient/MapWin.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- MapWin.java	1 Dec 2005 18:55:48 -0000	1.4
+++ MapWin.java	13 Jan 2006 01:56:19 -0000	1.5
@@ -1,784 +1,882 @@
 /*
- * Copyright (c) 1997, Phil Brown, phil@bolthole.com
- * Copyright (c) 2005, Brendan Lally, brenlally@gmail.com
- *
- * This file is part of jcrossclient.
- *
- * Jcrossclient 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 of the License, or (at your option) any later version.
- *
- * Jcrossclient is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * Jcrossclient; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/* Notes about our "Map":
- * We have a multi-level display going here.
- * At the back end, there is a 2d array of (VECTOR of pixmapID),
- *    called Cells[][]
- *  This gets updated when we read in a map string, in mapRead(String)
- * We also keep a "dirty" 2d array, of Cells that have been changed, not drawn.
- * 
- * updateMap() draws cells that have changed, to our fullscreen Image buffer
- * drawMap() draws ALL cells, changed or not, to the buffer
- *
- * Finally, paint() actually draws our buffer to the screen.
- *
- * We only really need to sync on updating "fullmap", I think?!
- * So, anything that draws with fullmapgc
- */
+    Copyright (c) 1997, Phil Brown, phil@bolthole.com
+    Copyright (c) 2005, Brendan Lally, brenlally@gmail.com
+    This file is part of jcrossclient.
+    Jcrossclient 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 of the License, or (at your option) any later version.
+    Jcrossclient is distributed in the hope that it will be useful, but WITHOUT ANY
+    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+    PARTICULAR PURPOSE. See the GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License along with
+    Jcrossclient; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+  */
+/*
+    Notes about our "Map":
+    We have a multi-level display going here.
+    At the back end, there is a 2d array of (Vector of MapSquare),
+    called Cells[][]
+    This gets updated when we read in a map string, in mapRead(String)
+    Each MapSquare also contains a "dirty" flag, indicating if it has been changed but not drawn.
+    updateMap() draws cells that have changed to our fullscreen Image buffer
+    drawMap() draws ALL cells, changed or not, to the buffer
+    Finally, paint() actually draws our buffer to the screen.
+    We only really need to sync on updating "fullmap", I think?!
+    So, anything that draws with fullmapgc
+  */
 
-import java.util.Vector;
-import java.lang.Integer;
+import java.util.LinkedList;
+import java.util.Iterator;
 import java.awt.*;
 import java.awt.event.*;
-import java.awt.AWTEvent;
 import java.io.*;
-import java.awt.image.BufferedImage;
 
-/*Here's the plan:
- Only actually update the map window every X milliseconds.
- But allow outside folks to add to the information.
- Need to have a locking mechanism, I guess.
-*/
+/*
+    Here's the plan:
+    Only actually update the map window every X milliseconds.
+    But allow outside folks to add to the information.
+    Need to have a locking mechanism, I guess.
+  */
 
-
-class MapUpdater extends Thread
-{
+class MapUpdater extends Thread {
 	MapWin mapwin;
 	CFclient toplevel;
 
-	static int sleepms=10;
+	static int sleepms = 10;
 
 	// note: this is in milliseconds
-	public void setSleepDelay(int sleep)
-	{
-		if(sleep<10) sleep=10;
-		if(sleep>1000) sleep=1000;
-		sleepms=sleep;
-	}
-	public void run()
-	{
-		mapwin.assign_cells();
-		while(mapwin.goodToGo() == false)
-		{
-			try
-			{
+
+	public void setSleepDelay(int sleep) {
+		if (sleep < 10) {
+			sleep = 10;
+		}
+		if (sleep > 1000) {
+			sleep = 1000;
+		}
+		sleepms = sleep;
+	}
+
+	public void run() {
+		mapwin.assignCells();
+		while (mapwin.goodToGo() == false) {
+			try {
 				Thread.sleep(100);
-			}catch (Exception err)	{ }
+			} catch (Exception err) {}
 		}
 
 		System.out.println("Map update thread good to go!");
-		while (true)
-		{
-			//System.out.println("m");
-			//mapwin.drawMap();
-
-			if(mapwin.updateMap())
+		while (true) {
+			if (mapwin.updateMap()) {
 				mapwin.repaint();
-
+			}
 			toplevel.drawFloor();
-			try
-			{
+			try {
 				Thread.sleep(sleepms);
 				//Thread.yield();
 
-			}catch (Exception err)
-			{
-				System.out.println("MapUpdater interrupted:"+
-						  err.getMessage());
 			}
-			
+			catch (Exception err) {
+				System.out.println("MapUpdater interrupted:" +
+				                   err.getMessage());
+			}
+
 		}
-		
+
 	}
-	public MapUpdater(MapWin parent, CFclient top)
-	{
-		mapwin=parent;
+
+
+	public MapUpdater(MapWin parent, CFclient top) {
+		mapwin = parent;
 		toplevel = top;
-		
+
 	}
 }
 
-class Map_Square
-{
-	public static final int NUMBER_OF_LAYERS=3;	/* ugly and hardcoded*/
+class MapSquare {
+
+
+	// ugly and hardcoded
+	public final static int NUMBER_OF_LAYERS = 3;
 	private int faces[] = new int[NUMBER_OF_LAYERS];
-	/* these are the number of tiles we are into the image, starting at zero */
-	private int x_size[] = new int[NUMBER_OF_LAYERS];
-	private int y_size[] = new int[NUMBER_OF_LAYERS];
+	private int xSize[] = new int[NUMBER_OF_LAYERS];
+	private int ySize[] = new int[NUMBER_OF_LAYERS];
 	private boolean offmap[] = new boolean[NUMBER_OF_LAYERS];
-	private boolean needs_updating;
 	private int darkness;
-	
-	public Map_Square()
-	{
+
+	public MapSquare() {
 		faces = new int[NUMBER_OF_LAYERS];
-		needs_updating=false;
 		darkness = -1;
 	}
-	
-	/** returns true on success, false on failure */
-	public synchronized void set_face(int face, int layer) {
-		if (layer > NUMBER_OF_LAYERS || layer < 1)
+
+	public synchronized void setFace(int face, int layer) {
+		if (layer > NUMBER_OF_LAYERS || layer < 1) {
+			return;
+		}
+		if (face == -1) {
 			return;
-		if (face == -1) return;
-		faces[layer-1] = face;
-		needs_updating = true;
-		x_size[layer-1] = 1;
-		y_size[layer-1] = 1;
+		}
+		faces[layer - 1] = face;
+		xSize[layer - 1] = 1;
+		ySize[layer - 1] = 1;
 	}
-	public synchronized void set_face(int face, int layer, int x, int y, boolean off_map) {
-		if (layer > NUMBER_OF_LAYERS || layer < 1)
+
+	public synchronized void setFace(int face, int layer,
+	                                 int x, int y, boolean off_map) {
+		if (layer > NUMBER_OF_LAYERS || layer < 1) {
+			return;
+		}
+		if (face == -1) {
 			return;
-		if (face == -1) return;
-		faces[layer-1] = face;
-		needs_updating = true;
-		x_size[layer-1] = x;
-		y_size[layer-1] = y;
-		offmap[layer-1] = off_map;
+		}
+		faces[layer - 1] = face;
+		xSize[layer - 1] = x;
+		ySize[layer - 1] = y;
+		offmap[layer - 1] = off_map;
 	}
 
-	public synchronized int get_face(int layer) {
-		if (layer > NUMBER_OF_LAYERS || layer < 1)
+	public synchronized int getFace(int layer) {
+		if (layer > NUMBER_OF_LAYERS || layer < 1) {
 			return 0;
-		return this.faces[layer-1];
+		}
+		return this.faces[layer - 1];
 	}
 
-	public synchronized int get_x_size(int layer) {
-		if (layer > NUMBER_OF_LAYERS || layer < 1)
+	public synchronized int getXSize(int layer) {
+		if (layer > NUMBER_OF_LAYERS || layer < 1) {
 			return 1;
-		return x_size[layer-1];
+		}
+		return xSize[layer - 1];
 	}
 
-	public synchronized int get_y_size(int layer) {
-		if (layer > NUMBER_OF_LAYERS || layer < 1)
+	public synchronized int getYSize(int layer) {
+		if (layer > NUMBER_OF_LAYERS || layer < 1) {
 			return 1;
-		return y_size[layer-1];
+		}
+		return ySize[layer - 1];
 	}
 
-	/* clones the square from this to target, sets it to need updating */
-	public synchronized void copy(Map_Square target) {
-		for (int i=0;i<NUMBER_OF_LAYERS;i++) {
-			if (!offmap[i])
-			  target.set_face(this.faces[i], i+1, x_size[i], y_size[i], false);
-		}
-		target.set_darkness(this.darkness); /*FIXME*/
-		target.is_dirty();
-		this.needs_updating = true;
-	}	
-
-	/** if this square contains this face, we need to redraw it */
-	public synchronized void face_is_dirty(int dirty_face) {
-		int i;
-		for (i=0;i<NUMBER_OF_LAYERS;i++)
-			if (faces[i] == dirty_face) 
-				needs_updating=true;
+	// return true iff this square contains any layer bigger than 1x1
+
+	public synchronized boolean isBig() {
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			if ((xSize[i] > 1) || (ySize[i] > 1)) {
+				return true;
+			}
+		}
+		return false;
 	}
 
-	public synchronized void clear() {
-		int i;
-		for (i=0;i<NUMBER_OF_LAYERS;i++) {
-			x_size[i] = 1;
-			y_size[i] = 1;
-			faces[i]=0;
+
+	// return the largest x dimension of all the layers in the square
+
+	public synchronized int biggestX() {
+		int maxX = 0;
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			if (xSize[i] > maxX) {
+				maxX = xSize[i];
+			}
+		}
+		return maxX;
+	}
+
+
+	// return the largest Y dimension of all the layers in the square
+
+	public synchronized int biggestY() {
+		int maxY = 0;
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			if (ySize[i] > maxY) {
+				maxY = ySize[i];
+			}
+		}
+		return maxY;
+	}
+
+
+	/*
+	    clones the square from this to target
+	  */
+
+	public synchronized void copyTo(MapSquare target) {
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			target.setFace(this.faces[i], i + 1,
+			               xSize[i], ySize[i], false);
 		}
-		this.darkness =-1;
-		this.needs_updating=true;
+		target.setDarkness(this.darkness);
+		/*
+		    FIXME
+		  */
 	}
 
-	public synchronized void is_dirty() {
-		this.needs_updating=true;
+
+	/**
+	 * if this square contains this face, we need to redraw it
+	 *
+	 * @param  dirty_face  Description of the Parameter
+	 */
+	public synchronized boolean containsFace(int face) {
+
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			if (faces[i] == face) {
+				return true;
+			}
+		}
+		return false;
 	}
-	public synchronized boolean should_update() {
-		return this.needs_updating;
+
+
+	public synchronized void clear() {
+		for (int i = 0; i < NUMBER_OF_LAYERS; i++) {
+			xSize[i] = 1;
+			ySize[i] = 1;
+			faces[i] = 0;
+		}
+		this.darkness = -1;
 	}
-	public static int get_layer_count() {
+
+
+	public static int getLayerCount() {
 		return NUMBER_OF_LAYERS;
 	}
-	public synchronized void was_updated() {
-		this.needs_updating = false;
-	}
-	public synchronized int get_darkness() {
+
+
+	public synchronized int getDarkness() {
 		return this.darkness;
 	}
-	public synchronized boolean set_darkness(int newvalue) {
-		if (newvalue >=0 && newvalue < 256) {
-			this.darkness=newvalue;
+
+
+	public synchronized boolean setDarkness(int newvalue) {
+		if (newvalue >= 0 && newvalue < 256) {
+			this.darkness = newvalue;
 			return true;
 		}
 		return false;
 	}
 }
 
+/*
+    A class representing rectangles of MapSquares. Used in MapWin to keep track
+    of regions that need to be redrawn.
+  */
+class Rectangle {
+
+	public int x1;
+	public int x2;
+	public int y1;
+	public int y2;
+
+	public Rectangle(int newX1, int newY1,
+	                 int newX2, int newY2) {
+		x1 = newX1;
+		x2 = newX2;
+		y1 = newY1;
+		y2 = newY2;
+	}
+
+	// return whether this rectangle intersects
+	// another given rectangle
+
+	public boolean intersects(Rectangle other) {
+		return !((x1 > other.x2) || (y1 > other.y2)
+		         || (x2 < other.x1) || (y2 < other.y1));
+	}
+}
+
+public class MapWin extends Canvas implements MouseListener, ActionListener {
 
-public class MapWin extends Canvas implements MouseListener,ActionListener
-{
-	public final int MAX_FACE_SIZE=8; 	/* the biggest height or width any face can have */
-	int stupidlock;
+	/*
+	    the biggest height or width any face can have
+	  */
+
+	public static final int MAX_FACE_SIZE = 8;
 	MapUpdater mapthread;
-	public static int pixwidth=32, pixheight=32;
+	public static final int pixwidth = 32, pixheight = 32;
 	// okay, ugly...
 	CFclient Parent;
 
-	int PlayerImageNum=-1;
+	int PlayerImageNum = -1;
 	Image PlayerImage;
 
-	final int XMAX=15, YMAX=15;
-	final int BUFFERX = XMAX + MAX_FACE_SIZE;
-	final int BUFFERY = YMAX + MAX_FACE_SIZE;
-
-	Map_Square Cells[][];
-	Image fullmap=null;
-	Graphics fullmapgc=null; // this gc exists solely to paint to fullmap
+	public static final int XMAX = 15, YMAX = 15;
+	public static final int BUFFERX = XMAX + MAX_FACE_SIZE;
+	public static final int BUFFERY = YMAX + MAX_FACE_SIZE;
 
-	boolean debugflag=false;
-	
-	public void assign_cells(){
-		/* java is a little speshul here, and won't size this correctly until we enter a method....*/
-		Cells = new Map_Square[BUFFERX][BUFFERY];
-		for (int i=0;i<BUFFERX;i++) {
-			for (int j=0;j<BUFFERY;j++) {
-				Cells[i][j]=new Map_Square();
+	MapSquare Cells[][];
+	LinkedList dirtyRectangles = new LinkedList();
+	Image fullmap = null;
+	Graphics fullmapgc = null;
+	// this gc exists solely to paint to fullmap
+
+	boolean debugflag = false;
+
+
+	public void assignCells() {
+		/*
+		    java is a little speshul here, and won't size this correctly
+		    until we enter a method....
+		  */
+		Cells = new MapSquare[BUFFERX][BUFFERY];
+		for (int i = 0; i < BUFFERX; i++) {
+			for (int j = 0; j < BUFFERY; j++) {
+				Cells[i][j] = new MapSquare();
 			}
 		}
 	}
-	void debug(String str)
-	{
-		if(debugflag)
+
+
+	void debug(String str) {
+		if (debugflag) {
 			System.out.println(str);
+		}
 	}
-	void print(String str)
-	{
-		System.out.println(str);
-	}
 
-	public void keyPressed(KeyEvent evt){ }
+	public void keyPressed(KeyEvent evt) { }
+
+
+	public void keyReleased(KeyEvent evt) { }
 
-	public void keyReleased(KeyEvent evt){ }
-	public void keyTyped(KeyEvent evt) {  }
 
+	public void keyTyped(KeyEvent evt) { }
 
-	public void actionPerformed(ActionEvent evt)
-	{
-		String time=evt.getActionCommand();
-		int ms=Integer.parseInt(time);
-		System.out.println("Set map delay to "+ms+" ms.");
+
+	public void actionPerformed(ActionEvent evt) {
+		String time = evt.getActionCommand();
+		int ms = Integer.parseInt(time);
+		System.out.println("Set map delay to " + ms + " ms.");
 		mapthread.setSleepDelay(ms);
-		
+
 	}
 
-	public void changeUpdateDelay(int ms)
-	{
-		System.out.println("Setting map delay to "+ms+" ms.");
+
+	public void changeUpdateDelay(int ms) {
+		System.out.println("Setting map delay to " + ms + " ms.");
 		mapthread.setSleepDelay(ms);
 	}
 
+
 	// not used right now, but could be in future.
-	public void wakeupMapUpdater()
-	{
+	public void wakeupMapUpdater() {
 		mapthread.interrupt();
 	}
 
-	/************************************************************/
+	// this exists solely for the MapUpdater to spin-lock on
 
-	/*public int TwoByteConv(String str, int start)
-	{
-		int val;
-		val=(int)str.charAt(start) <<8;
-		val+=(int)str.charAt(start+1);
-
-		return val;
-	}*/
-
-	// Quick and easy way to say "next redraw, draw ALL CELLS again"
-	public void markAllUpdate()
-	{
-		for(int y=0; y<BUFFERY; y++)
-		{
-		    for(int x=0; x<BUFFERX; x++)
-		    {
-				Cells[x][y].is_dirty();
-		    }
-		}
-	}
 
-	public void DebugMode()
-	{
-		System.out.println("TESTING: ignoring MapWin.DebugMode");
-		debugflag=false; return;
-		
-		//debugflag=!debugflag;
+	public boolean goodToGo() {
+		return (fullmapgc != null);
 	}
 
-	// this exists soley for the MapUpdater to spin-lock on
-	public boolean goodToGo()
-	{
-		if(fullmapgc==null)
-			return false;
-		return true;
-	}
+
 	// I'd love to call this in the constructor, but
+
 	// the createImage() thing only seems to work after
-	// we are truely visible. sigh.
+	// we are truly visible. sigh.
 	// May as well wait until here to start up map update thread,
 	// since it depends on this working, anyways..
-	void initBackBuffer()
-	{
+
+	void initBackBuffer() {
 		debug("initializing map backbuffer");
-		fullmap=createImage(XMAX*pixwidth,YMAX*pixheight);
-		fullmapgc=fullmap.getGraphics();
-		if(fullmap!=null)
+		fullmap = createImage(XMAX * pixwidth, YMAX * pixheight);
+		fullmapgc = fullmap.getGraphics();
+		if (fullmap != null) {
 			debug("got fullmap");
-		else
+		} else {
 			debug("FAILED to init fullmap image");
-		if(fullmapgc != null) {
+		}
+		if (fullmapgc != null) {
 			debug("got fullmapgc");
-			fullmapgc.clearRect(0,0,
-					    XMAX*pixwidth,
-					    YMAX*pixheight);
+			fullmapgc.clearRect(0, 0,
+			                    XMAX * pixwidth,
+			                    YMAX * pixheight);
 			fullmapgc.drawString("Waiting for images...",
-				      5+MAX_FACE_SIZE*pixwidth, YMAX*pixheight/2);
-		}
-		else {
+			                     5 + MAX_FACE_SIZE * pixwidth,
+			                     YMAX * pixheight / 2);
+		} else {
 			debug("FAILED to init fullmapgc");
 		}
 	}
-	
-	/* This routine renders ONLY map squares that have changed, to
-	 * our back buffer. It also updates the need_redraw[][] at the
-	 * same time, by way of calling paintCell()
-	 *
-	 * It does NOT DRAW TO THE USER'S SCREEN
-	 *
-	 * It returns true if we actually changed anything,
-	 *  false if nothing changed
-	 * This has been hacked to call draw map instead, this is slow, 
-	 * inefficient and above all, easy.
-	 */
-	synchronized boolean updateMap() {
-		drawMap();
-		return true;/*
-		//debug("updateMap() started");
-		if(fullmap == null) {
-			debug("Calling initBackBuffer in updateMap()");
-			initBackBuffer();
-		}
 
-		// The problem isn't actually here, as shown by dumpdirty.
-		// the problem is that something, somewhere, is somehow
-		// writing to the SCREEN, when it has no business
-		// doing so. and making black rectangles.
-		// But it somehow seems related to line-of-sight issues.
-		// very confusing!!
+	/* return a rectangle clipped  to the viewport
+	*/
 
-		/*
-		if(debugflag)
-		{
-			debug("in updateMap(), before actual redraw, squares needing redraw are:");
-			dumpdirty();
-		}
-		*/
-		/*
-		boolean changed=false;
-		for(int y = 0; y < YMAX; y++) {
-		    for(int x = 0; x < XMAX; x++) {
-				if(Cells[x][y].should_update()) {
-					changed=true;
-					paintCell(fullmapgc, x, y);
-				}
-		    }
-		}
-
-		
-		return changed;*/
-	}
-
-	// mark all cells with this face as "dirty", so on the
-	// next updateMap(), they get redrawn
-	void dirtyFace(int face)
-	{	
-		for(int y=0; y<BUFFERY; y++)
-		{
-		    for(int x=0; x<BUFFERX; x++)
-		    {
-		       Cells[x][y].face_is_dirty(face);
-		    }
-		}
+	public static Rectangle clippedRectangle(int x1, int y1, int x2, int y2){
+		return new Rectangle(Math.max(0, x1), Math.max(0, y1),
+				Math.min(BUFFERX - 1, x2), Math.min(BUFFERY - 1, y2) );
 	}
-
-	/* called by MapScrollCmd, in CFclient
-	 * scroll the map!
-	 * if x_change >0, means move pixmaps this way <<==
-	 *  etc, etc
-	 *
-	 * What we will do is copy the facenumbers across, from one cell to the
-	 * one that it should be, then we will clear the cell, and mark both dirty
-	 */
-	public synchronized void mapScroll(int x_change, int y_change)
-	{
-		int i,j;
-		/* this isn't a particularly nice way to do this, but it should
-		 * be quite fast. */
-		if (x_change >= 0) {
-			if (y_change >= 0) {
-				for (i=x_change;i<BUFFERX;i++) {
-					for (j=y_change;j<BUFFERY;j++) {
-						Cells[i][j].copy(Cells[i-x_change][j-y_change]);
-						Cells[i][j].clear();
-					}
-				}
-			}
-			else {
-				for (i=x_change;i<BUFFERX;i++) {
-					for (j=BUFFERY+y_change-1;j>=0;j--) {
-						Cells[i][j].copy(Cells[i-x_change][j-y_change]);
-						Cells[i][j].clear();
-					}
-				}
+		
+	/* mark a region of MapSquares as needing to be redrawn */
+	
+	public void addDirtyRectangle(Rectangle r) {
+		Iterator iter = dirtyRectangles.listIterator();
+		boolean intersected = false;
+		while (iter.hasNext() && !intersected) {
+			Rectangle nextRect = (Rectangle) iter.next();
+			if (r.intersects(nextRect)) {
+				iter.remove();
+				addCombined(r, nextRect);
+				intersected = true;
 			}
 		}
-		else {
-			if (y_change >= 0) {
-				for (i=BUFFERX+x_change-1;i>=0;i--) {
-					for (j=y_change;j<BUFFERY;j++) {
-						Cells[i][j].copy(Cells[i-x_change][j-y_change]);
-						Cells[i][j].clear();
-					}
-				}
-			}
-			else {
-				for (i=BUFFERX+x_change-1;i>=0;i--) {
-					for (j=BUFFERY+y_change-1;j>=0;j--) {
-						Cells[i][j].copy(Cells[i-x_change][j-y_change]);
-						Cells[i][j].clear();
-					}
-				}
-			}
+		if (!intersected) {
+			dirtyRectangles.add(r);
 		}
-		/* having gone through all of that, lets make it completely pointless,
-		 * by doing the stupidest, most inefficiant thing we could possibly do here */
-		updateMap();
 	}
 
-	// run through and see if there are any 'dirty' map squares
-	// that need to be written out to back buffer
-	boolean needUpdate() {
-		for (int y=0; y<BUFFERY; y++) {
-			for (int x=0; x<BUFFERX; x++) {
-				if(Cells[x][y].should_update()==true)
-					return true;
-			}
-		}
-		return false;
-	}
-	// quickly say if each cell supposedly needs a refresh or not
-	void dumpdirty()
-	{
-		for(int y=0; y<BUFFERY; y++)
-		{
-			StringBuffer line = new StringBuffer();
-			for(int x=0; x<BUFFERX; x++)
-			{
-				if(Cells[x][y].should_update()==true)
-				{
-					line.append("X ");
-				} else {
-					line.append("0 ");
-				}
-			}
-			System.out.println(line);
-		}
-	}
-		
-	/* called by "Map1aCmd" in CFclient
-	 * This processes a map update from the server
-	 * This function is a mutation of the original mapRead and the map code 
-	 * from the mainstream cf client, and hacked to vaguely compile. Consider 
-	 * it a study in how to write C in an inferior language, look at the C
-	 * client's common/commands.c for what this function wants to look like.
-	 */
-	public synchronized void map1aRead(DataInputStream mapdata) throws IOException {
-		//System.out.println("map1aRead now called");
-		int 	maplen = mapdata.available();
-		boolean	bottom = true;
-		int	mask;
-		int 	x, y, layer, darkness;
-		int	faces[] = new int[3];
-		int 	pos = 0;
-		int	x_size;
-		int	y_size;
-		Image	next_face;
-		int	NUM_LAYERS=2; /* this should be defined somewhere nicer */
-		//System.out.println("maplen is " + maplen);
-		while ( pos < maplen ) {
-		//	System.out.println("reading a new tile");
-			bottom = true;
-			//place = str.charAt(parse++);
-			mask = (int) mapdata.readUnsignedShort(); 
-			pos+=2;
-		//	System.out.println("mask is " + mask);
-	
-			x = (mask >> 10) & 0x3f;;
-			y = (mask >> 4) & 0x3f;;
-		//	System.out.println("tile is at " + x + ":" + y);
-
-			if ((mask & 0xf) == 0) {
-				//Cells[x][y].clear(); 
-				/* by commenting this out, we get something close to fog of war support */
-				continue;	/* if empty mask, none of the checks will be true. */
-			}
 
-			/* If this space was previously stored for fog of war, need to clear
-			* it now.  Needs to be done before anything else happens that we
-			* might want to store in it.
-			*/
-		
-			if ((mask & 0x8) != 0 ) { /* darkness bit */
-				darkness = mapdata.readUnsignedByte(); 
-				pos+=1;
-		//		System.out.println("darkness value is set, it is " + darkness);
-			}
-			else darkness = -1;
-		
-			/* Reduce redundant by putting the get image
-			* and flags in a common block.  The layers
-			* are the inverse of the bit order unfortunately.
-			*/
-			for (layer=NUM_LAYERS; layer>=0; layer--) {
-				if ((mask & (1 << layer)) != 0) {
-					faces[NUM_LAYERS-layer] = mapdata.readUnsignedShort();
-					pos+=2;
-				}
-				else {
-					faces[NUM_LAYERS-layer] = -1;
-				}
-			}
-			/* if you value your sanity, just give up on trying to follow this */
-			for (layer=0; layer<=NUM_LAYERS; layer++) {
-				if (faces[layer] < 0) continue;
-				next_face=Parent.getPixmap(faces[layer]);
-				/* had we an image < 32 pixels, the int division would give zero, but we will 
-				 * use 1 square at least.*/
-				x_size=Math.max(1, next_face.getWidth(null)/pixwidth);
-				y_size=Math.max(1, next_face.getHeight(null)/pixheight);
-				if (x_size > 1 || y_size > 1) {
-					boolean is_off_map =false;
-					if (x > XMAX || y > YMAX) is_off_map = true;
-					/* the faces are sent to their bottom right corner, but we store them
-					 * in the top left corner, so we need to figure out what the new cell is */
-					int x_coordinate = (x+1)-x_size;
-					int y_coordinate = (y+1)-y_size;
-					
-					Cells[x_coordinate+MAX_FACE_SIZE][y_coordinate+MAX_FACE_SIZE].set_face(
-					  faces[layer], layer+1, x_size, y_size, is_off_map);
-				}
-				else if (x<XMAX && y< YMAX)
-					Cells[x+MAX_FACE_SIZE][y+MAX_FACE_SIZE].set_face(faces[layer], layer+1);
-			}
-		}
+	/*
+	    given two intersecting rectangles, add a big rectangle that 
+	    covers both
+	    */
+
+	public void addCombined(Rectangle r1, Rectangle r2) {
+
+		addDirtyRectangle(new Rectangle(Math.min(r1.x1, r2.x1), Math.min(r1.y1, r2.y1),
+		                                       Math.max(r1.x2, r2.x2), Math.max(r1.y2, r2.y2) ) );
 	}
 
-	/*
-	 * It sucks that we have to register for ALL THESE
-	 * just to listen for mouse clicks. Sigh.
-	 */
-	public void mouseClicked(MouseEvent evt) {
-		int dx = (evt.getX()/pixwidth) - XMAX/2;
-		int dy = (evt.getY()/pixheight) - YMAX/2;
-		int buttonmask=evt.getModifiers();
-		
-		if ((buttonmask & InputEvent.BUTTON3_MASK)>0) {
-			
-		} else if ((buttonmask & InputEvent.BUTTON1_MASK)>0) {
-			Parent.lookat(dx, dy);
-		}
-	}
-	public void mouseEntered(MouseEvent evt) {
-	}
-	public void mouseExited(MouseEvent evt) {
-	}
-	public void mousePressed(MouseEvent evt) {
-	}
-	public void mouseReleased(MouseEvent evt) {
-	}
-
-	// Given a gc, and the CELL (x,y) co-ords, draw
-	// all objects in a given cell, or clear that window space if
-	// cell is empty
-	// Also, set flag that cell has been drawn
-	// this is currently broken, I'll fix it later, maybe.
-	/*public void paintCell(Graphics gc, int cellx, int celly)
-	{
-		Map_Square cell =  Cells[cellx][celly];
-		cell.was_updated();
-		gc.clearRect(cellx*pixwidth, celly*pixheight, pixwidth, pixheight);
-		// draw all pixmaps that overlay each other,
-		// at a particular Cell
-		int count=0;
-		int imgindex=-1;
-		Image img;
-		for(count=1; count<=cell.get_layer_count(); count++)
-		{
-			imgindex = cell.get_face(count);
-			if (imgindex <=0) continue;
-			img = Parent.getPixmap(imgindex);
-			if(img==null)
-			{
-				debug("We need face "+imgindex);
-				// get "?" image
-				img=Parent.getPixmap(0);
-				// put placeholder, so we dont request it
-				// again, then request from server.
-				Parent.addPixmap(imgindex,img);
-				Parent.askForFace(imgindex);
-			}
-			gc.drawImage(img, cellx*pixwidth, celly*pixheight,
-				     this);
-		}
-	}*/
-	
-	public void paint_cell_layer(Graphics gc, int cellx, int celly, int layer)
-	{
-		Map_Square cell =  Cells[cellx][celly];
-		int imgindex=-1;
-		Image img;
-		imgindex = cell.get_face(layer);
-		if (imgindex <=0) return;
-		img = Parent.getPixmap(imgindex);
-		if(img==null) {
-			debug("We need face "+imgindex);
-			// get "?" image
-			img=Parent.getPixmap(0);
-			// put placeholder, so we dont request it
-			// again, then request from server.
-			Parent.addPixmap(imgindex,img);
-			Parent.askForFace(imgindex);
-		}
-		gc.drawImage(img, (cellx-MAX_FACE_SIZE)*pixwidth, (celly-MAX_FACE_SIZE)*pixheight, this);
-	}
-
-	/* This is the routine that can do a FULL refresh of
-	 * our map
-	 * It draws every single square we know about, to the back buffer.
-	 * On the other hand, if you want to just update squares
-	 * that have changed, use updateMap()
-	 * the regions that need updating
-	 */
-	public void drawMap() {
-		debug("drawMap() called");
-			
-		if(fullmap==null) {
-			debug("calling initBackBuffer in drawMap()");
-			initBackBuffer();
-		}
 
-		int x,y;
-		for (int i = 1; i<=Map_Square.get_layer_count(); i++) {	
-			for(y=0; y<BUFFERY; y++) {
-				for(x=0; x<BUFFERX; x++) {
+	/*
+	    write the collection of cells described by the given rectangle
+	    to the buffer
+	  */
+	public void drawRectangle(Rectangle r) {
+		for (int i = 1; i <= MapSquare.getLayerCount(); i++) {
+			for (int y = r.y1; y <= r.y2; y++) {
+				for (int x = r.x1; x <= r.x2; x++) {
 					if (i == 1) {
-					  fullmapgc.clearRect(x*pixwidth, y*pixheight, pixwidth, pixheight);
-					  Cells[x][y].was_updated();
+						clearCell(x, y);
 					}
-					paint_cell_layer(fullmapgc, x, y, i);
+					paintCellLayer(x, y, i);
 				}
 			}
 		}
 	}
 
-	/* used when we get a newmap command */
-	public void clearmap() {
-		for(int y=0; y<BUFFERY; y++) {
-			for(int x=0; x<BUFFERX; x++) {
-				Cells[x][y].clear();
-			}
-		}
-	}
-
-	// we should really make a nice routine that subclasses
-	// this and only draws what "needs" to be updated.
-	// Then again.. sigh...
-	// We never actually call this, due to some odd locking issues
-	// about awt I don't understand. But interestingly, it does
-	// get called through update(), which gets called by the
-	// AWT-default repaint() routine
-	//
-	// We do all game-driven updates "directly".
-	//   [see updateMap() or drawMap()]
-	public void paint(Graphics gc)
-	{
-		//debug("MapWin.paint(gc) called");
-
-		if(fullmap==null)
-		{
-			debug("oops.. fullmap null for paint(), trying init");
-			initBackBuffer();
-			if(fullmap==null)
-			{
-				debug("oops.. fullmap STILL null?");
-				return;
-			}
-			
-		}
-		
-		gc.drawImage(fullmap, 0,0, this);
-		if(PlayerImage==null) return;
-	}
 
-	/*  This sets the image for the player. We will automatically
-	 *  draw this image at the center of the screen
-	 */
+	/*
+	    This is the routine that can do a FULL refresh of
+	    our map
+	    It draws every single square we know about to the back buffer.
+	    On the other hand, if you want to just update squares
+	    that have changed, use updateMap()
+	  */
+	public void drawMap() {
+		debug("drawMap() called");
 
-	public void setPlayerImage(int facenum){
-		PlayerImage=Parent.getPixmap(facenum);
-		PlayerImageNum=facenum;
-		debug(" MapWin set PlayerImageNum to "+facenum);
-		if(PlayerImage==null){
-			debug("(But image is null right now)");
-		} else {
-			debug(" (Player image NOT null)");
-			Cells[MAX_FACE_SIZE + XMAX/2][MAX_FACE_SIZE + YMAX/2].is_dirty();
+		if (fullmap == null) {
+			debug("calling initBackBuffer in drawMap()");
+			initBackBuffer();
 		}
-	}
-
-	// Since paint(gc) draws Every Cell, there is no need for
-	// the default update() behaviour of clearing the window first
-	// interestingly enough, note that repaint() calls this routine
-	public void update(Graphics gc)
-	{
-		paint(gc);
-	}
-
-	public MapWin(CFclient parent)
-	{
-		Cells = new Map_Square[XMAX][YMAX];
 
-		Parent=parent;
-		setSize(pixwidth*XMAX, pixheight*YMAX);
-		setBackground(Color.black);
-		setForeground(Color.white);
-
-		// These should really be on parent.
-		// but again, lets do this for debug now
-
-		addKeyListener(Parent);
-		addFocusListener(Parent);
-
-		addMouseListener(this);
-
-		mapthread = new MapUpdater(this, parent);
-		mapthread.start();
+		drawRectangle(new Rectangle(0, 0, BUFFERX - 1, BUFFERY - 1) );
+		dirtyRectangles.clear();
 	}
 
+	              /* Check for cells containing layers bigger than 1x1.  For each such cell
+	              Check if it overlaps any current dirty rectangles. If it does, add its 
+	              extent to the dirty rectangles list.
+	               */
+		       private synchronized void checkBigCells() {
+			       for (int y = 0; y < BUFFERY; y++) {
+				       for (int x = 0; x < BUFFERX; x++) {
+					       if (Cells[x][y].isBig()) {
+						       Iterator ri = dirtyRectangles.listIterator();
+						       boolean intersected = false;
+						       while(ri.hasNext() && (!intersected) ) {
+							       Rectangle currentDirty = (Rectangle) ri.next();
+							       Rectangle current = clippedRectangle(x, y, Cells[x][y].biggestX() + x - 1,
+									       Cells[x][y].biggestY() + y - 1);
+							       if (current.intersects(currentDirty) ) {
+								       addDirtyRectangle(current);
+								       intersected = true;
+							       }
+						       }
+					       }
+				       }
+			       }
+		       }
+
+	              /*
+	                  This routine renders ONLY map squares that have changed, to
+	                  our back buffer. It also clears the dirty rectangles list when it is
+			  finished. It returns true if we actually painted anything,
+	                  false if nothing changed
+	                */
+	              synchronized boolean updateMap() {
+		              if (fullmap == null) {
+			              debug("Calling initBackBuffer in updateMap()");
+			              initBackBuffer();
+		              }
+
+		            boolean changed = false;
+		              checkBigCells();
+		              Iterator di = dirtyRectangles.listIterator();
+		              while (di.hasNext()) {
+				      changed = true;
+			              drawRectangle((Rectangle) di.next());
+		              }
+		              dirtyRectangles.clear();
+		              return changed;
+	              }
+
+
+	              // mark all cells with this face as "dirty", so on the
+	              // next updateMap(), they get redrawn
+
+
+	              /*
+	                  called by MapScrollCmd, in CFclient
+	                  scroll the map!
+	                  if x_change >0, means move pixmaps this way <<==
+	                  etc, etc
+	                  What we will do is copy the facenumbers across, from one cell to the
+	                  one that it should be, then we will clear the cell, and mark both dirty
+	                */
+
+	              public synchronized void mapScroll(int x_change, int y_change) {
+		              int i;
+		              int j;
+		              /*
+		                  this isn't a particularly nice way to do this, but it should
+		                  be quite fast.
+		                */
+		              if (x_change >= 0) {
+			              if (y_change >= 0) {
+				              for (i = x_change; i < BUFFERX; i++) {
+					              for (j = y_change; j < BUFFERY; j++) {
+						              Cells[i][j].copyTo(Cells[i - x_change][j - y_change]);
+						              Cells[i][j].clear();
+					              }
+				              }
+			              } else {
+				              for (i = x_change; i < BUFFERX; i++) {
+					              for (j = BUFFERY + y_change - 1; j >= 0; j--) {
+						              Cells[i][j].copyTo(Cells[i - x_change][j - y_change]);
+						              Cells[i][j].clear();
+					              }
+				              }
+			              }
+		              } else {
+			              if (y_change >= 0) {
+				              for (i = BUFFERX + x_change - 1; i >= 0; i--) {
+					              for (j = y_change; j < BUFFERY; j++) {
+						              Cells[i][j].copyTo(Cells[i - x_change][j - y_change]);
+						              Cells[i][j].clear();
+					              }
+				              }
+			              } else {
+				              for (i = BUFFERX + x_change - 1; i >= 0; i--) {
+					              for (j = BUFFERY + y_change - 1; j >= 0; j--) {
+						              Cells[i][j].copyTo(Cells[i - x_change][j - y_change]);
+						              Cells[i][j].clear();
+					              }
+				              }
+			              }
+		              }
+		              addDirtyRectangle( new Rectangle(0, 0, BUFFERX - 1, BUFFERY - 1) );
+	              }
+
+
+	              /*
+	                  called by "Map1aCmd" in CFclient
+	                  This processes a map update from the server
+	                  This function is a mutation of the original mapRead and the map code
+	                  from the mainstream cf client, and hacked to vaguely compile. Consider
+	                  it a study in how to write C in an inferior language, look at the C
+	                  client's common/commands.c for what this function wants to look like.
+	                */
+	              public synchronized void map1aRead(DataInputStream mapdata)
+	              throws IOException {
+		              final int DARKNESS_MASK = 0x8;
+		              final int COORD_MASK = 0x3f;
+		              int maplen = mapdata.available();
+		              int mask;
+		              int x;
+		              int y;
+		              int layer;
+		              int faces[] = new int[MapSquare.NUMBER_OF_LAYERS];
+		              int pos = 0;
+		              int x_size;
+		              int y_size;
+		              Image next_face;
+
+		              // Layers are 0-indexed here and 1-indexed in MapSquare. Fix this?
+
+		              final int NUM_LAYERS = MapSquare.NUMBER_OF_LAYERS - 1;
+		              boolean bottom = false;
+			      int darkness;
+			      while (pos < maplen) {
+			              bottom = true;
+			              mask = (int) mapdata.readUnsignedShort();
+			              pos += 2;
+
+			              x = (mask >> 10) & COORD_MASK;
+			              y = (mask >> 4) & COORD_MASK;
+
+			              if ((mask & 0xf)== 0) {
+				              /*
+				                  by commenting this next line out, we get something
+				                  close to fog of war support
+				                */
+				              //	Cells[x][y].clear();
+				              continue;
+				              /*
+				                  if empty mask, none of the checks will be true.
+				                */
+			              }
+
+			              if ((mask & DARKNESS_MASK) != 0) {
+				              /*
+				                  darkness bit
+				                */
+				              darkness = mapdata.readUnsignedByte();
+				              pos += 1;
+				              //		System.out.println("darkness value is set, it is " + darkness);
+			              }
+			              else {
+				              darkness = -1;
+			              }
+			              /* correct? maybe we ought to leave
+			                  darkness alone 
+			               */
+
+			              /* Reduce redundancy by putting the get image
+			                  and flags in a common block.  The layers
+			                  are the inverse of the bit order unfortunately.
+			                */
+			              for (layer = NUM_LAYERS; layer >= 0; layer--) {
+				              if ((mask & (1 << layer)) != 0) {
+					              faces[NUM_LAYERS - layer]
+					              = mapdata.readUnsignedShort();
+					              pos += 2;
+				              } else {
+					              // set this face < 0, so it will be ignored
+					              // in the for loop below
+					              faces[NUM_LAYERS - layer] = -1;
+				              }
+			              }
+			              /* if you value your sanity, just give up on trying
+			                  to follow this
+			                */
+			              for (layer = 0; layer <= NUM_LAYERS; layer++) {
+				              if (faces[layer] < 0) {
+					              continue;
+				              }
+				              next_face = Parent.getPixmap(faces[layer]);
+				              /*
+				                  had we an image < 32 pixels, the int
+				                  division would give zero, but we will use
+				                  1 square at least.
+				                */
+				              int width = next_face.getWidth(null);
+				              int height = next_face.getHeight(null);
+				              boolean is_off_map = (x > XMAX + MAX_FACE_SIZE
+					      		|| y > YMAX + MAX_FACE_SIZE);
+					      x_size = (width % pixwidth == 0 ? 0 : 1)
+				                       + width / pixwidth;
+				              y_size = (height % pixheight == 0 ? 0 : 1)
+				                       + height / pixheight;
+				              if (x_size > 1 || y_size > 1) {
+					              
+					              /*
+					                  the faces are sent to their bottom
+					                  right corner, but we store them in
+					                  the top left corner, so we need to
+					                  figure out what the new cell is
+					                */
+					              int left = x + 1 - x_size + MAX_FACE_SIZE;
+					              int top = y + 1 - y_size + MAX_FACE_SIZE;
+
+					           addDirtyRectangle(clippedRectangle(left, top, 
+						      		Math.max(x, left + Cells[left][top].biggestX() - 1),
+								Math.max(y, top + Cells[left][top].biggestY() - 1) ) );
+						Cells[left][top].setFace(faces[layer],
+								layer + 1, x_size,
+								y_size, is_off_map);
+				              } else if ((x < XMAX) && (y < YMAX)) {
+					              addDirtyRectangle(clippedRectangle(x + MAX_FACE_SIZE, 
+						      		y + MAX_FACE_SIZE,
+								x + MAX_FACE_SIZE + Cells[x][y].biggestX() - 1,
+								y + MAX_FACE_SIZE + Cells[x][y].biggestY() - 1) );
+								Cells[x + MAX_FACE_SIZE][y + MAX_FACE_SIZE].setFace(faces[layer], layer + 1);
+					              }
+			              }
+		              }
+	              }
+
+
+	              /*
+	                  It sucks that we have to register for ALL THESE
+	                  just to listen for mouse clicks. Sigh.
+	                */
+	              public void mouseClicked(MouseEvent evt) {
+		              int dx = (evt.getX() / pixwidth) - XMAX / 2;
+		              int dy = (evt.getY() / pixheight) - YMAX / 2;
+		              int buttonmask = evt.getModifiers();
+
+		              if ((buttonmask & InputEvent.BUTTON3_MASK) > 0) {}
+		              else if ((buttonmask & InputEvent.BUTTON1_MASK) > 0) {
+			              Parent.lookat(dx, dy);
+		              }
+	              }
+
+
+	              public void mouseEntered(MouseEvent evt) {}
+
+
+	              public void mouseExited(MouseEvent evt) {}
+
+
+	              public void mousePressed(MouseEvent evt) {}
+
+
+	              public void mouseReleased(MouseEvent evt) {}
+
+
+	              // given the cell coords, clear the cell on the
+	              // back-buffer
+
+
+
+	              public void clearCell(int cellx, int celly) {
+
+		              fullmapgc.clearRect((cellx - MAX_FACE_SIZE) * pixwidth,
+		                                  (celly - MAX_FACE_SIZE) * pixheight,
+		                                  pixwidth, pixheight);
+	              }
+
+
+	              // given the cell coordinates and a layer number
+	              // draw a single layer of the cell
+
+	              public void paintCellLayer(int cellx, int celly, int layer) {
+		              MapSquare cell = Cells[cellx][celly];
+		              int imgindex = -1;
+		              Image img;
+		              imgindex = cell.getFace(layer);
+		              if (imgindex <= 0) {
+			              return;
+		              }
+		              img = Parent.getPixmap(imgindex);
+		              if (img == null) {
+			              debug("We need face " + imgindex);
+			              // get "?" image
+			              img = Parent.getPixmap(0);
+			              // put placeholder, so we dont request it
+			              // again, then request from server.
+			              Parent.addPixmap(imgindex, img);
+			              Parent.askForFace(imgindex);
+		              }
+		              fullmapgc.drawImage(img, (cellx - MAX_FACE_SIZE) * pixwidth,
+		                                  (celly - MAX_FACE_SIZE) * pixheight,
+		                                  this);
+	              }
+
+
+	              /*
+	                  given the coordinates, paint all the layers of a cell, clearing the
+	                  rectangle first.
+	                */
+	              public void paintCell(int x, int y) {
+		              clearCell(x, y);
+		              for (int i = 1; i <= MapSquare.getLayerCount(); i++) {
+			              paintCellLayer(x, y, i);
+		              }
+	              }
+
+
+	              /*
+	                  used when we get a newmap command
+	                */
+	              public void clearmap() {
+		              for (int y = 0; y < BUFFERY; y++) {
+			              for (int x = 0; x < BUFFERX; x++) {
+				              Cells[x][y].clear();
+			              }
+		              }
+			      addDirtyRectangle(new Rectangle(0, 0, BUFFERX -1 , BUFFERY - 1) );
+	              }
+
+
+	              /* mark all the squares containing the given face */
+	              public synchronized void dirtyFace(int face) {
+		              for (int y = 0; y < BUFFERY; y++) {
+			              for(int x = 0; x < BUFFERX; x++) {
+				              if (Cells[x][y].containsFace(face) ) {
+					              addDirtyRectangle(clippedRectangle(x, y, Cells[x][y].biggestX() + x - 1,
+					                                              Cells[x][y].biggestY() + y - 1) );
+				              }
+			              }
+		              }
+	              }
+
+	              // paint the back-buffer to the screen
+
+	              public void paint(Graphics gc) {
+		              //debug("MapWin.paint(gc) called");
+
+		              if (fullmap == null) {
+			              debug("oops.. fullmap null for paint(), trying init");
+			              initBackBuffer();
+			              if (fullmap == null) {
+				              debug("oops.. fullmap STILL null?");
+				              return;
+			              }
+		              }
+
+		              gc.drawImage(fullmap, 0, 0, this);
+	              }
+
+
+	              /*
+	                  This sets the image for the player. We will automatically
+	                  draw this image at the center of the screen
+	                */
+	              public synchronized void setPlayerImage(int facenum) {
+		              PlayerImage = Parent.getPixmap(facenum);
+		              PlayerImageNum = facenum;
+		              debug(" MapWin set PlayerImageNum to " + facenum);
+		              if (PlayerImage == null) {
+			              debug("(But image is null right now)");
+		              } else {
+			              debug(" (Player image NOT null)");
+			              addDirtyRectangle(new Rectangle(BUFFERX/2, BUFFERY/2, BUFFERX/2, BUFFERY/2) );
+		              }
+	              }
+
+
+	              // Since paint(gc) draws Every Cell, there is no need for
+	              // the default update() behaviour of clearing the window first
+
+	              public void update(Graphics gc) {
+		              paint(gc);
+	              }
+
+
+	              public MapWin(CFclient parent) {
+		              Parent = parent;
+		              setSize(pixwidth * XMAX, pixheight * YMAX);
+		              setBackground(Color.black);
+		              setForeground(Color.white);
+
+		              // These should really be on parent.
+		              // but again, lets do this for debug now
+
+		              addKeyListener(Parent);
+		              addFocusListener(Parent);
+
+		              addMouseListener(this);
+
+		              //	   initBackBuffer();
+		              mapthread = new MapUpdater(this, parent);
+		              mapthread.start();
+	              }
+
+
+	              public void askForFocus() {
+		              debug("MapWin taking Keyboard Focus");
+		              requestFocus();
+	              }
+              }
 
-	public void askForFocus(){
-		debug("MapWin taking Keyboard Focus");
-		requestFocus();
-	}
-}