home *** CD-ROM | disk | FTP | other *** search
Java Source | 1996-08-14 | 14.8 KB | 545 lines |
- /*
- * @(#)ClientBoardComponent.java
- */
-
- package games.Battle.client.ClientApplet;
-
- import java.awt.Canvas;
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.Graphics;
- import java.awt.Image;
- import java.awt.Event;
- import java.util.Random;
- import java.io.OutputStream;
-
- import games.Battle.shared.sys.Rules;
- import games.Battle.shared.sys.Symbols;
- import games.Battle.shared.comm.Command;
-
- /**
- * The ClientBoardComponent is the awt component derivation responsible
- * for a) maintaining and refreshing an offscreen game animation bitmap
- * and b) collecting events from the user and sending Commands to the
- * server on behalf of the player.
- *
- * The methods setPlayer() and setOutputStream() must be called
- * after contructing a ClientBoardCompent.
- *
- * @author Alex Nicolaou
- * @author Jay Steele
- */
- public class ClientBoardComponent extends Canvas
- {
- static Random rand = new Random();
-
- /**
- * The offscreen image used to make the board updating flicker-free.
- */
- Image offScreenImage = null;
-
- /**
- * The output stream to the server.
- */
- OutputStream os = null;
-
- /**
- * The board data for the client.
- */
- ClientBoard board = null;
-
- /**
- * The look of the client.
- */
- ClientLook look = null;
-
- /**
- * The last recorded x-mouse location.
- */
- int lastx=0;
-
- /**
- * The last recorded y-mouse location.
- */
- int lasty=0;
-
- /**
- * The player this component is processing events for.
- */
- int player = -1;
-
- /**
- * Construct a ClientBoardComponent with the given ClientBoard. We
- * keep a reference to the board we are playing on in order to
- * give the clients some intelligence with respect to the Commands
- * they will be sending to the server.
- * @param b the client board defining the current battle field
- * @see ClientBoard
- */
- public ClientBoardComponent(ClientBoard b) {
- board = b;
- }
-
- /**
- * We allow a reference to the look for this game board to be
- * stored here so that we can force it to update with the "!"
- * command. This is an undocumented emergency procedure for
- * forcing the client to update its terrain.
- * @param lk the ClientLook reference to update
- * @see ClientLook
- */
- public void setLook(ClientLook lk) {
- look = lk;
- }
-
- /**
- * Setting the player id for this component allows the component
- * to process events from the user with some degree of intelligence.
- * For example, events not occuring in a cell owned by this id
- * are thrown away. This method must be called after constructing
- * a ClientBoardComponent.
- * @param p the id of the player this ClientBoardComponent represents
- */
- public void setPlayer(int p) {
- player = p;
- }
-
- /**
- * Commands generated by user input events are sent to this output
- * stream, presumably a connection to the server.
- * @param os the output stream to send commands to
- */
- public void setOutputStream(OutputStream os) {
- this.os = os;
- }
-
- /**
- * We overload the update method to do nothing, as the regular
- * update cycles generated from the connection to the server will
- * regularly update.
- */
- public void update(Graphics g) {
- // Do nothing, the regular update cycle will handle painting
- }
-
- /**
- * We use the paint method to see if an offscreen bitmap image
- * has been created yet, and if not, to create one.
- * @param g unused
- */
- public void paint(Graphics g) {
- // If we don't have an offscreen image yet, create one
- // This seems to be one of the few valid places to do this
- requestFocus();
- if (offScreenImage == null) {
- offScreenImage = createImage(Symbols.BOARD_W, Symbols.BOARD_H);
- }
- }
-
- /**
- * Return a reference to the offscreen image being maintained by
- * this ClientBoardComponent.
- */
- public Image getOffScreenImage() {
- return offScreenImage;
- }
-
- /**
- * Return the preferred size for this component
- */
- public Dimension preferredSize() {
- return new Dimension(Symbols.BOARD_W, Symbols.BOARD_H);
- }
-
- /**
- * Given a "world" y-coordate, return which row this occurs in
- * @param y the y coordinate in board space
- */
- public int getRowFromBoardCoord(int y) {
- float height = (float)Symbols.BOARD_H / Rules.rows;
- int r = (int)((float)y / height);
- return r;
- }
-
- /**
- * Given a "world" x-coordinate, return which column this occurs in
- * @param x the x coordinate in board space
- */
- public int getColFromBoardCoord(int x) {
- float width = (float)Symbols.BOARD_W / Rules.cols;
- int c = (int)((float)x / width);
- return c;
- }
-
- /**
- * Given a "world" x-coordinate, return an x-coordinate in cell
- * space for the cell the mouse is currently located inside
- */
- public int getXPosFromBoardCoord(int x) {
- float width = (float)Symbols.BOARD_W / Rules.cols;
- int c = (int)((float)x / width);
- return (int)((float)x - (float)c * width);
- }
-
- /**
- * Given a "world" y-coordinate, return an y-coordinate in cell
- * space for the cell the mouse is currently located inside
- */
- public int getYPosFromBoardCoord(int y) {
- float height = (float)Symbols.BOARD_H / Rules.rows;
- int r = (int)((float)y / height);
- return (int)((float)y - (float)r * height);
- }
-
- /**
- * Handle game action events generated by a user playing the game.
- * When valid events are processed, a Command is created and
- * dumped to the server via out output stream.
- * @param ev the event to process
- */
- public boolean handleEvent(Event ev) {
-
- // If the output stream to the server has not yet been
- // established, we just get out without doing any processing.
- if (os == null)
- return false;
-
- // since the ev.x and ev.y location is not consistently
- // updated on keypresses from platform to platform, we
- // must know where the mouse is at all times.
- if (ev.id == Event.MOUSE_MOVE) {
- lastx = ev.x;
- lasty = ev.y;
- }
-
- // Figure out which cell we are on the board from our position
- // in the component.
- int row = getRowFromBoardCoord(lasty);
- int col = getColFromBoardCoord(lastx);
- int x = getXPosFromBoardCoord(lastx);
- int y = getYPosFromBoardCoord(lasty);
-
- // We are not allowed to issue events in any other square but
- // our own. If we are not in one of our cells, immediately
- // avoid processing.
- ClientCell checkcell = (ClientCell)board.getCell(row, col);
- if (checkcell.getOccupancy() != player) return false;
-
- if (ev.id == Event.MOUSE_DOWN) {
- Command cmd = null;
-
- if (ev.metaDown() || (ev.modifiers & Event.ALT_MASK) != 0) {
- cmd = new Command(0, Symbols.CLEAR_PIPES, row, col, x, y);
- try {
- cmd.writeTo(os);
- } catch ( Exception e ) {
- // where's the server?
- }
- }
-
- ClientCell cell = (ClientCell)board.getCell(row, col);
-
- int pipe = cell.pipeRegion(x, y);
-
- switch(pipe) {
- case Symbols.NORTH:
- cmd = new Command(0, Symbols.NORTH_PIPE, row, col, x, y);
- paintTempPipe(row, col, pipe);
- ClientSounds.playClick();
- break;
- case Symbols.SOUTH:
- cmd = new Command(0, Symbols.SOUTH_PIPE, row, col, x, y);
- paintTempPipe(row, col, pipe);
- ClientSounds.playClick();
- break;
- case Symbols.EAST:
- cmd = new Command(0, Symbols.EAST_PIPE, row, col, x, y);
- paintTempPipe(row, col, pipe);
- ClientSounds.playClick();
- break;
- case Symbols.WEST:
- cmd = new Command(0, Symbols.WEST_PIPE, row, col, x, y);
- paintTempPipe(row, col, pipe);
- ClientSounds.playClick();
- break;
- }
- if (cmd != null) {
- try {
- cmd.writeTo(os);
- } catch ( Exception e ) {
- // where's the server?
- }
- }
- }
-
- else if (ev.id == Event.KEY_PRESS) {
-
- Command cmd = null;
- switch ((char)ev.key) {
-
- case 'i':
- case 'I':
- // north
- cmd = new Command(Symbols.PLAYER0, Symbols.NORTH_PIPE,
- row, col, x, y);
- paintTempPipe(row, col, Symbols.NORTH);
- ClientSounds.playClick();
- break;
- case 'j':
- case 'J':
- // west
- cmd = new Command(Symbols.PLAYER0, Symbols.WEST_PIPE,
- row, col, x, y);
- paintTempPipe(row, col, Symbols.WEST);
- ClientSounds.playClick();
- break;
- case 'k':
- case 'K':
- // south
- cmd = new Command(Symbols.PLAYER0, Symbols.SOUTH_PIPE,
- row, col, x, y);
- paintTempPipe(row, col, Symbols.SOUTH);
- ClientSounds.playClick();
- break;
- case 'l':
- case 'L':
- // east
- cmd = new Command(Symbols.PLAYER0, Symbols.EAST_PIPE,
- row, col, x, y);
- paintTempPipe(row, col, Symbols.EAST);
- ClientSounds.playClick();
- break;
- // clear all of the pipes in a square
- case ' ':
- System.out.println("SPACE");
- cmd = new Command(0, Symbols.CLEAR_PIPES, row, col, x, y);
- ClientSounds.playClick();
- break;
-
- // Rebuild the terrain (in case it didn't draw correctly
- // the first time.
- case '!':
- if (look != null) look.updateTerrain(board);
- break;
-
- // Dig
- case 'd':
- case 'D':
- cmd = new Command(0, Symbols.DIG, row, col, x, y);
- break;
-
- // Fill
- case 'f':
- case 'F':
- cmd = new Command(0, Symbols.FILL, row, col, x, y);
- break;
-
- // Guns
- case 'g':
- case 'G':
- {
- double perx = (x / 16.0) - 1.0;
- double pery = (y / 16.0) - 1.0;
- perx *= 1.3;
- pery *= 1.3;
- int offx = (int)((Rules.gunRange + 1) * perx);
- int offy = (int)((Rules.gunRange + 1) * pery);
-
- if (offx > Rules.gunRange) {
- offx = Rules.gunRange;
- }
- if (offx < -Rules.gunRange) {
- offx = -Rules.gunRange;
- }
- if (offy > Rules.gunRange) {
- offy = Rules.gunRange;
- }
- if (offy < -Rules.gunRange) {
- offy = -Rules.gunRange;
- }
-
- // we ship the command to the server with the
- // source of the guns in row, col and
- // the (r,c) offset in x, y. The server validates
- // and ignores offenses.
- if (row+offy >= 0 && row+offy <= Rules.rows-1
- && col+offx >= 0 && col+offx <= Rules.cols-1)
- {
- cmd = new Command(0, Symbols.GUN, row, col,
- row+offy, col+offx);
-
- // draw a gun in a random position in the
- // destination cell
- int rx = Math.abs(rand.nextInt())
- % (32-ClientImages.gun[0].getWidth(this));
- int ry = Math.abs(rand.nextInt())
- % (32-ClientImages.gun[0].getHeight(this));
- int idx = Math.abs(rand.nextInt()) % 4;
- getGraphics().drawImage(ClientImages.gun[idx],
- 32*(col+offx)+rx, 32*(row+offy)+ry, this);
-
- ClientSounds.playGun();
- }
- break;
- }
-
- // Paratrooper
- case 'p':
- case 'P':
- {
- double perx = (x / 16.0) - 1.0;
- double pery = (y / 16.0) - 1.0;
- perx *= 1.3;
- pery *= 1.3;
- int offx = (int)((Rules.parRange + 1) * perx);
- int offy = (int)((Rules.parRange + 1) * pery);
-
- if (offx > Rules.parRange) {
- offx = Rules.parRange;
- }
- if (offx < -Rules.parRange) {
- offx = -Rules.parRange;
- }
- if (offy > Rules.parRange) {
- offy = Rules.parRange;
- }
- if (offy < -Rules.parRange) {
- offy = -Rules.parRange;
- }
-
- // we ship the command to the server with the
- // source of the paratroopers in row, col and
- // the destination in x, y. The server validates
- // and ignores offenses.
- if (row+offy >= 0 && row+offy <= Rules.rows-1
- && col+offx >= 0 && col+offx <= Rules.cols-1)
- {
- cmd = new Command(0, Symbols.PARATROOP, row, col,
- row+offy, col+offx);
-
- // draw a paratrooper in a random position in the
- // destination cell
- int rx = Math.abs(rand.nextInt())
- % (32-ClientImages.paratrooper.getWidth(this));
- int ry = Math.abs(rand.nextInt())
- % (32-ClientImages.paratrooper.getHeight(this));
- getGraphics().drawImage(ClientImages.paratrooper,
- 32*(col+offx)+rx, 32*(row+offy)+ry, this);
-
- ClientSounds.playParatrooper();
- }
- break;
- }
-
- /*
- // This feature has not been added to the server
- // Build
- case 'b':
- case 'B':
- cmd = new Command(0, Symbols.BUILD, row, col, x, y);
- break;
- */
-
- /*
- // This feature has not been added to the server
- // Scuttle
- case 's':
- case 'S':
- cmd = new Command(0, Symbols.SCUTTLE, row, col, x, y);
- break;
- */
-
- /*
- // This feature has not been added to the server
- // Reserves
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- cmd = new Command(0, Symbols.RESERVES, row, col,
- ev.key - (int)'0', 0);
- break;
- */
- }
-
- if (cmd != null) {
- try {
- cmd.writeTo(os);
- } catch ( Exception e ) {
- // where's the server?
- }
- }
- }
- return true;
- }
-
- /**
- * Paint a temporary pipe on the game board, which will be overwritten
- * on the next game turn update. If the server agrees that you can
- * set a pipe on this square, it will tell the client to draw a real
- * pipe. This pipe is simply to give the user some immediate feedback
- * that their commands are being processed.
- * @param row the row to paint the pipe in
- * @param col the column to paint the pipe in
- * @param pipe the pipe (n, s, e, w) to paint
- */
- void paintTempPipe(int row, int col, int pipe) {
-
- Graphics gc = getGraphics();
-
- int xorg = col * 32;
- int yorg = row * 32;
-
- int width = 32;
- int height = 32;
-
- int wh = (int)((float)width / (float)2.0);
- int hh = (int)((float)height / (float)2.0);
- int wa = (int)((float)width / (float)3.0);
- int ha = (int)((float)height / (float)3.0);
- int wb = (int)((float)width * (float)2.0 / (float)3.0);
- int hb = (int)((float)height * (float)2.0 / (float)3.0);
-
- Color outer = Color.lightGray;
- Color inner = Color.darkGray;
-
- if (pipe == Symbols.NORTH) {
- gc.setColor(inner);
- gc.drawLine(xorg+wh-1, yorg+1, xorg+wh-1, yorg+ha);
- gc.drawLine(xorg+wh+1, yorg+1, xorg+wh+1, yorg+ha);
- gc.setColor(outer);
- gc.drawLine(xorg+wh, yorg+1, xorg+wh, yorg+ha);
- } else
-
- if (pipe == Symbols.SOUTH) {
- gc.setColor(inner);
- gc.drawLine(xorg+wh-1, yorg+hb, xorg+wh-1, yorg+height-1);
- gc.drawLine(xorg+wh+1, yorg+hb, xorg+wh+1, yorg+height-1);
- gc.setColor(outer);
- gc.drawLine(xorg+wh, yorg+hb, xorg+wh, yorg+height-1);
- } else
-
- if (pipe == Symbols.EAST) {
- gc.setColor(inner);
- gc.drawLine(xorg+wb, yorg+hh-1, xorg+width, yorg+hh-1);
- gc.drawLine(xorg+wb, yorg+hh+1, xorg+width, yorg+hh+1);
- gc.setColor(outer);
- gc.drawLine(xorg+wb, yorg+hh, xorg+width, yorg+hh);
- } else
-
- if (pipe == Symbols.WEST) {
- gc.setColor(inner);
- gc.drawLine(xorg+1, yorg+hh-1, xorg+wa, yorg+hh-1);
- gc.drawLine(xorg+1, yorg+hh+1, xorg+wa, yorg+hh+1);
- gc.setColor(outer);
- gc.drawLine(xorg+1, yorg+hh, xorg+wa, yorg+hh);
- }
- }
- }
-