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 1) || (ySize[i] > 1)) { + return true; + } + } + return false; } - public synchronized void clear() { - int i; - for (i=0;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;i0, 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=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=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> 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 (x0) { - - } 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; y0, 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(); - } -}