APPENDIX B

MARBLES WORLD CLIENT CODE

import java.awt.*;

import java.applet.*;

import java.util.*;

import java.io.*;

import java.net.*;

import java.util.Date;

import vrml.external.field.*;

import vrml.external.Node;

import vrml.external.Browser;

import vrml.external.exception.*;

import netscape.javascript.JSObject;

import dtai.net.*;

 

//  ball.e is currently as follows:

//  #

//  0 - no effect

//  1 - non-collidable

//  2 - anti-gravity

//  3 - stop

//  4 - goal stealing

//  5 - add a fence

//  6 - bigger

 

//-------------------------------------------------------------------

//  This is the main applet class

 

public class Marbles extends Applet implements EventOutObserver  {

 

  TextArea output = null;

  //Main Window for Controls

  MainWindow b;

  // Browser we're using

  Browser browser;

  // Root of the scene graph (to which we add our clumps)

  Node board = null;

  Node pointer = null;

  Node designer  = null;

  Node sides  = null;

  Node slant = null;

  Node pitem[] = {null,null,null,null,null,null,null};

  Node slqu[] = {null,null,null,null};

  Node material = null;



  EventInSFColor diffuseColor = null;

  EventInMFNode addPI[] = {null,null,null,null,null,null,null};

  EventInMFNode removePI[] = {null,null,null,null,null,null,null};

  EventInMFNode addslqu[] = {null,null,null,null};

  EventInMFNode removeslqu[] = {null,null,null,null};

