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         }