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 }