  Node gl[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInMFNode addGoal[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInMFNode removeGoal[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  Node sensor[] = {null,null,null,null,null,null,null};

  Node slantSensor[] = {null,null,null,null};

  Node wrapSensor = null;

  Node root[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInMFNode addChildren[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventOutSFVec3f translation_changed[] = {null,null,null,null,null,null,null,null,null,null};

  EventOutSFVec3f pointer_changed = null;

  EventInSFVec3f pointer_translation = null;

  EventInMFNode addSides = null;

  EventInMFNode removeSides = null;

  EventInMFNode removeChildren[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInSFVec3f set_translation[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInSFVec3f slant_translation = null;

  EventInSFVec3f set_scale[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  EventInSFRotation slant_rotation = null;

  EventOutSFTime touchTime[] = {null,null,null,null,null,null,null};

  EventOutSFTime slantTouchTime[] = {null,null,null,null};

  EventOutSFTime wrapTouchTime = null;

  EventOutSFVec3f hitb = null;

  EventOutSFVec3f hitt = null;

  Node[] goalshape[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  Node[] pshape[] = {null,null,null,null,null,null,null,null,null,null};

  Node[] qshape[] = {null,null,null,null};

  Node[] shape = null;

  Node[] bumpers = null;

  Node[] pieceshape[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null};

  int marker=0;

  int newpiecex=0, newpiecey=0;

  int curnode=0;

  int curtype=0;

  double hitpoint[] = {0.0,0.0,0.0,0.0};

  float offsetx=0.0f,offsety=0.0f,oldx=0.0f,oldy=0.0f,oldz=0.0f;

  int mode = 0;

  boolean speedtest=true;  //test the speed of the animator for each client

  int qno = 103;

  boolean designing = false;

  int lastmodesent = 0;

  Player[] player = new Player[4];  // array of players

  int pn; //client player number

  int nump=0;

  int numplay=0;

  int turncolor=1;

  int sim_type=1;

  public final static int PORT=8765;

  PieceList pl = new PieceList(7);

  EffectList el = new EffectList(7);

  Image image[]={null,null,null,null,null,null,null};

  PortToServer pts;

 

  public void init() {

 

    //

    // Initialize connection to Cosmo Player

    //

 

    JSObject win = JSObject.getWindow(this);

    JSObject doc = (JSObject) win.getMember("document");

    JSObject embeds = (JSObject) doc.getMember("embeds");

    browser = (Browser) embeds.getSlot(0);

 

    try {

 

    pts = new PortToServer(this.getCodeBase().getHost(),PORT,this);

    pts.start();

      for(int x=0;x<100;x++) {

         root[x] = browser.getNode("T" + x);

         addChildren[x] = (EventInMFNode) root[x].getEventIn("addChildren");

         removeChildren[x] = (EventInMFNode) root[x].getEventIn("removeChildren");

         set_translation[x] = (EventInSFVec3f) root[x].getEventIn("translation");

         set_scale[x] = (EventInSFVec3f) root[x].getEventIn("scale");

      }

      for(int x=0;x<4;x++) {

         slantSensor[x] = browser.getNode("SS" + x);

         slantTouchTime[x] = (EventOutSFTime) slantSensor[x].getEventOut("touchTime");

         slantTouchTime[x].advise(this, new Integer(10 + x));

         slqu[x] = browser.getNode("SQ" + x);

         addslqu[x] = (EventInMFNode) slqu[x].getEventIn("addChildren");

         removeslqu[x] = (EventInMFNode) slqu[x].getEventIn("removeChildren");

      }

      for(int x=0;x<7;x++) {

         pitem[x] = browser.getNode("PI" + x);

         addPI[x] = (EventInMFNode) pitem[x].getEventIn("addChildren");

         removePI[x] = (EventInMFNode) pitem[x].getEventIn("removeChildren");

      }

      for(int x=0;x<30;x++) {

         gl[x] = browser.getNode("G" + x);

         addGoal[x] = (EventInMFNode) gl[x].getEventIn("addChildren");

         removeGoal[x] = (EventInMFNode) gl[x].getEventIn("removeChildren");

      }

      board = browser.getNode("TSB");

      slant = browser.getNode("BOARD");

      wrapSensor = browser.getNode("WRAP");

      wrapTouchTime = (EventOutSFTime) wrapSensor.getEventOut("touchTime");

      wrapTouchTime.advise(this, new Integer(14));

      pointer = browser.getNode("T100");

      pointer_translation = (EventInSFVec3f) pointer.getEventIn("translation");

      slant_rotation = (EventInSFRotation) slant.getEventIn("rotation");

      slant_translation = (EventInSFVec3f) slant.getEventIn("translation");

      pointer_changed = (EventOutSFVec3f) pointer.getEventOut("translation_changed");

      designer = browser.getNode("TSD");

      sides = browser.getNode("SIDES");

      addSides = (EventInMFNode) sides.getEventIn("addChildren");

      removeSides = (EventInMFNode) sides.getEventIn("removeChildren");

      for(int x=0;x<7;x++) {

         sensor[x] = browser.getNode("TS" + x);

         touchTime[x] = (EventOutSFTime) sensor[x].getEventOut("touchTime");

         touchTime[x].advise(this, new Integer(x+1));

      }

      // Get the material node...

      material = browser.getNode("MAT");

      // Get the diffuseColor EventIn

      diffuseColor = (EventInSFColor) material.getEventIn("set_diffuseColor");

      // Get Hit Point for Board

      hitb = (EventOutSFVec3f) board.getEventOut("translation_changed");

      hitt = (EventOutSFVec3f) designer.getEventOut("hitPoint_changed");

      // Set up the callback

      hitb.advise(this, new Integer(0));

      hitt.advise(this, new Integer(7));

    }

    catch (InvalidVrmlException e) {

      showStatus("PROBLEMS!: " + e);

    }

    catch (InvalidEventInException e) {

      showStatus("PROBLEMS!: " + e);

    }

    catch (InvalidEventOutException e) {

      showStatus("PROBLEMS!: " + e);

    }

    catch (IOException e) {

      showStatus("PROBLEMS!: " + e.toString());

    }

 

    b = new MainWindow("Controls",true,this);

    b.resize(250,480);

    if(sim_type==0) {b.show();}

    b.start();

 

    for(int x=0;x<4;x++) {

        addPlayer(new Player());

    }

    for(int ww=0;ww<4;ww++) {

        addSlQu(ww,0,true);

    }

    for(int xx=1;xx<8;xx++) {

        pl.AddtoPieceList(xx);

        el.AddtoEffectList(xx);

    }

}

 

public void callback(EventOut who, double when, Object which) {

    Integer whichNum = (Integer) which;

 

if (whichNum.intValue()>0 && whichNum.intValue()<10 ) {

       if(!designing && turncolor==pn && sim_type==0){

          float[] val = hitb.getValue();

          offsetx = val[0];

          offsety = val[1];

          UpdatePointer(0,0,0,30,0,0,0,0);

          oldx = 0;

          oldy = 0;

       }

    }

    if (whichNum.intValue()==0) {

          float[] val = hitb.getValue();

          UpdateVRML(4,curnode,oldx,oldy,oldz,val[0],val[1],offsetx,offsety);

          UpdatePointer(1,oldx,oldy,oldz,val[0],val[1],offsetx,offsety);

    }

    else if (whichNum.intValue()>0 && whichNum.intValue()<8) {

       int iv=whichNum.intValue()-1;

       if (mode==1 && !designing && pl.l[iv]>0 && turncolor==pn && sim_type==0) {

           if(pl.l[iv]==1 || pl.l[iv]==6 || pl.l[iv]==7) {

               curnode=b.anim.num;

           } else {

               curnode=50+b.anim.numo;

           }

           curtype=pl.l[iv];

           addNode(curnode,curtype,0,0,0,0,0);

           designing = true;

       } else if (mode==2) {

           b.anim.ars.AddToArsenal(el.l[iv]);

       } else if (mode==3){

           String eventstring = "3" + ((100*pn) + b.anim.ars.TakeFromArsenal(iv));

           eventstring = eventstring + (b.anim.count+b.anim.eql);

           pts.writeToServer(eventstring);

           ReplaceNode(0,iv,LoadString());

           eventstring = "3" + (100*pn);

           eventstring = eventstring + (b.anim.count+b.anim.eqd);

           pts.writeToServer(eventstring);

       }

    }

    else if (whichNum.intValue()>=10 && whichNum.intValue()<=13) {  //if animating, slant added to queue

        if(mode==1 && lastmodesent==0) { //ignore request for mode change if already made a request -- only server updates mode

            pts.writeToServer("92");

            lastmodesent++;

            //Paletize(2);

        }

        else if(mode==2 && lastmodesent==1) {

            pts.writeToServer("93");

            if(designing==true) {

                  removeChildren[curnode].setValue(pieceshape[curnode]);

                  designing=false;

            }

            UpdatePointer(0,0,0,30,0,0,0,0);

            lastmodesent++;

            Bumpers(1);

            b.anim.wrapon = false;

            //Paletize(3);

        }

        else if(mode==3&&turncolor==pn) {

            pts.writeToServer("2" + (whichNum.intValue()-9));

        }

    }

    else if (whichNum.intValue()==14) {

         if(mode==0) {

              pts.writeToServer("6" + pn);  //request goals for next round

              Paletize(1);

              mode=1;

              UpdateColor(0,turncolor);

              //Four Corner Balls

              b.anim.ball[0].x=500; b.anim.ball[0].y=100; b.anim.ball[0].m=b.anim.bm;

              addNode(0,0,20,1,1,0,0);             

              //if(b.anim.rules.roundtype==0) { addNode(0,0,20,1,1,0,0); }

              //else if(b.anim.rules.roundtype==1) { addNode(0,0,20,0,1,0,0); }

              //else if(b.anim.rules.roundtype==2) { addNode(0,0,20,0,0,1,0); }

              b.anim.ball[1].x=100; b.anim.ball[1].y=500; b.anim.ball[1].m=b.anim.bm;

              addNode(1,0,20,1,0,0,0);

              //if(b.anim.rules.roundtype==0) { addNode(1,0,20,1,0,0,0); }

              //else if(b.anim.rules.roundtype==1) { addNode(1,0,20,.6,1,0,0); }

              //else if(b.anim.rules.roundtype==2) { addNode(1,0,20,1,0,0,0); }

              b.anim.ball[2].x=900; b.anim.ball[2].y=500; b.anim.ball[2].m=b.anim.bm;

              addNode(2,0,20,0,0,1,0);

              //if(b.anim.rules.roundtype==0) { addNode(2,0,20,0,0,1,0); }

              //else if(b.anim.rules.roundtype==1) { addNode(2,0,20,0,1,.6,0); }

              //else if(b.anim.rules.roundtype==2) { addNode(2,0,20,1,.4,0,0); }

              b.anim.ball[3].x=500; b.anim.ball[3].y=900; b.anim.ball[3].m=b.anim.bm;

              addNode(3,0,20,0,1,0,0);

              //if(b.anim.rules.roundtype==0) { addNode(3,0,20,0,1,0,0); }

              //else if(b.anim.rules.roundtype==1) { addNode(3,0,20,0,1,.4,0); }

              //else if(b.anim.rules.roundtype==2) { addNode(3,0,20,0,.6,1,0); }

         }

         else if(mode>1) {

              //Do nothing

         } else {

              if(sim_type==0) {

                  removeChildren[curnode].setValue(pieceshape[curnode]);

                  pts.writeToServer("4" + pn + "" + curtype + (100 + curnode) + (1000 + newpiecex) + (1000 + newpiecey));

                  designing = false;

              }

         }

    }

}

 

public String LoadString() {

     String shapestring;

 

     shapestring  =  "Transform {\n" +

                     "  children [\n" +

                     "      Shape {\n" +

                     "         appearance Appearance {\n";

     shapestring = shapestring +  "   material Material { diffuseColor " + b.anim.ps.GetPiecesString(0);

     shapestring = shapestring + "    }\n]}\n";

     return shapestring;

}

 

public void UpdateVRML (int f, int m, float x, float y, float z, float px, float py, float opx, float opy) {

  float[] val = new float[3];

  if(f==0) {

      val[0] = 6.0f;

      val[1] = y + py - opy;

      val[2] = z - px + opx;

      set_translation[m].setValue(val);

  }

  else if (f==1 || f==4) {

      val[0] = x + px - opx;

      val[1] = y + py - opy;

      if (m==101){

         val[2] = z;

         slant_translation.setValue(val);

      } else {

         val[2] = 0.35f;

         set_translation[m].setValue(val);

      }

      if(f==4) {

         newpiecex = (int)val[0];  //prepare for final location to send to other players

         newpiecey = (int)val[1];

      }

  }

  else if (f==2) {

      val[0] = 0.0f;

      val[1] = 0.0f;

      val[2] = -400.0f;

      slant_translation.setValue(val);

  }

  else if (f==3) {

      val[0] = x;

      val[1] = y;

      val[2] = 0.35f;

      set_translation[m].setValue(val);

  }

}

 

public void UpdatePointer (int f, float x, float y, float z, float px, float py, float opx, float opy) {

  float[] val = new float[3];

  if(f==0) {

      val[0] = 0.0f;

      val[1] = 0.0f;

      val[2] = z;

      pointer_translation.setValue(val);

  }

  else if (f==1) {

      val[0] = x + px - opx;

      val[1] = y + py - opy;

      val[2] = 30.0f;

      pointer_translation.setValue(val);

  }

}

 

public void UpdateRotation (int m, int f) {

  float[] val = new float[4];

  if (f==0) {

      val[0] = 1.0f;

      val[1] = 0.0f;

      val[2] = 0.0f;

      val[3] = -0.4f;

      slant_rotation.setValue(val);

  }

  else if (f==1) {

      val[0] = 0.0f;

      val[1] = 1.0f;

      val[2] = 0.0f;

      val[3] = 0.4f;

      slant_rotation.setValue(val);

  }

  else if (f==2) {

      val[0] = 1.0f;

      val[1] = 0.0f;

      val[2] = 0.0f;

      val[3] = 0.4f;

      slant_rotation.setValue(val);

  }

  else if (f==3) {

      val[0] = 0.0f;

      val[1] = 1.0f;

      val[2] = 0.0f;

      val[3] = -0.4f;

      slant_rotation.setValue(val);

  }

  else {

      val[0] = 1.0f;

      val[1] = 0.0f;

      val[2] = 0.0f;

      val[3] = 0.0f;

      slant_rotation.setValue(val);

  }

}

 

public void UpdateColor (int m, int i) {

  float[] val = new float[3];

  if(i==0) {

      val[0] = 1.0f;

      val[1] = 1.0f;

      val[2] = 1.0f;

  }

  else if (i==1) {

      val[0] = 1.0f;

      val[1] = 0.0f;

      val[2] = 0.0f;

  }

  else if (i==2) {

      val[0] = 0.0f;

      val[1] = 0.0f;

      val[2] = 1.0f;

  }

  else if (i==3) {

      val[0] = 0.0f;

      val[1] = 1.0f;

      val[2] = 0.0f;

  }

  else if (i==4) {

      val[0] = 1.0f;

      val[1] = 1.0f;

      val[2] = 0.0f;

  }

  else {

      val[0] = 1.0f;

      val[1] = 1.0f;

      val[2] = 1.0f;

  }

  if(m==0) { diffuseColor.setValue(val); }

}

 

public void UpdateScale (int i, float x) {

  float[] val = new float[3];

 

  val[0] = x;

  val[1] = x;

  val[2] = x;

  set_scale[i].setValue(val);

}

 

 public void Paletize(int mode) {

     String shapestring = "";

     int n=0;

     int slot=0;

 

     for(int nn=0;nn<7;nn++) {

              if(mode==1) { n=pl.l[nn]; } else if(mode==2) { n=el.l[nn]; } else { n=nn; }

              shapestring  =  "Transform {\n" +

                              "  children [\n" +

                              "      Shape {\n" +

                              "         appearance Appearance {\n";

              if(mode==1||mode==4) {

                   shapestring = shapestring +  "            material Material { diffuseColor " + b.anim.ps.GetPiecesString(n);

                          } else if(mode==2) {

                   shapestring = shapestring +  "            material Material { diffuseColor " + b.anim.es.GetEffectsString(n);

                          } else if (mode==3) {

                   shapestring = shapestring +  "            material Material { diffuseColor " + b.anim.es.GetEffectsString(b.anim.ars.ReadFromArsenal(n));

                          }

              if((mode==1||mode==4)&&n==4) {

                  shapestring = shapestring + "      }\n] rotation 0 0 1 .78 }\n";

              } else if((mode==1||mode==4)&&n==5) {

                  shapestring = shapestring + "      }\n] rotation 0 0 1 -.78 }\n";

              } else {

                  shapestring = shapestring + "      }\n]}\n";

              }

              if(mode==1&&b.anim.round==1) {

                  ReplaceNode(2,slot++,shapestring);

              } else {

                  ReplaceNode(0,slot++,shapestring);

              }

     }

 }

 

 public void addNode(int whichnode, int index, double radius, double bx, double by, double ex, double ey) {

    String shapestring = "";

 

    try {

 

      if(whichnode >= 100) { return; }

 

      shapestring  =       "Transform {\n" +

                           "  children [\n" +

                           "      Shape {\n" +

                           "         appearance Appearance {\n" +

                           "            material Material { diffuseColor ";

 

      if(index==1 || index==6) {

              shapestring = shapestring + b.anim.ps.GetPiecesString(index) +

                              "      }\n" +

                                              "] translation 0 0 15 }\n";

      }

      if(index==0) {

              shapestring  =  shapestring + " " + bx + " " + by + " " + ex + " }\n" +

                              "         }\n" +

                              "         geometry Sphere {radius " + radius + " }\n" +

                              "      }\n" +

                              "]}\n";

      }

      else if(index==4) {

             shapestring = shapestring + b.anim.ps.GetPiecesString(2) +

                              "      }\n" +

                                              "] rotation 0 0 1 .78 }\n";

      }

      else if(index==5) {

              shapestring = shapestring + b.anim.ps.GetPiecesString(2) +

                              "      }\n" +

                                              "] rotation 0 0 1 -.78 }\n";

      }

      else {

              shapestring = shapestring + b.anim.ps.GetPiecesString(index) +

                              "      }\n" +

                                              "]}\n";

      }

      ReplaceNode(1,whichnode,shapestring);

      curnode=whichnode;

    }

    catch (InvalidNodeException e) {

    }

    catch (InvalidEventInException e) {

    }

    catch (InvalidVrmlException e) {

    }

 }

 

 public void ReplaceNode(int where, int ndx, String str) {

    String shapestring = "";

 

    try {

       if(where==0) { //Palette Piece Replace

          removePI[ndx].setValue(pshape[ndx]);

          pshape[ndx] = browser.createVrmlFromString(str);

          addPI[ndx].setValue(pshape[ndx]);

       } else if(where==1) { //Add a Board Piece

          pieceshape[ndx] = browser.createVrmlFromString(str);

          addChildren[ndx].setValue(pieceshape[ndx]);

       } else if(where==2) { //Pallete Piece New

          pshape[ndx] = browser.createVrmlFromString(str);

          addPI[ndx].setValue(pshape[ndx]);

       }

    }

    catch (InvalidNodeException e) {

    }

    catch (InvalidEventInException e) {

    }

    catch (InvalidVrmlException e) {

    }

 }

 

 public void addGoal(int whichgoal, int color, double goalx, double goaly) {

    String shapestring = "";

    String colorstring = "";

 

    try {

 

      if(whichgoal >= b.anim.maxg) { return; }

 

      if(color==0) {

         colorstring = "1 1 0";

      } else if (color==1) {

         colorstring = "1 0 0";

      } else if (color==2) {

         colorstring = "0 0 1";

      } else if (color==3) {

         colorstring = "0 1 0";

      } else colorstring = "0 0 0";

 

      shapestring  =  "Transform {\n" +

                                      "  children [\n" +

                      "      Shape {\n" +

                      "         appearance Appearance {\n" +

                      "            material Material { diffuseColor " + colorstring +  " }\n" +

                      "         }\n" +

                      "         geometry Cylinder {radius 20 height 5 }\n" +

                      "      }\n" +

                                      "] translation " + goalx + " " + goaly + " 0 \n" +

                                      "  rotation 1 0 0 1.57 }\n";

 

     goalshape[whichgoal] = browser.createVrmlFromString(shapestring);

     addGoal[whichgoal].setValue(goalshape[whichgoal]);

    }

    catch (InvalidNodeException e) {

    }

    catch (InvalidEventInException e) {

    }

    catch (InvalidVrmlException e) {

    }

 }

 

 public void addSlQu(int whichslqu, int color, boolean initial) {

    String shapestring = "";

    String colorstring = "";

    String coordstring = "";

    String transstring = "";

 

    try {

 

      if(whichslqu >= 4 || whichslqu < 0) {

         return;

      } else if (whichslqu==0) {

         transstring = "-200 300 0";

      } else if (whichslqu==1) {

         transstring = "-200 400 0";

      } else if (whichslqu==2) {

         transstring = "-200 500 0";

      } else {

         transstring = "-200 600 0";

      }

 

      if (color==1) {

         colorstring = "0 1 0";

         coordstring = "point [ 0 0 25, 100 0 25, 100 100 -25, 0 100 -25 ]";

      } else if (color==2) {

         colorstring = "0 0 1";

         coordstring = "point [ 0 0 25, 100 0 -25, 100 100 -25, 0 100 25 ]";

      } else if(color==3) {

         colorstring = "1 1 0";

         coordstring = "point [ 0 0 -25, 100 0 -25, 100 100 25, 0 100 25 ]";

      } else if (color==4) {

         colorstring = "1 0 0";

         coordstring = "point [ 0 0 -25, 100 0 25, 100 100 25, 0 100 -25 ]";

      } else {

         colorstring = "0 0 0 transparency 1";

      }

 

      if(!initial) {

          removeslqu[whichslqu].setValue(qshape[whichslqu]);

      }

 

      shapestring  =  "Transform {\n" +

                                      "  children [\n" +

                      "      Shape {\n" +

                      "         appearance Appearance {\n" +

                      "            material Material { diffuseColor " + colorstring +  " }\n" +

                      "         }\n" +

                      "         geometry IndexedFaceSet {\n" +

                      "            coord Coordinate {\n" +

                      "               " + coordstring + "\n" +

                      "            }\n" +

                      "            coordIndex [ 0, 1, 2, 3 ]\n" +

                      "         }\n" +

                      "      }\n" +

                                      "  ] translation " + transstring + "\n" +

                                      "}\n";

 

     qshape[whichslqu] = browser.createVrmlFromString(shapestring);

     addslqu[whichslqu].setValue(qshape[whichslqu]);

    }

    catch (InvalidNodeException e) {

    }

    catch (InvalidEventInException e) {

    }

    catch (InvalidVrmlException e) {

    }

 }

 

 public void Bumpers(int whichmode) {

 String colorstring = "";

 

    if(pn==1){colorstring = " 1 0 0 ";}

    else if(pn==2){colorstring = " 0 0 1 ";}

    else if(pn==3){colorstring = " 0 1 0 ";}

    else if(pn==4){colorstring = " 1 1 0 ";}

 

    try {

 

      if(whichmode == 1) {   //Ball

          bumpers  =  browser.createVrmlFromString("Transform {\n" +

                                  "  children [\n" +

                                  "   Transform {\n" +

                                  "    children [\n" +

                                  "      DEF Bumper Shape {\n" +

                                  "         appearance Appearance {\n" +

                                  "              material Material { diffuseColor " + colorstring + "}\n" +

                                  "         }\n" +

                                  "         geometry Box {}\n" +

                                  "      }\n" +

                                  "    ]\n" +

                                  "    scale 30 500 30\n" +

                                  "    translation -30 500 2\n" +

                                  "   },\n" +

                                  "   Transform {\n" +

                                  "     children [\n" +

                                  "      USE Bumper\n" +

                                  "     ]\n" +

                                  "     scale 30 500 30\n" +

                                  "     translation 1030 500 2\n" +

                                  "   },\n" +

                                  "   Transform {\n" +

                                  "     children [\n" +

                                  "      USE Bumper\n" +

                                  "     ]\n" +

                                  "     scale 560 30 30\n" +

                                  "     translation 500 1030 2\n" +

                                  "   },\n" +

                                  "   Transform {\n" +

                                  "     children [\n" +

                                  "      USE Bumper\n" +

                                  "     ]\n" +

                                  "     scale 560 30 30\n" +

                                  "     translation 500 -30 2\n" +

                                  "   },\n" +

                                  "]}\n");

          addSides.setValue(bumpers);

      }

      else {

          removeSides.setValue(bumpers);

      }

    }

    catch (InvalidNodeException e) {

    }

    catch (InvalidEventInException e) {

    }

    catch (InvalidVrmlException e) {

    }

 }

 

 public void addPlayer(Player p) {

    if (nump<4) player[nump++] = p;

 }

 

 public void stop() {

   if (b!=null) b.stop();

 }

          //{{DECLARE_CONTROLS

          //}}

}

 

//-------------------------------------------------------------------

//  This class controls the main window

 

class MainWindow extends Frame {

  Animator anim;

  Ticker tick;

  Thread anim_thread;

  Thread tick_thread;

  boolean is_applet;

  Marbles i;

  MyFrame pwindow;

  Scrollbar mgrav,fric,rest;

  TextField grav;

  Label label1,label2,label3,label4,label5,label6,label7,label8,label9,inst;

  Checkbox trace,collide,mush,wrap,smooth,filled,cb1,cb2,cb3,cb4,cb5,cb6,cb7,cb8,cb9;

 

  //  This method creates layout and main objects.

 

  MainWindow(String title, boolean isapp, Marbles i_in) {

    super(title);

    is_applet = isapp;

    i = i_in;

    Panel p = new Panel();                // control panel

    Label n = new Label("Marbles 1.0");

    p.setLayout(new GridLayout(0,1)); // vertical layout

    p.setFont(new Font("Helvetica",Font.BOLD,12));

    n.setFont(new Font("TimesRoman",Font.BOLD,16));

    p.add(n);

    p.add(new Button("quit"));

    p.add(inst=new Label(""));

    p.add(grav=new TextField(40));

    grav.setText("");

    p.add(label1=new Label(""));

    p.add(cb1=new Checkbox(""));

    p.add(label2=new Label(""));

    p.add(cb2=new Checkbox(""));

    p.add(label3=new Label(""));

    p.add(cb3=new Checkbox(""));

    p.add(label4=new Label(""));

    p.add(cb4=new Checkbox(""));

    p.add(label5=new Label(""));

    p.add(cb5=new Checkbox(""));

    p.add(label6=new Label(""));

    p.add(cb6=new Checkbox(""));

    p.add(label7=new Label(""));

    p.add(cb7=new Checkbox(""));

    p.add(label8=new Label(""));

    p.add(cb8=new Checkbox(""));

    p.add(label9=new Label(""));

    p.add(cb9=new Checkbox(""));

    //Qgov();

    Qctime();

    tick = new Ticker(p); // pace maker for the animator

    anim = new Animator(tick,p,this);

    setLayout(new BorderLayout(2,2));

    add("Center",anim);

    add("West",p);

    pwindow = new MyFrame("Choose Pieces",i);

    pwindow.resize(400,480);

    pwindow.hide();

}

 

  //  This starts the threads.

 

  public void start() {

    if (anim_thread==null) {

      anim_thread = new Thread(anim);

      anim_thread.start();    // start new thread

    }

    if (tick_thread==null) {

      tick_thread = new Thread(tick);

      tick_thread.start();   // start new thread

    }

  }

 

  //  This stops the threads.

 

  public void stop() {

    if (anim_thread!=null) {

      anim_thread.stop();    // kill the thread

      anim_thread = null;     // release object

    }

    if (tick_thread!=null) {

      tick_thread.stop();      // kill the thread

      tick_thread = null;       // release object

    }

  }

 

  //  These handle user input events.

 

  public boolean action(Event e, Object arg) {

    if (e.target instanceof Button) {

      if (((String)arg).equals("quit")) {

               stop();

               hide(); // I don't know if all this is necessary.

               removeAll();

               dispose();

               if (!is_applet) System.exit(0);

      }

      else return false;

      return true;

    }

    if(cb1.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "0");

          cb1.setState(false);

    }

    else if(cb2.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "1");

          cb2.setState(false);

    }

    else if(cb3.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "2");

          cb3.setState(false);

    }

    else if(cb4.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "3");

          cb4.setState(false);

    }

    else if(cb5.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "4");

          cb5.setState(false);

    }

    else if(cb6.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "5");

          cb6.setState(false);

    }

    else if(cb7.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "6");

          cb7.setState(false);

    }

    else if(cb8.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "7");

          cb8.setState(false);

    }

    else if(cb9.getState()==true) {

          i.pts.writeToServer("1" + i.qno + "" + i.pn + "8");

          cb9.setState(false);

    }

    return false;

  }

 

  protected void Qgov() {

       labelCheckBoxes("What Government Type?","Democratic","Democratic Hi-Lo",

                       "Autocratic","","","","","","");

  }

 

  protected void Qlibrary() {

       labelCheckBoxes("Library Game or Players Rules","Library","Players",

                       "","","","","","","");

  }

 

  protected void Qchange() {

       labelCheckBoxes("Which Do You Want To Change Next Round?","Government","Objects",

                       "Events","Physics","Rules","Winning","","","");

  }

 

  protected void QnumEvents() {

       labelCheckBoxes("How Many Event Types Would You Like?","1","2",

                       "3","4","5","6","7","","");

  }

 

  protected void QnumPieces() {

       labelCheckBoxes("How Many Piece Types Would You Like?","1","2",

                       "3","4","5","6","7","","");

  }

 

  protected void QnumArsenal() {

       labelCheckBoxes("How Many Arsenal Items Would You Like?","1","2",

                       "3","4","5","6","7","","");

  }

 

  protected void Qctime() {

       labelCheckBoxes("How Much Time To Make Choices?","5 seconds","10 seconds",

                       "15 seconds","20 seconds","25 seconds","30 seconds","45 seconds",

                       "1 minute","2 minutes");

  }

 

  protected void QslantQtype() {

       labelCheckBoxes("How Should the Slant Queue Work?","Take Turns",

                       "FIFO","Random","","","","","","");

  }

 

  protected void QslantQwrap() {

       labelCheckBoxes("Should the Slant Queue Wrap to Next Round?","YES",

                       "NO","","","","","","","");

  }

 

  protected void QslantQtime() {

       labelCheckBoxes("How Often Should Slant Change?","20 frames","40 frames",

                       "60 frames","80 frames","100 frames","120 frames","150 frames",

                       "","");

  }

 

  protected void Qbumpers() {

       labelCheckBoxes("Should There Be Bumpers?","YES",

                       "NO","Change w/Turns","","","","","","");

  }

 

  protected void Qwinning() {

       labelCheckBoxes("How Compensated for Winning?","More Goals","Bigger Ball",

                       "More Points/Goal","","","","","","");

  }

 

  protected void QnextRound() {

       labelCheckBoxes("What Type of Round Next?","Competitive","Collaborative",

                       "Teams of 2","","","","","","");

  }

 

  protected void QeventQlag() {

       labelCheckBoxes("How Many Frames for Event Queue Lag?","20 frames","30 frames",

                       "40 frames","50 frames","60 frames","70 frames","","","");

  }

 

  protected void QeventDuration() {

       labelCheckBoxes("How Many Frames Is Event Active?","40 frames","60 frames",

                       "80 frames","100 frames","120 frames","150 frames","","","");

  }

 

  protected void Qgravity() {

       labelCheckBoxes("How High Gravity (1-low,9-high)?","1","2",

                       "3","4","5","6","7","8","9");

  }

 

  protected void Qrestitution() {

       labelCheckBoxes("How High Restitution (1-low,9-high)?","1","2",

                       "3","4","5","6","7","8","9");

  }

 

  protected void Qmass() {

       labelCheckBoxes("How High Mass (1-low,9-high)?","1","2",

                       "3","4","5","6","7","8","9");

  }

 

  protected void Qviscosity() {

       labelCheckBoxes("How High Viscosity (1-low,9-high)?","1","2",

                       "3","4","5","6","7","8","9");

  }

 

  protected void Qpieces() {

       for(int x=1;x<7;x++) {

           i.image[x-1]=i.getImage(i.getCodeBase(), "images/" + x + ".gif");

       }

       pwindow.label11.setText("Small Spherical Barrier");

       pwindow.label12.setText("Horizontal Stick Barrier");

       pwindow.label13.setText("Vertical Stick Barrier");

       pwindow.label14.setText("Vertical Rotated 45 Degrees");

       pwindow.label15.setText("Vertical Rotated -45 Degrees");

       pwindow.label16.setText("Variable Size Spherical Barrier");

       pwindow.label17.setText("Pendulum");

       pwindow.label18.setText("Rotating Cross");

       pwindow.label19.setText("");

       pwindow.label20.setText("");

       pwindow.show(); //.hide() will hide the window.

  }

 

  protected void Qeffects() {

       for(int x=1;x<7;x++) {

           i.image[x-1]=null;

           i.image[x-1]=i.getImage(i.getCodeBase(), "images/10" + x + ".gif");

       }

       pwindow.label11.setText("No Collisions");

       pwindow.label12.setText("Anti-Gravity");

       pwindow.label13.setText("Stop");

       pwindow.label14.setText("Grab A Goal");

       pwindow.label15.setText("Create a New Barrier");

       pwindow.label16.setText("Double Ball Radius");

       pwindow.label17.setText("");

       pwindow.label18.setText("");

       pwindow.label19.setText("");

       pwindow.label20.setText("");

       pwindow.show(); //.hide() will hide the window.

  }

 

  protected void labelCheckBoxes(String txt, String cblbl1, String cblbl2, String cblbl3,

                                 String cblbl4, String cblbl5, String cblbl6,

                                 String cblbl7, String cblbl8, String cblbl9) {

       inst.setText(txt);

       label1.setText(cblbl1);

       label2.setText(cblbl2);

       label3.setText(cblbl3);

       label4.setText(cblbl4);

       label5.setText(cblbl5);

       label6.setText(cblbl6);

       label7.setText(cblbl7);

       label8.setText(cblbl8);

       label9.setText(cblbl9);

  }

          //{{DECLARE_CONTROLS

          //}}

          //{{DECLARE_MENUS

          //}}

}

 

class MyFrame extends Frame {

