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 }