001         package com.croftsoft.apps.shooter;
002    
003         import java.applet.*;
004         import java.awt.*;
005         import java.awt.event.*;
006         import java.awt.geom.*;
007         import java.util.Random;
008         import javax.swing.*;
009         import javax.swing.event.*;
010    
011         import com.croftsoft.core.animation.*;
012         import com.croftsoft.core.animation.painter.NullComponentPainter;
013         import com.croftsoft.core.animation.sprite.*;
014         import com.croftsoft.core.animation.updater.NullComponentUpdater;
015         import com.croftsoft.core.lang.NullArgumentException;
016         import com.croftsoft.core.math.MathLib;
017    
018         /*********************************************************************
019         * An IconSprite subclass representing the Attacker.
020         *
021         * @version
022         *   2003-07-11
023         * @since
024         *   2002-03-23
025         * @author
026         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
027         *********************************************************************/
028    
029         public class  ShooterSprite
030           extends AbstractSprite
031           implements MouseInputListener
032         //////////////////////////////////////////////////////////////////////
033         //////////////////////////////////////////////////////////////////////
034         {
035    
036         private static final long  IDLE_TIMEOUT_MILLIS    = 60 * 1000;
037    
038         private static final long  RECHARGE_PERIOD_MILLIS = 100;
039    
040         private static final long  BANG_PERIOD_MILLIS     = 300;
041    
042         private static final long  RECOVERY_PERIOD_MILLIS = 1000;
043    
044         //
045    
046         private static final int   STATE_REST = 0;
047    
048         private static final int   STATE_BANG = 1;
049    
050         private static final int   STATE_BOOM = 2;
051    
052         //
053    
054         private final Image            restImage;
055    
056         private final Image            bangImage;
057    
058         private final Image            boomImage;
059    
060         private final AudioClip        bangAudioClip;
061    
062         private final Rectangle        paintBounds;
063    
064         private final AffineTransform  affineTransform;
065    
066         private final Shooter          shooter;
067    
068         //
069    
070         private boolean  bang;
071    
072         private long     bangTimeMillis;
073    
074         private Point    mousePoint;
075    
076         private boolean  shooting;
077    
078         private long     lastMoveTimeMillis;
079    
080         private int      state;
081    
082         private long     boomTimeMillis;
083    
084         private Image    image;
085    
086         private Point2D  rayPoint2D;
087    
088         private long     score;
089    
090         private int      targetX;
091    
092         private int      targetY;
093    
094         //////////////////////////////////////////////////////////////////////
095         // constructor methods
096         //////////////////////////////////////////////////////////////////////
097    
098    
099         public  ShooterSprite (
100           Image      restImage,
101           Image      bangImage,
102           Image      boomImage,
103           AudioClip  bangAudioClip,
104           Shooter    shooter )
105         //////////////////////////////////////////////////////////////////////
106         {
107           super ( 0.0, 0.0, 0.0, 0.0, 0.0,
108             NullComponentUpdater.INSTANCE,
109             NullComponentPainter.INSTANCE );
110    
111           NullArgumentException.check ( this.restImage     = restImage     );
112    
113           NullArgumentException.check ( this.bangImage     = bangImage     );
114    
115           NullArgumentException.check ( this.boomImage     = boomImage     );
116    
117           NullArgumentException.check ( this.bangAudioClip = bangAudioClip );
118    
119           NullArgumentException.check ( this.shooter       = shooter       );
120    
121           paintBounds = new Rectangle ( );
122    
123           affineTransform = new AffineTransform ( );
124    
125           image = restImage;
126    
127           rayPoint2D = new Point2D.Double ( );
128         }
129    
130         //////////////////////////////////////////////////////////////////////
131         // accessor methods
132         //////////////////////////////////////////////////////////////////////
133    
134         public long  getScore ( ) { return score; }
135    
136         public Shape  getCollisionShape ( )
137         //////////////////////////////////////////////////////////////////////
138         {
139           return paintBounds;
140         }
141    
142         public void  getPaintBounds ( Rectangle  paintBounds )
143         //////////////////////////////////////////////////////////////////////
144         {
145           paintBounds.setBounds ( this.paintBounds );
146         }
147    
148         //////////////////////////////////////////////////////////////////////
149         // mutator methods
150         //////////////////////////////////////////////////////////////////////
151    
152         public void  setHit ( )
153         //////////////////////////////////////////////////////////////////////
154         {
155           image = boomImage;
156    
157           state = STATE_BOOM;
158    
159           boomTimeMillis = System.currentTimeMillis ( );
160    
161           score = 0;
162         }
163    
164         public void  resetScore ( )
165         //////////////////////////////////////////////////////////////////////
166         {
167           score = 0;
168         }
169    
170         //////////////////////////////////////////////////////////////////////
171         // interface ComponentAnimator methods
172         //////////////////////////////////////////////////////////////////////
173    
174         public void  update ( JComponent  component )
175         //////////////////////////////////////////////////////////////////////
176         {
177           long  updateTimeMillis = System.currentTimeMillis ( );
178    
179           if ( updateTimeMillis >= lastMoveTimeMillis + IDLE_TIMEOUT_MILLIS )
180           {
181             setX ( Integer.MAX_VALUE / 2 );
182    
183             paintBounds.x = Integer.MAX_VALUE / 2;
184    
185             score = 0;
186    
187             return;
188           }
189    
190           shooting = false;
191    
192           if ( bang &&
193             ( updateTimeMillis >= bangTimeMillis + RECHARGE_PERIOD_MILLIS ) )
194           {
195             shooting = true;
196    
197             bangTimeMillis = updateTimeMillis;
198    
199             bangAudioClip.play ( );
200    
201             if ( state != STATE_BOOM )
202             {
203               state = STATE_BANG;
204    
205               image = bangImage;
206             }
207    
208             shoot ( component );
209           }
210    
211           if ( state == STATE_BOOM )
212           {
213             if ( updateTimeMillis >= boomTimeMillis + RECOVERY_PERIOD_MILLIS )
214             {
215               image = restImage;
216    
217               state = STATE_REST;
218             }
219           }
220           else if ( state == STATE_BANG )
221           {
222             if ( updateTimeMillis >= bangTimeMillis + RECHARGE_PERIOD_MILLIS )
223             {
224               image = restImage;
225    
226               state = STATE_REST;
227             }
228           }
229    
230           double  x = component.getWidth  ( ) / 2;
231    
232           double  y = component.getHeight ( ) / 2;
233    
234           setX ( x );
235    
236           setY ( y );
237    
238           paintBounds.setRect ( x - 16.0, y - 16.0, 32.0, 32.0 );
239    
240           Point  mousePoint = this.mousePoint;
241    
242           if ( mousePoint != null )
243           {
244             double  deltaX = mousePoint.x - x;
245    
246             double  deltaY = mousePoint.y - y;
247    
248             setHeading ( Math.atan2 ( deltaY, deltaX ) );
249           }
250         }
251    
252         //////////////////////////////////////////////////////////////////////
253         //////////////////////////////////////////////////////////////////////
254    
255         public void  paint (
256           JComponent  component,
257           Graphics2D  graphics )
258         //////////////////////////////////////////////////////////////////////
259         {
260           double  x = getX ( );
261    
262           double  y = getY ( );
263    
264           if ( shooting )
265           {
266             graphics.setColor ( Color.RED );
267    
268             graphics.drawLine (
269               ( int ) Math.round ( getX ( ) ),
270               ( int ) Math.round ( getY ( ) ),
271               targetX,
272               targetY );
273           }
274    
275           affineTransform.setToTranslation ( x, y );
276    
277           affineTransform.rotate ( getHeading ( ) + Math.PI / 2 );
278    
279           affineTransform.translate ( -16, -16 );
280    
281           graphics.drawImage ( image, affineTransform, null );
282         }
283    
284         //////////////////////////////////////////////////////////////////////
285         //////////////////////////////////////////////////////////////////////
286    
287         public void  mouseClicked ( MouseEvent  mouseEvent )
288         //////////////////////////////////////////////////////////////////////
289         {
290         }
291    
292         public void  mouseDragged ( MouseEvent  mouseEvent )
293         //////////////////////////////////////////////////////////////////////
294         {
295           mousePoint = mouseEvent.getPoint ( );
296    
297           lastMoveTimeMillis = System.currentTimeMillis ( );
298         }
299    
300         public void  mouseEntered ( MouseEvent  mouseEvent )
301         //////////////////////////////////////////////////////////////////////
302         {
303         }
304    
305         public void  mouseExited ( MouseEvent  mouseEvent )
306         //////////////////////////////////////////////////////////////////////
307         {
308         }
309    
310         public void  mouseMoved ( MouseEvent  mouseEvent )
311         //////////////////////////////////////////////////////////////////////
312         {
313           mousePoint = mouseEvent.getPoint ( );
314    
315           lastMoveTimeMillis = System.currentTimeMillis ( );
316         }
317    
318         public void  mousePressed ( MouseEvent  mouseEvent )
319         //////////////////////////////////////////////////////////////////////
320         {
321           bang = true;
322    
323           lastMoveTimeMillis = System.currentTimeMillis ( );
324         }
325    
326         public void  mouseReleased ( MouseEvent  mouseEvent )
327         //////////////////////////////////////////////////////////////////////
328         {
329           bang = false;
330    
331           lastMoveTimeMillis = System.currentTimeMillis ( );
332         }
333    
334         //////////////////////////////////////////////////////////////////////
335         //////////////////////////////////////////////////////////////////////
336    
337         private void  shoot ( JComponent  component )
338         //////////////////////////////////////////////////////////////////////
339         {
340           double  heading = getHeading ( );
341    
342           double  x1 = getX ( );
343    
344           double  y1 = getY ( );
345    
346           int  width  = component.getWidth  ( );
347    
348           int  height = component.getHeight ( );
349    
350           int  rayLength = width > height ? width : height;
351    
352           MathLib.toRectangular ( rayLength, heading, rayPoint2D );
353    
354           double  x2 = x1 + rayPoint2D.getX ( );
355    
356           double  y2 = y1 + rayPoint2D.getY ( );
357    
358           int  index = -1;
359    
360           AttackerSprite [ ]  attackerSprites
361             = shooter.getAttackerSprites ( );
362    
363           for ( int  i = 0; i < attackerSprites.length; i++ )
364           {
365             AttackerSprite  attackerSprite = attackerSprites [ i ];
366    
367             if ( attackerSprite.intersectsLine ( x1, y1, x2, y2 ) )
368             {
369               index = i;
370    
371               if ( !attackerSprite.isHit ( ) )
372               {
373                 attackerSprite.setHit ( );
374       
375                 score++;
376               }
377    
378               targetX = ( int ) Math.round ( attackerSprite.getX ( ) );
379    
380               targetY = ( int ) Math.round ( attackerSprite.getY ( ) );
381    
382               break;
383             }
384           }
385    
386           if ( index < 0 )
387           {
388             targetX = ( int ) Math.round ( x2 );
389    
390             targetY = ( int ) Math.round ( y2 );
391           }
392         }
393    
394         //////////////////////////////////////////////////////////////////////
395         //////////////////////////////////////////////////////////////////////
396         }