   Checkbox cb11,cb12,cb13,cb14,cb15,cb16,cb17,cb18,cb19,cb20;

   Label label11,label12,label13,label14,label15,

         label16,label17,label18,label19,label20;

   MyCanvas pPictures;

   Marbles m;

   int fmode=0;

 

   MyFrame(String title, Marbles m_in) {

       super(title);

       m = m_in;

       setLayout(new GridLayout(1,3,0,0));

       Panel one=new Panel();

       Panel three=new Panel();

       add("first",one);

       pPictures = new MyCanvas(this);

       pPictures.setBackground(Color.black);

       add(pPictures);

       add("third",three);

       one.setLayout(new GridLayout(11,1,0,0));

       one.add(cb11=new Checkbox(""));

       one.add(cb12=new Checkbox(""));

       one.add(cb13=new Checkbox(""));

       one.add(cb14=new Checkbox(""));

       one.add(cb15=new Checkbox(""));

       one.add(cb16=new Checkbox(""));

       one.add(cb17=new Checkbox(""));

       one.add(cb18=new Checkbox(""));

       one.add(cb19=new Checkbox(""));

       one.add(cb20=new Checkbox(""));

       //one.add(new Button("OK"));

       three.setLayout(new GridLayout(11,1,0,0));

       three.add(label11=new Label(""));

       three.add(label12=new Label(""));

       three.add(label13=new Label(""));

       three.add(label14=new Label(""));

       three.add(label15=new Label(""));

       three.add(label16=new Label(""));

       three.add(label17=new Label(""));

       three.add(label18=new Label(""));

       three.add(label19=new Label(""));

       three.add(label20=new Label(""));

   }

 

