001 package com.croftsoft.apps.dodger; 002 003 import java.applet.*; 004 import java.awt.*; 005 import java.awt.event.*; 006 import java.awt.geom.*; 007 import java.io.*; 008 import java.net.URL; 009 import java.util.*; 010 import javax.swing.*; 011 import javax.swing.event.*; 012 013 import com.croftsoft.core.CroftSoftConstants; 014 import com.croftsoft.core.animation.AnimatedComponent; 015 import com.croftsoft.core.animation.ComponentAnimator; 016 import com.croftsoft.core.animation.Sprite; 017 import com.croftsoft.core.animation.animator.TileAnimator; 018 import com.croftsoft.core.animation.clock.SystemClock; 019 import com.croftsoft.core.animation.sprite.IconSprite; 020 import com.croftsoft.core.animation.sprite.TextSprite; 021 import com.croftsoft.core.applet.AppletLib; 022 import com.croftsoft.core.awt.image.ImageLib; 023 import com.croftsoft.core.gui.LifecycleWindowListener; 024 import com.croftsoft.core.io.SerializableLib; 025 import com.croftsoft.core.jnlp.JnlpLib; 026 import com.croftsoft.core.lang.ClassLib; 027 import com.croftsoft.core.lang.lifecycle.Lifecycle; 028 029 /********************************************************************* 030 * Main Dodger class. 031 * 032 * @version 033 * 2003-09-29 034 * @since 035 * 2002-03-18 036 * @author 037 * <a href="https://www.croftsoft.com/">David Wallace Croft</a> 038 *********************************************************************/ 039 040 public final class Dodger 041 extends JApplet 042 implements ComponentAnimator, Lifecycle 043 ////////////////////////////////////////////////////////////////////// 044 ////////////////////////////////////////////////////////////////////// 045 { 046 047 ////////////////////////////////////////////////////////////////////// 048 // Applet constants 049 ////////////////////////////////////////////////////////////////////// 050 051 private static final String VERSION 052 = "2003-09-29"; 053 054 private static final String TITLE 055 = "CroftSoft Dodger"; 056 057 private static final String INFO 058 = "\n" + TITLE + "\n" 059 + CroftSoftConstants.COPYRIGHT + "\n" 060 + CroftSoftConstants.HOME_PAGE + "\n" 061 + "Version " + VERSION + "\n"; 062 063 ////////////////////////////////////////////////////////////////////// 064 // Frame constants 065 ////////////////////////////////////////////////////////////////////// 066 067 private static final String FRAME_TITLE 068 = TITLE; 069 070 private static final String FRAME_ICON_FILENAME 071 = "/images/croftsoft.png"; 072 073 private static final Dimension FRAME_SIZE 074 = null; 075 076 private static final String SHUTDOWN_CONFIRMATION_PROMPT 077 = "Close " + TITLE + "?"; 078 079 ////////////////////////////////////////////////////////////////////// 080 // animation constants 081 ////////////////////////////////////////////////////////////////////// 082 083 /** frames per second */ 084 private static final double FRAME_RATE = 60.0; 085 086 /** pixels per second */ 087 private static final double FLOW_RATE = 100.0; 088 089 private static final int PIXELS_PER_FRAME 090 = ( int ) Math.round ( FLOW_RATE / FRAME_RATE ); 091 092 private static final int MAX_OBSTACLES = 200; 093 094 private static final double OBSTACLE_DENSITY 095 = 1 / ( 5.0 * 32 * 32 ); 096 097 private static final String MEDIA_DIR = "media/dodger/"; 098 099 private static final String DODGER_GREEN_IMAGE_FILENAME 100 = MEDIA_DIR + "dodger_green.png"; 101 102 private static final String DODGER_YELLOW_IMAGE_FILENAME 103 = MEDIA_DIR + "dodger_yellow.png"; 104 105 private static final String DODGER_RED_IMAGE_FILENAME 106 = MEDIA_DIR + "dodger_red.png"; 107 108 private static final String DODGER_BANG_IMAGE_FILENAME 109 = MEDIA_DIR + "dodger_bang.png"; 110 111 private static final String DODGER_BOOM_IMAGE_FILENAME 112 = MEDIA_DIR + "dodger_boom.png"; 113 114 private static final String BANG_AUDIO_FILENAME 115 = MEDIA_DIR + "bang.wav"; 116 117 private static final String OBSTACLE_EXPLODE_AUDIO_FILENAME 118 = MEDIA_DIR + "explode.wav"; 119 120 private static final Color BACKGROUND_COLOR 121 = Color.WHITE; 122 123 ////////////////////////////////////////////////////////////////////// 124 // persistence constants 125 ////////////////////////////////////////////////////////////////////// 126 127 private static final String FILE_CONTENTS_SPEC = "DodgerData"; 128 129 private static final String LATEST_DATA_FILENAME 130 = ".croftsoft" 131 + File.separator + "dodger" 132 + File.separator + "dodger01.dat"; 133 134 private static final String BACKUP_DATA_FILENAME 135 = ".croftsoft" 136 + File.separator + "dodger" 137 + File.separator + "dodger02.dat"; 138 139 private static final String PERSISTENCE_KEY = FILE_CONTENTS_SPEC; 140 141 ////////////////////////////////////////////////////////////////////// 142 // instance variables 143 ////////////////////////////////////////////////////////////////////// 144 145 private AnimatedComponent animatedComponent; 146 147 private Rectangle bounds; 148 149 private DodgerSprite dodgerSprite; 150 151 private ObstacleSprite [ ] obstacleSprites; 152 153 private Rectangle dodgerBounds; 154 155 private long highScore; 156 157 private TextSprite scoreTextSprite; 158 159 private Random random; 160 161 private AudioClip obstacleExplodeAudioClip; 162 163 ////////////////////////////////////////////////////////////////////// 164 ////////////////////////////////////////////////////////////////////// 165 166 public static void main ( String [ ] args ) 167 ////////////////////////////////////////////////////////////////////// 168 { 169 JFrame jFrame = new JFrame ( FRAME_TITLE ); 170 171 try 172 { 173 jFrame.setIconImage ( ClassLib.getResourceAsImage ( 174 Dodger.class, FRAME_ICON_FILENAME ) ); 175 } 176 catch ( Exception ex ) 177 { 178 } 179 180 Dodger dodger = new Dodger ( ); 181 182 jFrame.setContentPane ( dodger ); 183 184 LifecycleWindowListener.launchFrameAsDesktopApp ( 185 jFrame, 186 new Lifecycle [ ] { dodger }, 187 FRAME_SIZE, 188 SHUTDOWN_CONFIRMATION_PROMPT ); 189 } 190 191 ////////////////////////////////////////////////////////////////////// 192 ////////////////////////////////////////////////////////////////////// 193 194 public String getAppletInfo ( ) 195 ////////////////////////////////////////////////////////////////////// 196 { 197 return INFO; 198 } 199 200 ////////////////////////////////////////////////////////////////////// 201 // interface Lifecycle methods 202 ////////////////////////////////////////////////////////////////////// 203 204 public void init ( ) 205 ////////////////////////////////////////////////////////////////////// 206 { 207 System.out.println ( INFO ); 208 209 animatedComponent = new AnimatedComponent ( this, FRAME_RATE ); 210 211 animatedComponent.addComponentListener ( 212 new ComponentAdapter ( ) 213 { 214 public void componentResized ( ComponentEvent componentEvent ) 215 { 216 resetBounds ( ); 217 } 218 } ); 219 220 Container contentPane = getContentPane ( ); 221 222 contentPane.setLayout ( new BorderLayout ( ) ); 223 224 contentPane.add ( animatedComponent, BorderLayout.CENTER ); 225 226 animatedComponent.setCursor ( 227 new Cursor ( Cursor.CROSSHAIR_CURSOR ) ); 228 229 animatedComponent.init ( ); 230 231 bounds = animatedComponent.getBounds ( ); 232 233 try 234 { 235 Icon dodgerGreenIcon = new ImageIcon ( loadAutomaticImage ( 236 DODGER_GREEN_IMAGE_FILENAME, Transparency.BITMASK ) ); 237 238 Icon dodgerYellowIcon = new ImageIcon ( loadAutomaticImage ( 239 DODGER_YELLOW_IMAGE_FILENAME, Transparency.BITMASK ) ); 240 241 Icon dodgerRedIcon = new ImageIcon ( loadAutomaticImage ( 242 DODGER_RED_IMAGE_FILENAME, Transparency.BITMASK ) ); 243 244 Icon dodgerBangIcon = new ImageIcon ( loadAutomaticImage ( 245 DODGER_BANG_IMAGE_FILENAME, Transparency.BITMASK ) ); 246 247 Icon dodgerBoomIcon = new ImageIcon ( loadAutomaticImage ( 248 DODGER_BOOM_IMAGE_FILENAME, Transparency.BITMASK ) ); 249 250 URL bangAudioURL = getClass ( ).getClassLoader ( ) 251 .getResource ( BANG_AUDIO_FILENAME ); 252 253 AudioClip bangAudioClip = Applet.newAudioClip ( bangAudioURL ); 254 255 dodgerSprite = new DodgerSprite ( 256 dodgerGreenIcon, 257 dodgerYellowIcon, 258 dodgerRedIcon, 259 dodgerBangIcon, 260 dodgerBoomIcon, 261 bangAudioClip, 262 bounds ); 263 264 animatedComponent.addMouseListener ( dodgerSprite ); 265 266 animatedComponent.addMouseMotionListener ( dodgerSprite ); 267 268 animatedComponent.addKeyListener ( dodgerSprite ); 269 } 270 catch ( IOException ex ) 271 { 272 ex.printStackTrace ( ); 273 } 274 275 animatedComponent.requestFocus ( ); 276 277 // obstacles 278 279 obstacleSprites = new ObstacleSprite [ 0 ]; 280 281 obstacleExplodeAudioClip = Applet.newAudioClip ( 282 getClass ( ).getClassLoader ( ).getResource ( 283 OBSTACLE_EXPLODE_AUDIO_FILENAME ) ); 284 285 random = new Random ( ); 286 287 dodgerBounds = new Rectangle ( ); 288 289 scoreTextSprite = new TextSprite ( TITLE ); 290 291 scoreTextSprite.setX ( 20 ); 292 293 scoreTextSprite.setY ( 20 ); 294 295 scoreTextSprite.setColor ( Color.RED ); 296 297 try 298 { 299 DodgerData dodgerData = ( DodgerData ) SerializableLib.load ( 300 LATEST_DATA_FILENAME, 301 BACKUP_DATA_FILENAME, 302 FILE_CONTENTS_SPEC, 303 ( Applet ) this, 304 PERSISTENCE_KEY, 305 ( ClassLoader ) null, 306 ( String ) null ); 307 308 if ( dodgerData != null ) 309 { 310 highScore = dodgerData.highScore; 311 } 312 } 313 catch ( Exception ex ) 314 { 315 ex.printStackTrace ( ); 316 } 317 } 318 319 public void start ( ) { animatedComponent.start ( ); } 320 321 public void stop ( ) { animatedComponent.stop ( ); } 322 323 public void destroy ( ) 324 ////////////////////////////////////////////////////////////////////// 325 { 326 try 327 { 328 SerializableLib.save ( 329 new DodgerData ( highScore ), 330 LATEST_DATA_FILENAME, 331 BACKUP_DATA_FILENAME, 332 FILE_CONTENTS_SPEC, 333 ( Applet ) this, 334 PERSISTENCE_KEY ); 335 } 336 catch ( Exception ex ) 337 { 338 ex.printStackTrace ( ); 339 } 340 341 animatedComponent.destroy ( ); 342 } 343 344 ////////////////////////////////////////////////////////////////////// 345 // interface ComponentAnimator methods 346 ////////////////////////////////////////////////////////////////////// 347 348 public void update ( JComponent component ) 349 ////////////////////////////////////////////////////////////////////// 350 { 351 ObstacleSprite [ ] obstacleSprites = this.obstacleSprites; 352 353 for ( int i = 0; i < obstacleSprites.length; i++ ) 354 { 355 obstacleSprites [ i ].update ( component ); 356 } 357 358 Shape dodgerCollisionShape = dodgerSprite.getCollisionShape ( ); 359 360 if ( dodgerCollisionShape != null ) 361 { 362 Rectangle2D dodgerCollisionRectangle2D 363 = dodgerCollisionShape.getBounds2D ( ); 364 365 for ( int i = 0; i < obstacleSprites.length; i++ ) 366 { 367 Shape obstacleCollisionShape 368 = obstacleSprites [ i ].getCollisionShape ( ); 369 370 if ( obstacleCollisionShape != null ) 371 { 372 if ( obstacleCollisionShape.intersects ( 373 dodgerCollisionRectangle2D ) ) 374 { 375 dodgerSprite.setHit ( ); 376 377 obstacleSprites [ i ].setHit ( ); 378 379 break; 380 } 381 } 382 } 383 } 384 385 dodgerSprite.update ( component ); 386 387 if ( dodgerSprite.isShooting ( ) ) 388 { 389 shoot ( ); 390 } 391 392 long score = dodgerSprite.getScore ( ); 393 394 highScore = score > highScore ? score : highScore; 395 396 scoreTextSprite.setText ( 397 "High Score: " + highScore + " Score: " + score ); 398 399 component.repaint ( ); 400 } 401 402 public void paint ( 403 JComponent component, 404 Graphics2D graphics ) 405 ////////////////////////////////////////////////////////////////////// 406 { 407 graphics.setColor ( BACKGROUND_COLOR ); 408 409 graphics.fillRect ( 0, 0, bounds.width, bounds.height ); 410 411 ObstacleSprite [ ] obstacleSprites = this.obstacleSprites; 412 413 for ( int i = 0; i < obstacleSprites.length; i++ ) 414 { 415 obstacleSprites [ i ].paint ( component, graphics ); 416 } 417 418 dodgerSprite.paint ( component, graphics ); 419 420 scoreTextSprite.paint ( component, graphics ); 421 } 422 423 ////////////////////////////////////////////////////////////////////// 424 ////////////////////////////////////////////////////////////////////// 425 426 private Image loadAutomaticImage ( 427 String imageFilename, 428 int transparency ) 429 throws IOException 430 ////////////////////////////////////////////////////////////////////// 431 { 432 return ImageLib.loadAutomaticImage ( 433 imageFilename, 434 transparency, 435 animatedComponent, 436 getClass ( ).getClassLoader ( ), 437 null ); 438 } 439 440 private void shoot ( ) 441 ////////////////////////////////////////////////////////////////////// 442 { 443 Rectangle2D shootArea = dodgerSprite.getShootArea ( ); 444 445 int index = -1; 446 447 ObstacleSprite [ ] obstacleSprites = this.obstacleSprites; 448 449 for ( int i = 0; i < obstacleSprites.length; i++ ) 450 { 451 Sprite obstacleSprite = obstacleSprites [ i ]; 452 453 Shape obstacleCollisionShape 454 = obstacleSprite.getCollisionShape ( ); 455 456 if ( obstacleCollisionShape != null ) 457 { 458 if ( obstacleCollisionShape.intersects ( shootArea ) ) 459 { 460 if ( ( index < 0 ) 461 || ( obstacleSprites [ index ].getY ( ) 462 < obstacleSprite.getY ( ) ) ) 463 { 464 index = i; 465 } 466 } 467 } 468 } 469 470 if ( index > -1 ) 471 { 472 obstacleSprites [ index ].setHit ( ); 473 } 474 } 475 476 private void resetBounds ( ) 477 ////////////////////////////////////////////////////////////////////// 478 { 479 dodgerSprite.resetScore ( ); 480 481 animatedComponent.getBounds ( bounds ); 482 483 int obstacleCount = ( int ) Math.round ( 484 OBSTACLE_DENSITY * bounds.width * bounds.height ); 485 486 obstacleSprites = new ObstacleSprite [ obstacleCount ]; 487 488 for ( int i = 0; i < obstacleSprites.length; i++ ) 489 { 490 ObstacleSprite obstacleSprite = new ObstacleSprite ( 491 obstacleExplodeAudioClip, 492 random, 493 bounds, 494 PIXELS_PER_FRAME ); 495 496 obstacleSprite.setY ( Double.POSITIVE_INFINITY ); 497 498 obstacleSprites [ i ] = obstacleSprite; 499 } 500 } 501 502 ////////////////////////////////////////////////////////////////////// 503 ////////////////////////////////////////////////////////////////////// 504 }