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.lang.NullArgumentException;
012         import com.croftsoft.core.animation.*;
013         import com.croftsoft.core.animation.painter.NullComponentPainter;
014         import com.croftsoft.core.animation.sprite.*;
015         import com.croftsoft.core.animation.updater.NullComponentUpdater;
016    
017         /*********************************************************************
018         * An IconSprite subclass representing a Shooter attacker.
019         *
020         * @version
021         *   2003-07-11
022         * @since
023         *   2002-03-23
024         * @author
025         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
026         *********************************************************************/
027    
028         public class  AttackerSprite
029           extends AbstractSprite
030         //////////////////////////////////////////////////////////////////////
031         //////////////////////////////////////////////////////////////////////
032         {
033    
034         private static final int   ATTACKER_WIDTH        = 32;
035    
036         private static final int   ATTACKER_HEIGHT       = 32;
037    
038         private static final int   ATTACKER_HALF_WIDTH  = ATTACKER_WIDTH  / 2;
039    
040         private static final int   ATTACKER_HALF_HEIGHT = ATTACKER_HEIGHT / 2;
041    
042         private static final long  EXPLODE_PERIOD_MILLIS = 1000;
043    
044         //
045    
046         private final AudioClip  explodeAudioClip;
047    
048         private final Random     random;
049    
050         private final Rectangle  paintBounds;
051    
052         //
053    
054         private Color    color;
055    
056         private long     explodeTimeMillis;
057    
058         private boolean  hit;
059    
060         private boolean  exploding;
061    
062         private int      componentHeight;
063    
064         private int      componentWidth;
065    
066         private double   averageVelocity;
067    
068         //////////////////////////////////////////////////////////////////////
069         // constructor methods
070         //////////////////////////////////////////////////////////////////////
071    
072    
073         public  AttackerSprite (
074           AudioClip  explodeAudioClip,
075           Random     random,
076           double     averageVelocity )
077         //////////////////////////////////////////////////////////////////////
078         {
079           super ( 0.0, 0.0, 0.0, 0.0, 0.0,
080             NullComponentUpdater.INSTANCE,
081             NullComponentPainter.INSTANCE );
082    
083           NullArgumentException.check (
084             this.explodeAudioClip = explodeAudioClip );
085    
086           NullArgumentException.check ( this.random = random );
087    
088           paintBounds = new Rectangle (
089             0, 0, ATTACKER_WIDTH, ATTACKER_HEIGHT );       
090    
091           setY ( Double.POSITIVE_INFINITY );
092    
093           color = new Color (
094             random.nextInt ( 256 ),
095             random.nextInt ( 256 ),
096             random.nextInt ( 256 ) );
097    
098           this.averageVelocity = averageVelocity;
099         }
100    
101         //////////////////////////////////////////////////////////////////////
102         // accessor methods
103         //////////////////////////////////////////////////////////////////////
104    
105         public void  getPaintBounds ( Rectangle  paintBounds )
106         //////////////////////////////////////////////////////////////////////
107         {
108           paintBounds.setBounds ( this.paintBounds );
109         }
110    
111         public Shape  getCollisionShape ( )
112         //////////////////////////////////////////////////////////////////////
113         {
114           if ( exploding )
115           {
116             return null;
117           }
118    
119           return paintBounds;
120         }
121    
122         public boolean  intersectsLine (
123           double  x1,
124           double  y1,
125           double  x2,
126           double  y2 )
127         //////////////////////////////////////////////////////////////////////
128         {
129           return paintBounds.intersectsLine ( x1, y1, x2, y2 );
130         }
131    
132         public boolean  isHit ( ) { return hit; }
133    
134         //////////////////////////////////////////////////////////////////////
135         // mutator methods
136         //////////////////////////////////////////////////////////////////////
137    
138         public void  setX ( double  x )
139         //////////////////////////////////////////////////////////////////////
140         {
141           super.setX ( x );
142    
143           paintBounds.x = ( int ) Math.round ( x ) - ATTACKER_HALF_HEIGHT;
144         }
145    
146         public void  setY ( double  y )
147         //////////////////////////////////////////////////////////////////////
148         {
149           super.setY ( y );
150    
151           paintBounds.y = ( int ) Math.round ( y ) - ATTACKER_HALF_WIDTH;
152         }
153    
154         public void  setHit ( )
155         //////////////////////////////////////////////////////////////////////
156         {
157           hit = true;
158         }
159    
160         public void  reset ( )
161         //////////////////////////////////////////////////////////////////////
162         {
163           hit = false;
164    
165           exploding = false;
166    
167           double  centerX = componentWidth  / 2.0;
168    
169           double  centerY = componentHeight / 2.0;
170    
171           double  radius = centerX > centerY ? centerX : centerY;
172    
173           double  angle = 2.0 * Math.PI * random.nextDouble ( );
174    
175           double  x = centerX + radius * Math.cos ( angle );
176    
177           double  y = centerY + radius * Math.sin ( angle );
178    
179           setX ( x );
180    
181           setY ( y );
182    
183           setHeading ( Math.atan2 ( ( centerY - y ), ( centerX - x ) ) );
184    
185           setVelocity ( ( 0.5 + random.nextDouble ( ) ) * averageVelocity );
186    
187           color = new Color (
188             random.nextInt ( 256 ),
189             random.nextInt ( 256 ),
190             random.nextInt ( 256 ) );
191         }
192    
193         //////////////////////////////////////////////////////////////////////
194         // interface ComponentAnimator methods
195         //////////////////////////////////////////////////////////////////////
196    
197         public void  update ( JComponent  component )
198         //////////////////////////////////////////////////////////////////////
199         {
200           long  updateTimeMillis = System.currentTimeMillis ( );
201    
202           double  x = getX ( );
203    
204           double  y = getY ( );
205    
206           double  velocity = getVelocity ( );
207    
208           double  heading  = getHeading  ( );
209    
210           x += ( velocity * Math.cos ( heading ) );
211    
212           y += ( velocity * Math.sin ( heading ) );
213    
214           componentWidth  = component.getWidth  ( );
215    
216           componentHeight = component.getHeight ( );
217    
218           if ( ( y >=  2.0 * componentHeight )
219             || ( y <= -1.0 * componentHeight )
220             || ( x >=  2.0 * componentWidth  )
221             || ( x <= -1.0 * componentWidth  ) )
222           {
223             reset ( );
224           }
225           else
226           {
227             setX ( x );
228    
229             setY ( y );
230           }
231    
232           if ( exploding )
233           {
234             if ( updateTimeMillis
235               >= explodeTimeMillis + EXPLODE_PERIOD_MILLIS )
236             {
237               reset ( );
238             }
239    
240             color = new Color (
241               random.nextInt ( 256 ),
242               random.nextInt ( 256 ),
243               random.nextInt ( 256 ) );
244           }
245           else if ( hit )
246           {
247             explodeAudioClip.play ( );
248    
249             color = new Color (
250               random.nextInt ( 256 ),
251               random.nextInt ( 256 ),
252               random.nextInt ( 256 ) );
253    
254             explodeTimeMillis = updateTimeMillis;
255             
256             exploding = true;
257           }
258         }
259    
260         public void  paint (
261           JComponent  component,
262           Graphics2D  graphics )
263         //////////////////////////////////////////////////////////////////////
264         {
265           graphics.setColor ( color );
266    
267           graphics.fillRect (
268             paintBounds.x,
269             paintBounds.y,
270             ATTACKER_WIDTH,
271             ATTACKER_HEIGHT );
272    
273           graphics.setColor ( Color.WHITE );
274    
275           graphics.drawRect (
276             paintBounds.x,
277             paintBounds.y,
278             ATTACKER_WIDTH,
279             ATTACKER_HEIGHT );
280         }
281    
282         //////////////////////////////////////////////////////////////////////
283         //////////////////////////////////////////////////////////////////////
284         }