   public boolean action(Event e, Object arg) {

     if(cb11.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "1");

       cb11.setState(false);

     }

     if(cb12.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "2");

       cb12.setState(false);

     }

     if(cb13.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "3");

       cb13.setState(false);

     }

     if(cb14.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "4");

       cb14.setState(false);

     }

     if(cb15.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "5");

       cb15.setState(false);

     }

     if(cb16.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "6");

       cb16.setState(false);

     }

     if(cb17.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "7");

       cb17.setState(false);

     }

     if(cb18.getState()==true) {

       m.pts.writeToServer("1" + m.qno + "" + m.pn + "8");

       cb18.setState(false);

     }

     return true;

   }

          //{{DECLARE_CONTROLS

          //}}

          //{{DECLARE_MENUS

          //}}

}

 

class MyCanvas extends Canvas {

   MyFrame f;

 

   MyCanvas(MyFrame f_in) {

       f=f_in;

   }

 

   public void drawPieces() {

   }

 

   public void paint(Graphics g) {

      for(int x=0;x<6;x++) {

          g.drawImage(f.m.image[x],5,(x*40)+5,this);

      }

   }

}

 

//-------------------------------------------------------------------

//  This class performs the animation in the main canvas.

 

class Animator extends Canvas implements Runnable {

  final int max = 50;

