001 package breakout; 002 003 import java.awt.*; 004 import java.awt.geom.*; 005 import java.awt.event.*; 006 import java.util.*; 007 import javax.swing.JComponent; 008 009 import cs101.awt.geom.*; 010 011 /** 012 * WorldState implements the World interface, providing an 013 * augmented container for BreakoutComponents. It also serves 014 * as a synchronization point for the program. In order for it 015 * to behave properly, it must be passed a JComponent GUI object 016 * before any components are added. It currently uses 017 * a dumb component intersection and rebound implementation. 018 * 019 * @see breakout.World 020 * 021 * @author benmv@olin.edu 022 * @version <tt>$id$</tt> 023 */ 024 public class WorldState implements World { 025 JComponent gui; 026 Vector elements; 027 Vector listeners; 028 boolean playing; 029 int ballsLeft; 030 031 public final Wall NORTH,SOUTH,EAST,WEST; 032 033 /** 034 * Builds a WorldState object with only the components that 035 * make up the walls. The South wall is a KillerWall. If 036 * the networked version is requested, the North wall is a 037 * TransferWall. 038 * 039 * @param networked specifies whether the worldstate will 040 * be networked to another world. 041 * @see breakout.Wall 042 * @see breakout.KillerWall 043 * @see breakout.TransferWall 044 */ 045 public WorldState(boolean networked) { 046 this.playing = false; 047 this.ballsLeft = 3; 048 049 this.elements = new Vector(); 050 this.listeners = new Vector(); 051 if (networked) 052 this.NORTH = new TransferWall(new Point(0,-5), 053 new Dimension(World.MAXX-1,5), 054 this); 055 else 056 this.NORTH = new Wall(new Point(0,-5), 057 new Dimension(World.MAXX-1,5), 058 this); 059 this.SOUTH = new KillerWall(new Point(0,World.MAXY-1), 060 new Dimension(World.MAXX,5), 061 this); 062 this.EAST = new Wall(new Point(World.MAXX-1,0), 063 new Dimension(5,World.MAXY), 064 this); 065 this.WEST = new Wall(new Point(-5,0), 066 new Dimension(5,World.MAXY), 067 this); 068 add(this.NORTH); 069 add(this.SOUTH); 070 add(this.EAST); 071 add(this.WEST); 072 } 073 074 /** 075 * Assumes the component checking for intersections is 076 * rectangular. 077 * 078 * @see breakout.World 079 */ 080 public BreakoutComponent intersects(BreakoutComponent bc) { 081 // Shape myShape= bc.getShape(); 082 Rectangle2D myShape = bc.getShape().getBounds2D(); 083 synchronized (elements) { 084 for (int i=0; i < elements.size(); i++) { 085 BreakoutComponent other = (BreakoutComponent)elements.elementAt(i); 086 if (other != bc && !other.isDead() && 087 other.getShape().intersects(myShape)) 088 // ShapeUtils.isOverlapping(other.getShape(),myShape)) 089 return other; 090 } 091 } 092 093 return null; 094 } 095 096 /** 097 * Assumes that two active components rebounding are equal 098 * mass and non active components have infinite mass. 099 */ 100 public void rebound(BreakoutComponent bc1, BreakoutComponent bc2) { 101 // try to get an ABC into bc1 102 if (bc2 instanceof ActiveBreakoutComponent) { 103 BreakoutComponent temp = bc1; 104 bc1 = bc2; 105 bc2 = temp; 106 } 107 // both ABCs? 108 if (bc1 instanceof ActiveBreakoutComponent && 109 bc2 instanceof ActiveBreakoutComponent) { 110 111 ActiveBreakoutComponent abc1 = (ActiveBreakoutComponent)bc1; 112 ActiveBreakoutComponent abc2 = (ActiveBreakoutComponent)bc2; 113 114 Point c1 = abc1.getCenter(); 115 Point c2 = abc2.getCenter(); 116 117 double xdist = Math.abs(c1.x-c2.x); 118 double ydist = Math.abs(c1.y-c2.y); 119 120 Point dir1 = abc1.getDirection(); 121 Point dir2 = abc2.getDirection(); 122 123 int temp; 124 if (xdist < ydist) { 125 temp = dir2.y; 126 dir2.y = dir1.y; 127 dir1.y = temp; 128 } else { 129 temp = dir2.x; 130 dir2.x = dir1.x; 131 dir1.x = temp; 132 } 133 abc1.setDirection(dir1); 134 abc2.setDirection(dir2); 135 } else if (bc1 instanceof ActiveBreakoutComponent) { 136 137 ActiveBreakoutComponent abc = (ActiveBreakoutComponent)bc1; 138 /* 139 Shape movingShape = bc1.getShape(); 140 Shape stationaryShape = bc2.getShape(); 141 142 Point dir = abc.getDirection(); 143 144 System.out.println("x:"+dir.x); 145 System.out.println("y:"+dir.y); 146 double newXVel = ShapeUtils.doXReflect(movingShape,stationaryShape,dir.x,dir.y); 147 double newYVel = ShapeUtils.doYReflect(movingShape,stationaryShape,dir.x,dir.y); 148 149 System.out.println(newXVel); 150 System.out.println(newYVel); 151 152 Point newDir = new Point(); 153 newDir.setLocation(newXVel,newYVel); 154 155 System.out.println("new x:"+newDir.x); 156 System.out.println("new y:"+newDir.y); 157 abc.setDirection(newDir); 158 */ 159 Point c1 = abc.getCenter(); 160 Point c2 = bc2.getCenter(); 161 162 double xdist=Math.abs(c1.x-c2.x); 163 double ydist=Math.abs(c1.y-c2.y); 164 165 166 Dimension size = bc2.getSize(); 167 Point dir = abc.getDirection(); 168 169 if (xdist/ydist < size.getWidth()/size.getHeight()) { 170 dir.y = -dir.y; 171 } else { 172 dir.x = -dir.x; 173 } 174 abc.setDirection(dir); 175 } // both not ABCs.. nothing to do 176 } 177 178 /** 179 * adds the component to the world and sends a WorldEvent 180 * if the component added was a brick. 181 * 182 * @param bc BreakoutComponent to add 183 */ 184 public void add(BreakoutComponent bc) { 185 bc.setWorld(this); 186 elements.add(bc); 187 if (bc instanceof Brick) { 188 fireBrickAdded((Brick)bc); 189 } 190 } 191 192 /** 193 * adds the component to the world using the add method and 194 * then registers the component with the GUI as a MouseListener 195 * and MouseMotionListener. 196 * 197 * @param bc BreakoutComponent to add to World and mouse listeners. 198 * @throws ClassCastException if the BreakoutComponent does not 199 * also implement MouseListener and MouseMotionListener. 200 */ 201 public void addMouseAware(BreakoutComponent bc) { 202 add(bc); 203 this.gui.addMouseListener((MouseListener)bc); 204 this.gui.addMouseMotionListener((MouseMotionListener)bc); 205 } 206 207 /** 208 * Returns an iterator that supports the remove operation. 209 * Remember to synchronize on this before using. 210 */ 211 public Iterator iterator() { 212 final Iterator iter = elements.iterator(); 213 return new Iterator() { 214 Object last; 215 public Object next() { return last=iter.next();} 216 public boolean hasNext() { return iter.hasNext(); } 217 public void remove() { 218 iter.remove(); 219 if (last instanceof Brick) { 220 fireBrickRemoved((Brick)last); 221 } 222 } 223 }; 224 } 225 226 public void removeAt(Point loc) { 227 for (int i=0; i < elements.size(); i++) { 228 BreakoutComponent bc = (BreakoutComponent)elements.elementAt(i); 229 if (bc.getLocation().equals(loc)) { 230 elements.removeElementAt(i); 231 if (bc instanceof Brick) 232 fireBrickRemoved((Brick)bc); 233 return; 234 } 235 } 236 } 237 238 public void ballLeft(Ball b) { 239 b.kill(); 240 fireBallLeft(b); 241 } 242 243 public void killAll() { 244 synchronized (this) { 245 for (int i=0; i < elements.size(); i++) { 246 BreakoutComponent bc = (BreakoutComponent)elements.elementAt(i); 247 bc.kill(); 248 if (bc instanceof Brick) { 249 fireBrickRemoved((Brick)bc); 250 } 251 } 252 } 253 MouseListener[] mlisten = (MouseListener[])this.gui.getListeners(MouseListener.class); 254 for (int i=0; i < mlisten.length; i++) { 255 this.gui.removeMouseListener(mlisten[i]); 256 } 257 MouseMotionListener[] mmlisten = (MouseMotionListener[])this.gui.getListeners(MouseMotionListener.class); 258 for (int i=0; i < mmlisten.length; i++) { 259 this.gui.removeMouseMotionListener(mmlisten[i]); 260 } 261 try { 262 Thread.sleep(500); 263 } catch (InterruptedException e) { 264 // nothing to do 265 } 266 this.playing = false; 267 this.ballsLeft = 3; 268 this.elements = new Vector(); 269 add(this.NORTH); 270 add(this.SOUTH); 271 add(this.EAST); 272 add(this.WEST); 273 } 274 275 public void startPlaying() { 276 this.playing = true; 277 if (this.ballsLeft == 0) 278 return; 279 else 280 this.ballsLeft--; 281 Point bloc = new Point(World.MAXX/2,World.MAXY-20); 282 Dimension bsize = new Dimension(10,10); 283 Point dir = new Point((int)((Math.random()-0.5)*200), 284 -(int)(Math.random()*100)); 285 add(new SimpleBall(bloc,bsize,this,dir,Ball.DEFAULT_SPEED,Color.red)); 286 } 287 288 public boolean playing() { 289 return this.playing; 290 } 291 292 public int getBallsLeft() { 293 return this.ballsLeft; 294 } 295 296 public void addWorldListener(WorldListener wl) { 297 this.listeners.add(wl); 298 } 299 300 void setGui(JComponent gui) { 301 this.gui = gui; 302 } 303 304 private void fireBrickAdded(Brick bc) { 305 if (!(bc instanceof Brick)) return; 306 WorldEvent event = WorldEvent.createEvent(WorldEvent.ADD_BRICK,bc); 307 Iterator i = this.listeners.iterator(); 308 while (i.hasNext()) { 309 ((WorldListener)i.next()).componentAdded(event); 310 } 311 } 312 313 private void fireBrickRemoved(Brick bc) { 314 WorldEvent event = WorldEvent.createEvent(WorldEvent.REMOVE_BRICK,bc); 315 Iterator it = this.listeners.iterator(); 316 while (it.hasNext()) { 317 ((WorldListener)it.next()).componentRemoved(event); 318 } 319 } 320 321 private void fireBallLeft(Ball bc) { 322 WorldEvent event = WorldEvent.createEvent(WorldEvent.BALL_TRANSFER,bc); 323 Iterator it = this.listeners.iterator(); 324 while (it.hasNext()) { 325 ((WorldListener)it.next()).ballLeft(event); 326 } 327 } 328 } 329