home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / enxle1f6 / src / games / battle / client / clientapplet / clientboardcomponent.java < prev    next >
Encoding:
Java Source  |  1996-08-14  |  14.8 KB  |  545 lines

  1. /*
  2.  * @(#)ClientBoardComponent.java
  3.  */
  4.  
  5. package games.Battle.client.ClientApplet;
  6.  
  7. import java.awt.Canvas;
  8. import java.awt.Color;
  9. import java.awt.Dimension;
  10. import java.awt.Graphics;
  11. import java.awt.Image;
  12. import java.awt.Event;
  13. import java.util.Random;
  14. import java.io.OutputStream;
  15.  
  16. import games.Battle.shared.sys.Rules;
  17. import games.Battle.shared.sys.Symbols;
  18. import games.Battle.shared.comm.Command;
  19.  
  20. /**
  21.  * The ClientBoardComponent is the awt component derivation responsible
  22.  * for a) maintaining and refreshing an offscreen game animation bitmap
  23.  * and b) collecting events from the user and sending Commands to the
  24.  * server on behalf of the player.
  25.  *
  26.  * The methods setPlayer() and setOutputStream() must be called
  27.  * after contructing a ClientBoardCompent.
  28.  *
  29.  * @author Alex Nicolaou
  30.  * @author Jay Steele
  31.  */
  32. public class ClientBoardComponent extends Canvas
  33. {
  34.     static Random rand = new Random();
  35.  
  36.     /**
  37.      * The offscreen image used to make the board updating flicker-free.
  38.      */
  39.     Image offScreenImage = null;
  40.  
  41.     /** 
  42.      * The output stream to the server.
  43.      */
  44.     OutputStream os = null;
  45.  
  46.     /** 
  47.      * The board data for the client.
  48.      */
  49.     ClientBoard board = null;
  50.  
  51.     /** 
  52.      * The look of the client.
  53.      */
  54.     ClientLook look = null;
  55.  
  56.     /** 
  57.      * The last recorded x-mouse location.
  58.      */
  59.     int lastx=0;
  60.  
  61.     /** 
  62.      * The last recorded y-mouse location.
  63.      */
  64.     int lasty=0;
  65.  
  66.     /** 
  67.      * The player this component is processing events for.
  68.      */
  69.     int player = -1;
  70.  
  71.     /**
  72.      * Construct a ClientBoardComponent with the given ClientBoard. We
  73.      * keep a reference to the board we are playing on in order to
  74.      * give the clients some intelligence with respect to the Commands
  75.      * they will be sending to the server.
  76.      * @param b the client board defining the current battle field
  77.      * @see ClientBoard
  78.      */
  79.     public ClientBoardComponent(ClientBoard b) {
  80.         board = b;
  81.     }
  82.  
  83.     /**
  84.      * We allow a reference to the look for this game board to be
  85.      * stored here so that we can force it to update with the "!"
  86.      * command. This is an undocumented emergency procedure for
  87.      * forcing the client to update its terrain.
  88.      * @param lk the ClientLook reference to update
  89.      * @see ClientLook
  90.      */
  91.     public void setLook(ClientLook lk) {
  92.         look = lk;
  93.     }
  94.  
  95.     /**
  96.      * Setting the player id for this component allows the component
  97.      * to process events from the user with some degree of intelligence.
  98.      * For example, events not occuring in a cell owned by this id
  99.      * are thrown away. This method must be called after constructing
  100.      * a ClientBoardComponent.
  101.      * @param p the id of the player this ClientBoardComponent represents
  102.      */
  103.     public void setPlayer(int p) {
  104.         player = p;
  105.     }
  106.  
  107.     /**
  108.      * Commands generated by user input events are sent to this output
  109.      * stream, presumably a connection to the server.
  110.      * @param os the output stream to send commands to
  111.      */
  112.     public void setOutputStream(OutputStream os) {
  113.         this.os = os;
  114.     }
  115.  
  116.     /**
  117.      * We overload the update method to do nothing, as the regular
  118.      * update cycles generated from the connection to the server will
  119.      * regularly update.
  120.      */
  121.     public void update(Graphics g) {
  122.         // Do nothing, the regular update cycle will handle painting
  123.     }
  124.  
  125.     /**
  126.      * We use the paint method to see if an offscreen bitmap image
  127.      * has been created yet, and if not, to create one.
  128.      * @param g unused
  129.      */
  130.     public void paint(Graphics g) {
  131.         // If we don't have an offscreen image yet, create one
  132.         // This seems to be one of the few valid places to do this
  133.         requestFocus();
  134.         if (offScreenImage == null) {
  135.             offScreenImage = createImage(Symbols.BOARD_W, Symbols.BOARD_H);
  136.         }
  137.     }
  138.  
  139.     /**
  140.      * Return a reference to the offscreen image being maintained by
  141.      * this ClientBoardComponent.
  142.      */
  143.     public Image getOffScreenImage() {
  144.         return offScreenImage;
  145.     }
  146.  
  147.     /**
  148.      * Return the preferred size for this component
  149.      */
  150.     public Dimension preferredSize() {
  151.         return new Dimension(Symbols.BOARD_W, Symbols.BOARD_H);
  152.     }
  153.  
  154.     /**
  155.      * Given a "world" y-coordate, return which row this occurs in
  156.      * @param y the y coordinate in board space
  157.      */
  158.     public int getRowFromBoardCoord(int y) {
  159.         float height = (float)Symbols.BOARD_H / Rules.rows;
  160.         int r = (int)((float)y / height);
  161.         return r;
  162.     }
  163.  
  164.     /**
  165.      * Given a "world" x-coordinate, return which column this occurs in
  166.      * @param x the x coordinate in board space
  167.      */
  168.     public int getColFromBoardCoord(int x) {
  169.         float width = (float)Symbols.BOARD_W / Rules.cols;
  170.         int c = (int)((float)x / width);
  171.         return c;
  172.     }
  173.  
  174.     /**
  175.      * Given a "world" x-coordinate, return an x-coordinate in cell
  176.      * space for the cell the mouse is currently located inside
  177.      */
  178.     public int getXPosFromBoardCoord(int x) {
  179.         float width = (float)Symbols.BOARD_W / Rules.cols;
  180.         int c = (int)((float)x / width);
  181.         return (int)((float)x - (float)c * width);
  182.     }
  183.  
  184.     /**
  185.      * Given a "world" y-coordinate, return an y-coordinate in cell
  186.      * space for the cell the mouse is currently located inside
  187.      */
  188.     public int getYPosFromBoardCoord(int y) {
  189.         float height = (float)Symbols.BOARD_H / Rules.rows;
  190.         int r = (int)((float)y / height);
  191.         return (int)((float)y - (float)r * height);
  192.     }
  193.  
  194.     /**
  195.      * Handle game action events generated by a user playing the game.
  196.      * When valid events are processed, a Command is created and
  197.      * dumped to the server via out output stream.
  198.      * @param ev the event to process
  199.      */
  200.     public boolean handleEvent(Event ev) {
  201.  
  202.         // If the output stream to the server has not yet been
  203.         // established, we just get out without doing any processing.
  204.         if (os == null) 
  205.             return false;
  206.  
  207.         // since the ev.x and ev.y location is not consistently
  208.         // updated on keypresses from platform to platform, we
  209.         // must know where the mouse is at all times.
  210.         if (ev.id == Event.MOUSE_MOVE) {
  211.             lastx = ev.x;
  212.             lasty = ev.y;
  213.         }
  214.  
  215.         // Figure out which cell we are on the board from our position
  216.         // in the component.
  217.         int row = getRowFromBoardCoord(lasty);
  218.         int col = getColFromBoardCoord(lastx);
  219.         int x = getXPosFromBoardCoord(lastx);
  220.         int y = getYPosFromBoardCoord(lasty);
  221.  
  222.         // We are not allowed to issue events in any other square but
  223.         // our own. If we are not in one of our cells, immediately
  224.         // avoid processing.
  225.         ClientCell checkcell = (ClientCell)board.getCell(row, col);
  226.         if (checkcell.getOccupancy() != player) return false;
  227.  
  228.         if (ev.id == Event.MOUSE_DOWN) {
  229.             Command cmd = null;
  230.  
  231.             if (ev.metaDown() || (ev.modifiers & Event.ALT_MASK) != 0) {
  232.                 cmd = new Command(0, Symbols.CLEAR_PIPES, row, col, x, y);
  233.                 try {
  234.                     cmd.writeTo(os);
  235.                 } catch ( Exception e ) {
  236.                     // where's the server?
  237.                 }
  238.             }
  239.  
  240.             ClientCell cell = (ClientCell)board.getCell(row, col);
  241.  
  242.             int pipe = cell.pipeRegion(x, y);
  243.  
  244.             switch(pipe) {
  245.                 case Symbols.NORTH: 
  246.                     cmd = new Command(0, Symbols.NORTH_PIPE, row, col, x, y);
  247.                     paintTempPipe(row, col, pipe);
  248.                     ClientSounds.playClick();
  249.                     break;
  250.                 case Symbols.SOUTH:
  251.                     cmd = new Command(0, Symbols.SOUTH_PIPE, row, col, x, y);
  252.                     paintTempPipe(row, col, pipe);
  253.                     ClientSounds.playClick();
  254.                     break;
  255.                 case Symbols.EAST:
  256.                     cmd = new Command(0, Symbols.EAST_PIPE, row, col, x, y);
  257.                     paintTempPipe(row, col, pipe);
  258.                     ClientSounds.playClick();
  259.                     break;
  260.                 case Symbols.WEST:
  261.                     cmd = new Command(0, Symbols.WEST_PIPE, row, col, x, y);
  262.                     paintTempPipe(row, col, pipe);
  263.                     ClientSounds.playClick();
  264.                     break;
  265.             }
  266.             if (cmd != null) {
  267.                 try {
  268.                     cmd.writeTo(os);
  269.                 } catch ( Exception e ) {
  270.                     // where's the server?
  271.                 }
  272.             }
  273.         }
  274.  
  275.         else if (ev.id == Event.KEY_PRESS) {
  276.  
  277.             Command cmd = null;
  278.             switch ((char)ev.key) {
  279.  
  280.                 case 'i':
  281.                 case 'I':
  282.                     // north
  283.                     cmd = new Command(Symbols.PLAYER0, Symbols.NORTH_PIPE,
  284.                                         row, col, x, y);
  285.                     paintTempPipe(row, col, Symbols.NORTH);
  286.                     ClientSounds.playClick();
  287.                     break;
  288.                 case 'j':
  289.                 case 'J':
  290.                     // west
  291.                     cmd = new Command(Symbols.PLAYER0, Symbols.WEST_PIPE,
  292.                                         row, col, x, y);
  293.                     paintTempPipe(row, col, Symbols.WEST);
  294.                     ClientSounds.playClick();
  295.                     break;
  296.                 case 'k':
  297.                 case 'K':
  298.                     // south
  299.                     cmd = new Command(Symbols.PLAYER0, Symbols.SOUTH_PIPE,
  300.                                         row, col, x, y);
  301.                     paintTempPipe(row, col, Symbols.SOUTH);
  302.                     ClientSounds.playClick();
  303.                     break;
  304.                 case 'l':
  305.                 case 'L':
  306.                     // east
  307.                     cmd = new Command(Symbols.PLAYER0, Symbols.EAST_PIPE,
  308.                                         row, col, x, y);
  309.                     paintTempPipe(row, col, Symbols.EAST);
  310.                     ClientSounds.playClick();
  311.                     break;
  312.                 // clear all of the pipes in a square
  313.                 case ' ':
  314.                     System.out.println("SPACE");
  315.                     cmd = new Command(0, Symbols.CLEAR_PIPES, row, col, x, y);
  316.                     ClientSounds.playClick();
  317.                     break;
  318.  
  319.                 // Rebuild the terrain (in case it didn't draw correctly
  320.                 // the first time.
  321.                 case '!':
  322.                     if (look != null) look.updateTerrain(board);
  323.                     break;
  324.  
  325.                 // Dig
  326.                 case 'd':
  327.                 case 'D':
  328.                     cmd = new Command(0, Symbols.DIG, row, col, x, y);
  329.                     break;
  330.  
  331.                 // Fill
  332.                 case 'f':
  333.                 case 'F':
  334.                     cmd = new Command(0, Symbols.FILL, row, col, x, y);
  335.                     break;
  336.  
  337.                 // Guns
  338.                 case 'g':
  339.                 case 'G':
  340.                 {
  341.                     double perx = (x / 16.0) - 1.0;
  342.                     double pery = (y / 16.0) - 1.0;
  343.                     perx *= 1.3;
  344.                     pery *= 1.3;
  345.                     int offx = (int)((Rules.gunRange + 1) * perx);
  346.                     int offy = (int)((Rules.gunRange + 1) * pery);
  347.  
  348.                     if (offx > Rules.gunRange) {
  349.                         offx = Rules.gunRange;
  350.                     }
  351.                     if (offx < -Rules.gunRange) {
  352.                         offx = -Rules.gunRange;
  353.                     }
  354.                     if (offy > Rules.gunRange) {
  355.                         offy = Rules.gunRange;
  356.                     }
  357.                     if (offy < -Rules.gunRange) {
  358.                         offy = -Rules.gunRange;
  359.                     }
  360.  
  361.                     // we ship the command to the server with the
  362.                     // source of the guns in row, col and 
  363.                     // the (r,c) offset in x, y. The server validates
  364.                     // and ignores offenses.
  365.                     if (row+offy >= 0 && row+offy <= Rules.rows-1
  366.                         && col+offx >= 0 && col+offx <= Rules.cols-1)
  367.                     {
  368.                         cmd = new Command(0, Symbols.GUN, row, col, 
  369.                                             row+offy, col+offx);
  370.  
  371.                         // draw a gun in a random position in the
  372.                         // destination cell
  373.                         int rx = Math.abs(rand.nextInt()) 
  374.                                 % (32-ClientImages.gun[0].getWidth(this));
  375.                         int ry = Math.abs(rand.nextInt()) 
  376.                                 % (32-ClientImages.gun[0].getHeight(this));
  377.                         int idx = Math.abs(rand.nextInt()) % 4;
  378.                         getGraphics().drawImage(ClientImages.gun[idx], 
  379.                             32*(col+offx)+rx, 32*(row+offy)+ry, this);
  380.  
  381.                         ClientSounds.playGun();
  382.                     }
  383.                     break;
  384.                 }
  385.  
  386.                 // Paratrooper
  387.                 case 'p':
  388.                 case 'P':
  389.                 {
  390.                     double perx = (x / 16.0) - 1.0;
  391.                     double pery = (y / 16.0) - 1.0;
  392.                     perx *= 1.3;
  393.                     pery *= 1.3;
  394.                     int offx = (int)((Rules.parRange + 1) * perx);
  395.                     int offy = (int)((Rules.parRange + 1) * pery);
  396.  
  397.                     if (offx > Rules.parRange) {
  398.                         offx = Rules.parRange;
  399.                     }
  400.                     if (offx < -Rules.parRange) {
  401.                         offx = -Rules.parRange;
  402.                     }
  403.                     if (offy > Rules.parRange) {
  404.                         offy = Rules.parRange;
  405.                     }
  406.                     if (offy < -Rules.parRange) {
  407.                         offy = -Rules.parRange;
  408.                     }
  409.  
  410.                     // we ship the command to the server with the
  411.                     // source of the paratroopers in row, col and 
  412.                     // the destination in x, y. The server validates
  413.                     // and ignores offenses.
  414.                     if (row+offy >= 0 && row+offy <= Rules.rows-1
  415.                         && col+offx >= 0 && col+offx <= Rules.cols-1)
  416.                     {
  417.                         cmd = new Command(0, Symbols.PARATROOP, row, col, 
  418.                                             row+offy, col+offx);
  419.  
  420.                         // draw a paratrooper in a random position in the
  421.                         // destination cell
  422.                         int rx = Math.abs(rand.nextInt()) 
  423.                                 % (32-ClientImages.paratrooper.getWidth(this));
  424.                         int ry = Math.abs(rand.nextInt()) 
  425.                                 % (32-ClientImages.paratrooper.getHeight(this));
  426.                         getGraphics().drawImage(ClientImages.paratrooper, 
  427.                             32*(col+offx)+rx, 32*(row+offy)+ry, this);
  428.  
  429.                         ClientSounds.playParatrooper();
  430.                     }
  431.                     break;
  432.                 }
  433.  
  434. /*
  435. // This feature has not been added to the server
  436.                 // Build
  437.                 case 'b':
  438.                 case 'B':
  439.                     cmd = new Command(0, Symbols.BUILD, row, col, x, y);
  440.                     break;
  441. */
  442.  
  443. /*
  444. // This feature has not been added to the server
  445.                 // Scuttle
  446.                 case 's':
  447.                 case 'S':
  448.                     cmd = new Command(0, Symbols.SCUTTLE, row, col, x, y);
  449.                     break;
  450. */
  451.  
  452. /*
  453. // This feature has not been added to the server
  454.                 // Reserves
  455.                 case '0':
  456.                 case '1':
  457.                 case '2':
  458.                 case '3':
  459.                 case '4':
  460.                 case '5':
  461.                 case '6':
  462.                 case '7':
  463.                 case '8':
  464.                 case '9':
  465.                     cmd = new Command(0, Symbols.RESERVES, row, col, 
  466.                                 ev.key - (int)'0', 0);
  467.                     break;
  468. */
  469.             }
  470.  
  471.             if (cmd != null) {
  472.                 try {
  473.                     cmd.writeTo(os);
  474.                 } catch ( Exception e ) {
  475.                     // where's the server?
  476.                 }
  477.             }
  478.         }
  479.         return true;
  480.     }
  481.  
  482.     /**
  483.      * Paint a temporary pipe on the game board, which will be overwritten
  484.      * on the next game turn update. If the server agrees that you can
  485.      * set a pipe on this square, it will tell the client to draw a real
  486.      * pipe. This pipe is simply to give the user some immediate feedback
  487.      * that their commands are being processed.
  488.      * @param row the row to paint the pipe in
  489.      * @param col the column to paint the pipe in
  490.      * @param pipe the pipe (n, s, e, w) to paint
  491.      */
  492.     void paintTempPipe(int row, int col, int pipe) {
  493.  
  494.         Graphics gc = getGraphics();
  495.  
  496.         int xorg = col * 32;
  497.         int yorg = row * 32;
  498.  
  499.         int width = 32;
  500.         int height = 32;
  501.  
  502.         int wh = (int)((float)width / (float)2.0);
  503.         int hh = (int)((float)height / (float)2.0);
  504.         int wa = (int)((float)width / (float)3.0);
  505.         int ha = (int)((float)height / (float)3.0);
  506.         int wb = (int)((float)width * (float)2.0 / (float)3.0);
  507.         int hb = (int)((float)height * (float)2.0 / (float)3.0);
  508.  
  509.         Color outer = Color.lightGray;
  510.         Color inner = Color.darkGray;
  511.  
  512.         if (pipe == Symbols.NORTH) {
  513.             gc.setColor(inner);
  514.             gc.drawLine(xorg+wh-1, yorg+1, xorg+wh-1, yorg+ha);
  515.             gc.drawLine(xorg+wh+1, yorg+1, xorg+wh+1, yorg+ha);
  516.             gc.setColor(outer);
  517.             gc.drawLine(xorg+wh, yorg+1, xorg+wh, yorg+ha);
  518.         } else 
  519.  
  520.         if (pipe == Symbols.SOUTH) {
  521.             gc.setColor(inner);
  522.             gc.drawLine(xorg+wh-1, yorg+hb, xorg+wh-1, yorg+height-1);
  523.             gc.drawLine(xorg+wh+1, yorg+hb, xorg+wh+1, yorg+height-1);
  524.             gc.setColor(outer);
  525.             gc.drawLine(xorg+wh, yorg+hb, xorg+wh, yorg+height-1);
  526.         } else 
  527.  
  528.         if (pipe == Symbols.EAST) {
  529.             gc.setColor(inner);
  530.             gc.drawLine(xorg+wb, yorg+hh-1, xorg+width, yorg+hh-1);
  531.             gc.drawLine(xorg+wb, yorg+hh+1, xorg+width, yorg+hh+1);
  532.             gc.setColor(outer);
  533.             gc.drawLine(xorg+wb, yorg+hh, xorg+width, yorg+hh);
  534.         } else 
  535.  
  536.         if (pipe == Symbols.WEST) {
  537.             gc.setColor(inner);
  538.             gc.drawLine(xorg+1, yorg+hh-1, xorg+wa, yorg+hh-1);
  539.             gc.drawLine(xorg+1, yorg+hh+1, xorg+wa, yorg+hh+1);
  540.             gc.setColor(outer);
  541.             gc.drawLine(xorg+1, yorg+hh, xorg+wa, yorg+hh);
  542.         }
  543.     }
  544. }
  545.