  final int maxeg = 30;

  final int maxp = 20;

  int maxg = 10;

  int num=0;            // number of balls

  int numo=0;           // number of obstacles

  int numg=0;           // number of goals

  int captg=0;          // number of captured goals

  int round=1;

  Ball[] ball = new Ball[max];          // array of balls

  Obstacle[] obstacle = new Obstacle[max];   // array of obstacles

  SlantQueue sq = new SlantQueue(60);

  EffectsQueue eq = new EffectsQueue(60);

  Effects es = new Effects(20);

  Arsenal ars = new Arsenal(7);

  Pieces ps = new Pieces(20);

  Goal[] goal = new Goal[maxeg];     // array of goals

  int goalvalue[]={100,100,100,100};  //current goal values

  int[] rs = {0,0,0,0};                     // round scores

  Physics[] phys = new Physics[maxp]; // array of physics

  Ticker tick;

  long starttime;

  boolean timeOK=true;

  Date theDate;

  long count=0;

  MainWindow mw;

  Rules rules = new Rules();            // Rules Structure

  boolean clearAll;

 

  //  The following are some "physical" properties.  Each property

  //  has a value and a control.  The values are updated once per

  //  animation loop (this is for efficiency).

 

  public double g,vg,hg,mg,f,r,bm;

  public boolean trc,col,mu,wrapon,sm;

  public int xsize,ysize,sqt,eql,eqd;

 

  Animator(Ticker t, Panel p, MainWindow m) {

    tick = t;

    mw = m;

    setBackground(Color.black);

    xsize = 1000; //width of playing board

    ysize = 1000; //height of playing board

    vg = 0.0;

    hg = 0.0;

    sqt = 100;

    eql = 50;

    eqd = 150;

    f = .50;  //effect of viscosity

    g = .25;  //effect of gravity

    r = .75;  //effect of restitution

    bm = 64;  //ball mass

    col = true;

    wrapon = true;

    loadPhysics();

  }

  //  The run method updates the locations of the balls.

  public void run() {

   while(true) {

    while (captg<numg || numg==0) {

     if(mw.i.mode>2 || mw.i.speedtest) {

       if(mw.i.speedtest && count==0) {

              addBall(new Ball(64,20,Color.blue,500,100,0.0,0.0,this,0));

              mw.i.addNode(num-1,0,20,1,1,1,0);

              addBall(new Ball(64,20,Color.blue,100,500,0.0,0.0,this,0));

              mw.i.addNode(num-1,0,20,1,1,1,0);

              addBall(new Ball(64,20,Color.blue,900,500,0.0,0.0,this,0));

              mw.i.addNode(num-1,0,20,1,1,1,0);

              addBall(new Ball(64,20,Color.blue,500,900,0.0,0.0,this,0));

              mw.i.addNode(num-1,0,20,1,1,1,0);

              vg=.33;

              theDate = new Date();

                    starttime =  theDate.getTime();

       }

       for (int i=0; i<num; ++i) {

         if(ball[i].e!=1) { //e=1 means not collidable

                   for (int j=0; j<numo; ++j) {  //look for collisions with obstacles

                       ball[i].interact(obstacle[j]);

             }

                   for (int j=i+1; j<num; ++j)  { //look for collisions with balls

                       ball[i].interact(ball[j]);

             }

         }

         if(i<4) { //ball is one of the players

                 for (int j=0; j<numg; ++j)  { //look for collisions with goals

                    if (ball[i].interact(goal[j])) {

                       if(goal[j].c > 3 || ball[i].e==4) { //ball effect=4 is goal stealing

                           mw.i.removeGoal[j].setValue(mw.i.goalshape[j]);

                           if(goal[j].c > 3) {captg++;}

                       if(rules.roundtype==0) { goal[j].c = i; }

                       else if(rules.roundtype==1) { goal[j].c=3; }

                       else if(rules.roundtype==2) {

                           if(i==0||i==3) {

                               goal[j].c=2;

                           } else { goal[j].c=1; }

                       }

                           rs[i]=rs[i]+goalvalue[i];  //value for each goal

                           mw.i.addGoal(j,goal[j].c,goal[j].x,goal[j].y);

                       }

                    }

                 }

         }

      }

      for (int j=0; j<num; ++j) {

               if(ball[j].t==0)  {

                   if(ball[j].e==3) { //e=3 is ball stoppage

                   ball[j].vx = 0.0;

                   ball[j].vy = 0.0;

                   } else {

                   ball[j].update();

               }

           }

               if(ball[j].t==1)  {

                ball[j].vx = 0.0;

                ball[j].vy = 0.0;

           }

           if(ball[j].t==2||ball[j].t==8)  {  //Pendulum

                if(count%40==0) {

                    if(ball[j].t==2){

                       for(int k=1;k<5;k++) {

                           ball[j+k].x=ball[j].x-(21.25*k);

                           ball[j+k].y=ball[j].y-(10*k);

                           ball[j+k].vx=phys[k].x[0];

                           ball[j+k].vy=phys[k].y[0];

                       }

                    } else {

                       ball[j+1].y=ball[j].y+60;

                       ball[j+2].x=ball[j].x+60;

                       ball[j+3].y=ball[j].y-60;

                       ball[j+4].x=ball[j].x-60;

                       for(int k=1;k<5;k++) {

                           ball[j+k].vx=phys[k].x[0];

                           ball[j+k].vy=phys[k].y[0];

                       }

                    }

                } else {

                    int index=0;

                    while(count%40>=phys[1].t[index]) {

                        index++;

                    }

                    for(int k=1;k<5;k++) {

                        if(ball[j].t==2){

                           ball[j+k].vx=phys[k].x[index];

                           ball[j+k].vy=phys[k].y[index];

                        } else {

                           ball[j+k].vx=phys[k+5].x[index];

                           ball[j+k].vy=phys[k+5].y[index];

                        }

                    }

                }

                for(int k=0;k<5;k++) {

                       ball[j+k].y += ball[j+k].vy;

                       ball[j+k].x += ball[j+k].vx;

                       mw.i.UpdateVRML(3,j+k,(float)ball[j+k].x,(float)ball[j+k].y,0,0,0,0,0);

                }

                j=j+4;

            }

            if(ball[j].t==3)  {  //Increasing Scale

                   ball[j].vx = 0.0;

                   ball[j].vy = 0.0;

                   ball[j].z *= 1.1;

                   mw.i.UpdateScale(j,(float)(ball[j].z/40));

                   if(ball[j].z > 40.0) {

                        ball[j].t = 4;

                   }

             }

             if(ball[j].t==4)  {  //Decreasing Scale

                   ball[j].vx = 0.0;

                   ball[j].vy = 0.0;

                   ball[j].z *= (1/1.1);

                   mw.i.UpdateScale(j,(float)(ball[j].z/40));

                   if(ball[j].z < 2.0) {

                        ball[j].t = 3;

                   }

             }

        }

      }

      DrawBalls();

      tick.poll();                 // wait for tick

      if (mw.i.mode>2 || mw.i.speedtest) {

        if (count%sqt==0) {

          int qs=sq.TakeFromSlantQueue();

          if (qs==1) {

            vg = g;

            hg = 0.0;

            mw.i.UpdateRotation(0,0);

            mw.i.UpdateVRML(1,101,0,0,100,0,0,0,0);

          }

          else if (qs==2) {

            vg = 0.0;

            hg = g;

            mw.i.UpdateRotation(0,1);

            mw.i.UpdateVRML(1,101,0,0,100,0,0,0,0);

          }

          else if (qs==3) {

            vg = -g;

            hg = 0.0;

            mw.i.UpdateRotation(0,2);

            mw.i.UpdateVRML(2,101,0,0,0,0,0,0,0);

          }

          else if (qs==4) {

            vg = 0.0;

            hg = -g;

            mw.i.UpdateRotation(0,3);

            mw.i.UpdateVRML(2,101,0,0,0,0,0,0,0);

          }

          int val=sq.InterrogateSlantQueue(0);

          if((val==-1)&&(mw.i.pn==mw.i.turncolor)&&(mw.i.mode>2)) {

              val=(int)((Math.random()*4)+1);

              mw.i.pts.writeToServer("2" + val); //can't have an empty slant queue at any point in time

          }

          mw.i.addSlQu(0,val,false); //show next queue

          val=-1;

          for(int ww=1;ww<4;ww++) { //Show upcoming slants

             val=sq.InterrogateSlantQueue(ww);

             if(val>=0 && val<=4) { mw.i.addSlQu(ww,val,false); }

             else if(val==-1) { mw.i.addSlQu(ww,0,false); }

          }

        }

        //HandleEventsQueue

        if (eq.GetEffectsQueueTime(0)<count) {

          int cureffect = eq.GetEffectsQueueEffect(0);  //cureffect is ball number plus event -- example: 101

          int curballno = cureffect/100;

          if(ball[curballno].e==6) { //e=6 is grow marble

              ball[curballno].z = ball[curballno].z/2;

              mw.i.UpdateScale(curballno,1);

          }

          ball[curballno].e = eq.TakeFromEffectsQueue()%100;

          if(ball[curballno].e==5) {  //e=5 is add an obstacle

             mw.i.curnode=50+numo;

             mw.i.removeChildren[mw.i.curnode].setValue(mw.i.pieceshape[mw.i.curnode]);

             mw.i.pts.writeToServer("4" + mw.i.pn + "" + "2" + (100 + mw.i.curnode) +

                              (1000 + (int) ball[curballno].x) + (1000 + (int) ball[curballno].y-25));

          } else if(ball[curballno].e==6) { //e=6 is make ball bigger

              ball[curballno].z = ball[curballno].z*2;

              mw.i.UpdateScale(curballno,2);

          }

        }

        count++;

        if(count%50==0 && !mw.i.speedtest) {  //coordinate speed with server

            theDate = new Date();

            mw.i.pts.writeToServer("8" + mw.i.pn + "" + (count/50 + 100) + "" + (theDate.getTime() - starttime));

            starttime=theDate.getTime();

            //timeOK=false;

        //} else if((count-40)%100==0 && !mw.i.speedtest) {  //make sure have a valid time back

        //    if(timeOK=false){ tick.speed=2000; } //someone has not reported in yet

        } else if(mw.i.speedtest && count==50) {

            theDate = new Date();

            mw.i.pts.writeToServer("8" + mw.i.pn + "100" + (theDate.getTime() - starttime));

            mw.i.speedtest=false;

            count=0;

            mw.i.UpdateColor(0,mw.i.pn);  //show player who they are

            for(int xz=0;xz<4;xz++) {

                mw.i.removeChildren[xz].setValue(mw.i.pieceshape[xz]);

                ball[xz].vx=0.0; ball[xz].vy=0.0;

            }

            vg=0.0;

        }

        mw.i.showStatus("count= " + count + " speed " + tick.speed);

        if(count==2000) { //too long to wait -- end turn

            mw.i.pts.writeToServer("98" + captg);

            numg=captg;

        }   

      }

    } //end while loop

    if(count<2000) { //write out number of frames to finish

        mw.i.pts.writeToServer("99" + count);

    }   

    ResetGame();

    round++;

   }

  }

 

  public void ResetGame() {   //HGC

      double share=0.0;

 

      //TALLY RESULTS FROM LAST ROUND

      for(int i=0;i<4;i++) {

          if(rules.roundtype==0) { mw.i.player[i].IncreasePlayerScore(rs[i]); }

          else if(rules.roundtype==1&&rules.winning==0) {

               if(numg/maxg>.8) { if(maxg<29) { maxg=maxg+2; } }

               else if(numg/maxg>.5) { if(maxg<30) { maxg++; } }

               else if(numg/maxg>.2) { if(maxg>2) { maxg=maxg-1; } }

               else { if(maxg>3) { maxg=maxg-2; } }

          }

          else if(rules.roundtype==2) {

               if(i==0||i==3) {

                  mw.i.player[i].IncreasePlayerScore((rs[0] + rs[3])/2);

               } else {

                  mw.i.player[i].IncreasePlayerScore((rs[1] + rs[2])/2);

               }

          }

      }

      if(rules.winning==1) {

         for(int i=0;i<4;i++) {

            if(rules.roundtype==0) {

               share=rs[i]/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               if(share==0) { ball[i].z=ball[i].z*.7; }

               else if(share<.15) { ball[i].z=ball[i].z*.85; }

               else if(share<.35) { } //No Change

               else if(share<.55) { ball[i].z=ball[i].z/.85; }

               else if(share<=1)  { ball[i].z=ball[i].z/.7; }

            }

            else if(rules.roundtype==1) {

               if(numg/maxg>.8) { if(maxg<29) { ball[i].z=ball[i].z/.7; } }

               else if(numg/maxg>.5) { if(maxg<30) { ball[i].z=ball[i].z/.85; } }

               else if(numg/maxg>.35) { if(maxg>2) { } }

               else if(numg/maxg>.2) { if(maxg>2) { ball[i].z=ball[i].z*.85;} }

               else { if(maxg>3) { ball[i].z=ball[i].z*.7; } }

            }

            else if(rules.roundtype==2) {

               if(i==0||i==3) {

                   share=(rs[0]+rs[3])/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               }

               else {

                   share=(rs[1]+rs[2])/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               }

               if(share==0) { ball[i].z=ball[i].z*.7; }

               else if(share<.3) { ball[i].z=ball[i].z*.85; }

               else if(share<.7) { } //No Change

               else if(share<1) { ball[i].z=ball[i].z/.85; }

               else if(share==1)  { ball[i].z=ball[i].z/.7; }

            }

         }

      }

      else if(rules.winning==2) {

         for(int i=0;i<4;i++) {

            if(rules.roundtype==0) {

               share=rs[i]/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               if(share==0) { goalvalue[i]=goalvalue[i]-16; }

               else if(share<.15) { goalvalue[i]=goalvalue[i]-8; }

               else if(share<.35) { } //No Change

               else if(share<.55) { goalvalue[i]=goalvalue[i]+8; }

               else if(share<=1)  { goalvalue[i]=goalvalue[i]+16; }

            }

            else if(rules.roundtype==1) {

               if(numg/maxg>.8) { goalvalue[i]=goalvalue[i]+16; }

               else if(numg/maxg>.5) { goalvalue[i]=goalvalue[i]+8; }

               else if(numg/maxg>.35) { }

               else if(numg/maxg>.2) { goalvalue[i]=goalvalue[i]-8; }

               else { goalvalue[i]=goalvalue[i]-16; }

            }

            else if(rules.roundtype==2) {

               if(i==0||i==3) {

                   share=(rs[0]+rs[3])/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               }

               else {

                   share=(rs[1]+rs[2])/((rs[0]+rs[1]+rs[2]+rs[3])/4);

               }

               if(share==0) { goalvalue[i]=goalvalue[i]-16; }

               else if(share<.3) { goalvalue[i]=goalvalue[i]-8; }

               else if(share<.7) { } //No Change

               else if(share<1) { goalvalue[i]=goalvalue[i]+8; }

               else if(share==1)  { goalvalue[i]=goalvalue[i]+16; }

            }

         }

      }

      mw.i.showStatus("Team Score Time:  " + count + " Captured Goals: " + captg);

      //mw.i.showStatus("Current Score:  Yellow " + mw.i.player[0].GetPlayerScore() +

      //                               " Red " + mw.i.player[1].GetPlayerScore() +

      //                               " Blue " + mw.i.player[2].GetPlayerScore() +

      //                               " Green " + mw.i.player[3].GetPlayerScore());

      for(int xx=0;xx<4;xx++) {

          rs[xx]=0;

      }

      mw.i.mode=0;

      mw.i.lastmodesent=0;

      for(int x=0;x<50;x++) { //put balls and obstacles back

          if(x>3) {

             mw.i.removeChildren[x].setValue(mw.i.pieceshape[x]);

             ball[x]=null;

          }

          mw.i.removeChildren[x+50].setValue(mw.i.pieceshape[x+50]);

          obstacle[x]=null;

      }

      for(int j=0; j<numg; ++j)  { //put goals back

          mw.i.removeGoal[j].setValue(mw.i.goalshape[j]);

          goal[j]=null;

      }

      ball[0].x = 500;

      ball[0].vx = 0.0;

      ball[0].y = 100;

      ball[0].vy = 0.0;

      mw.i.UpdateVRML(3,0,(float)ball[0].x,(float)ball[0].y,0,0,0,0,0);

      ball[1].x = 100;

      ball[1].vx = 0.0;

      ball[1].y = 500;

      ball[1].vy = 0.0;

      mw.i.UpdateVRML(3,1,(float)ball[1].x,(float)ball[1].y,0,0,0,0,0);

      ball[2].x = 900;

      ball[2].vx = 0.0;

      ball[2].y = 500;

      ball[2].vy = 0.0;

      mw.i.UpdateVRML(3,2,(float)ball[2].x,(float)ball[2].y,0,0,0,0,0);

      ball[3].x = 500;

      ball[3].vx = 0.0;

      ball[3].y = 900;

      ball[3].vy = 0.0;

      mw.i.UpdateVRML(3,3,(float)ball[3].x,(float)ball[3].y,0,0,0,0,0);

      numo=0; num=4; numg=0; captg=0;

      mw.i.UpdateRotation(0,4);

      mw.i.UpdateVRML(1,101,0,0,0,0,0,0,0);

      if(rules.slantQclear==true) { //reset slant queue if chosen

          for(int zz=0;zz<60;zz++) {

              sq.t[zz]=-1;

              sq.size=0;

              sq.head=0;

              sq.tail=0;

          }

          for(int xx=0;xx<4;xx++) {

              mw.i.addSlQu(xx,0,false);

          }

      }

      for(int yy=0;yy<60;yy++) { //reset event queue

          eq.t[yy]=-1;

          eq.e[yy]=-1;

          eq.size=0;

          eq.head=0;

          eq.tail=0;

      }

      mw.i.Paletize(4);

      mw.i.Bumpers(1);

      wrapon=false;

      mw.i.UpdatePointer(0,0,0,30,0,0,0,0);

      mw.i.pts.goalcount=0;

      //mw.i.lst.createGoals(maxg);

  }

 

  //  The DrawBalls method displays objects.

 

  public void DrawBalls() {

    int j=0;

    float x=0,y=0;

 

    for (j=0; j<num; ++j) {

      x = (float) (ball[j].x);

      y = (float) (ball[j].y);

      if(ball[j].t<1)

          mw.i.UpdateVRML(1,j,x,y,0,0,0,0,0);

      }

  }

 

  //  These adds balls

 

  public void addMoving(int m,int r,float x,float y,int t,int c0,int c1,int c2) {

      addBall(new Ball(m,r,Color.pink,x,y,0.0,0.0,this,t));

      mw.i.addNode(num-1,0,r,c0,c1,c2,0);

      x=(float)ball[num-1].x;

      y=(float)ball[num-1].y;

      addBall(new Ball(m,r,Color.pink,x,y,3.0,1.5,this,t));

      mw.i.addNode(num-1,0,r,c0,c1,c2,0);

      mw.i.UpdateVRML(1,num-1,x,y,0,0,0,0,0);

      addBall(new Ball(m,r,Color.pink,x,y,6.0,3.0,this,t));

      mw.i.addNode(num-1,0,r,c0,c1,c2,0);

      mw.i.UpdateVRML(1,num-1,x,y,0,0,0,0,0);

      addBall(new Ball(m,r,Color.pink,x,y,9.0,4.5,this,t));

      mw.i.addNode(num-1,0,r,c0,c1,c2,0);

      mw.i.UpdateVRML(1,num-1,x,y,0,0,0,0,0);

      addBall(new Ball(m,r,Color.pink,x,y,12.0,6.0,this,t));

      mw.i.addNode(num-1,0,r,c0,c1,c2,0);

      mw.i.UpdateVRML(1,num-1,x,y,0,0,0,0,0);

  }

 

  public void addBall(Ball b) {

    if (num<max) ball[num++] = b;

  }

 

  //  This adds obstacles.

 

  public void addObstacle(Obstacle o) {

    if (numo<max) obstacle[numo++] = o;

  }

 

  public void addGoal(Goal g) {

    if (numg<maxg) goal[numg++] = g;

    if(numg==1) {

        sq.AddtoSlantQueue((int)((goal[0].x%4)+1)); //start with a random slant (can't be empty)

        sq.AddtoSlantQueue((int)((goal[0].y%4)+1)); //and one random slant in queue (can't be empty)

        for(int ww=0;ww<4;ww++) { //Show upcoming slants

            int val=sq.InterrogateSlantQueue(ww);

            if(val>=0 && val<=4) { mw.i.addSlQu(ww,val,false); }

            else if(val==-1) { mw.i.addSlQu(ww,0,false); }

        }

    }

  }

 

  public void loadPhysics() {

    phys[0] = new Physics(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

                          0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[1] = new Physics(2.125,2.125,-2.125,-2.125,0.0,0.0,0.0,0.0,

                          -1.0,1.0,-1.0,1.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[2] = new Physics(4.25,4.25,-4.25,-4.25,0.0,0.0,0.0,0.0,

                          -2.0,2.0,-2.0,2.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[3] = new Physics(6.375,6.375,-6.375,-6.375,0.0,0.0,0.0,0.0,

                          -3.0,3.0,-3.0,3.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[4] = new Physics(8.5,8.5,-8.5,-8.5,0.0,0.0,0.0,0.0,

                          -4.0,4.0,-4.0,4.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[5] = new Physics(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

                          0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[6] = new Physics(6.0,-6.0,-6.0,6.0,0.0,0.0,0.0,0.0,

                          -6.0,-6.0,6.0,6.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[7] = new Physics(-6.0,-6.0,6.0,6.0,0.0,0.0,0.0,0.0,

                          -6.0,6.0,6.0,-6.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[8] = new Physics(-6.0,6.0,6.0,-6.0,0.0,0.0,0.0,0.0,

                          6.0,6.0,-6.0,-6.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

    phys[9] = new Physics(6.0,6.0,-6.0,-6.0,0.0,0.0,0.0,0.0,

                          6.0,-6.0,-6.0,6.0,0.0,0.0,0.0,0.0,

                          10,20,30,40,0,0,0,0);

  }

 

  //  This returns the index of the ball nearest to x,y

  //  excluding ex.  (wrap is not taken into account).

 

  int nearestBall(int x, int y, int ex) {

    double d=1e20,t; int j=0;

    for (int i=0; i<num; ++i) {

      t = Ball.hypot(x - ball[i].x, y - ball[i].y);

      if (t < d && i!=ex) { d = t; j = i; }

    }

    return j;

  }

 

  void rotate(int j) {

    double d,mm,dx,dy,tt;

    d = Ball.hypot(ball[j].x-ball[j-1].x,ball[j].y-ball[j-1].y);

    if (d<1e-20) return;     // too close

    mm = ball[j].m + ball[j-1].m;

    if (mm<1e-50 && mm>-1e-50) return;  // too small

    tt = Math.sqrt(20/mm/d)/d;

    dy = tt*(ball[j].x - ball[j-1].x);   // perpendicular direction vector

    dx = tt*(ball[j-1].y - ball[j].y);

 

    ball[j].vx = - dx*ball[j-1].m;

    ball[j].vy = - dy*ball[j-1].m;

  }

 

  // This adjusts the frame of reference so that the total momentum becomes zero.

 

  void zeroMomentum() {

    double mx=0,my=0,M=0;

    for (int i=0; i<num; ++i) {

      mx += ball[i].vx * ball[i].m;

      my += ball[i].vy * ball[i].m;

      M += ball[i].m;

    }

    if (M != 0)

      for (int i=0; i<num; ++i) {

          ball[i].vx -= mx/M;

          ball[i].vy -= my/M;

      }

  }

 

  //  This adjusts the centroid to the center of the canvas.

  //  Note, the "while" loops here could be simply use %= but some

  //  interpreters have bugs with %=.

 

  void centerMass() {

    double x,y,cx=0,cy=0,M=0;

    for (int i=0; i<num; ++i) {

      x = ball[i].x;  y = ball[i].y;

      if (wrapon) {    // if wrap, convert the top 1/4 to negative

          if (x > xsize*0.75) x -= xsize;

          if (y > ysize*0.75) y -= ysize;

      }

      cx += ball[i].x * ball[i].m;

      cy += ball[i].y * ball[i].m;

      M += ball[i].m;

    }

    if (M != 0)

      for (int i=0; i<num; ++i) {

          ball[i].x += xsize/2 - cx/M;

          ball[i].y += ysize/2 - cy/M;

          while (ball[i].x < 0) ball[i].x += xsize;

          while (ball[i].x > xsize) ball[i].x -= xsize;

          while (ball[i].y < 0) ball[i].y += ysize;

          while (ball[i].y > ysize) ball[i].y -= ysize;

      }

  }

}

 

//--------------------------------------------------------------------------------------------

//  The Ball class

 

class Ball {

  double x,y;                    // location

  double z;                            // radius

  double vx,vy;                                // velocity

  Color c;                              // color

  double m;                           // mass

  boolean hit;                   // scratch field

  double ox,oy;                                // old location (for smooth redraw)

  final double vmin = 1e-20;              // a weak force to prevent overlapping

  Animator a;

  boolean iok;                   // image is ok.

  Image img;                     // a bitmap to use in "filled" mode

  int t;             // type of Ball

  int e;             // current effect on the ball

 

  Ball(double mass, double radius, Color color,

       double px, double py, double sx, double sy, Animator an, int typ) {

    m=mass; z=radius-0.5; c=color; t=typ;

    if (z<0.5) z = Math.min(Math.sqrt(Math.abs(m)),Math.min(px,py));

    if (z<0.5) z=0.5;

    x=px; y=py; vx=sx; vy=sy;

    iok = false;

    a = an;

    e = 0;

    if(color==Color.white) t=1;

  }

 

  //  This updates a ball according to the physical universe.

  //  The reason I exempt a ball from gravity during a hit is

  //  to simulate "at rest" equilibrium when the ball is resting

  //  on the floor or on another ball.

 

  void UpdateBall(double dx, double dy) {

      x=dx; y=dy;

  }

 

  void AddBallEffect(int ex) {

      e=ex;

  }

 

  public void update() {

    x += vx;

    if (x+z > a.xsize)

      if (a.wrapon) {

         x -= a.xsize;

      } else {

          if (vx > 0) vx *= a.r;                              // restitution

          vx = -Math.abs(vx)-vmin;     // reverse velocity

          x = a.xsize-z;

          hit = true;

          //  Check if location is completely off screen

          if (x-z > a.xsize) x = a.xsize + z;

      }

    if (x-z < 0)

      if (a.wrapon) x += a.xsize;

      else {

          if (vx < 0) vx *= a.r;      // restitution

          vx = Math.abs(vx)+vmin;     // reverse velocity

          x = z;

          hit = true;

          if (x+z < 0) x = -z;

      }

    y += vy;

    if (y+z > a.ysize) {

      if (a.wrapon) y -= a.ysize;

      else {

        vy = -1*vy;

        y = a.ysize-z;

      }

    }

    if (y-z < 0) {

      if (a.wrapon) {

        y += a.ysize;

      }

      else {

        vy = -1*vy;

        y = z;

      }

          }

    if (a.f > 0 && m != 0) {                              // viscosity

      double t = 100/(100 + a.f*hypot(vx,vy)*z*z/m);

      vx *= t; vy *= t;

    }

    if (!hit) {

       if (e==2) {     // effect of 2 means anti-gravity

         vy -= a.vg;              // if not hit, exert anti-gravity

         vx -= a.hg;

       } else {

         vy += a.vg;             // if not hit, exert gravity

         vx += a.hg;

       }

    }

    hit = false;                   // reset flag

  }

 

  //  This computes the interaction of two balls, either collision

  //  or gravitational force.

 

  public boolean interact(Goal g) {

      double p = g.x - x;

      double q = g.y - y;

      double h2 = p*p + q*q;

      double h = Math.sqrt(h2);

      if (h < z+g.r) {                                          // HIT

              hit = true;

              return true;

      } else {

              return false;

      }

  }

 

  public boolean interact(Ball b) {

    double p = b.x - x;

    double q = b.y - y;

    if (a.wrapon) {            // wrap around, use shortest distance

      if (p > a.xsize/2) p-=a.xsize;

      else if (p < -a.xsize/2) p+=a.xsize;

      if (q > a.ysize/2) q-=a.ysize;

      else if (q < -a.ysize/2) q+=a.ysize;

    }

    double h2 = p*p + q*q;

    double h = Math.sqrt(h2);

    if (a.col) {        // collisions enabled

      if (h < z+b.z) {         // HIT

              hit = b.hit = true;

              if (h > 1e-10) {

                //  Compute the elastic collision of two balls.

 

          double v1,v2,r1,r2,s,t2,v;

            p /= h;  q /= h;                     // normalized impact direction

            v1 = vx*p + vy*q;

            v2 = b.vx*p + b.vy*q;                        // impact velocity

            r1 = vx*q - vy*p;

            r2 = b.vx*q - b.vy*p;                          // remainder velocity

            if (v1<v2) return false;

            s = m + b.m;                                        // total mass

            if (s==0) return false;

          t2 = (v1*m + v2*b.m)/s;

          if(t==0) {

              v = t2 + a.r*(v2 - v1)*b.m/s;

                    vx = v*p + r1*q;

                    vy = v*q - r1*p;

          }