1 /***************************************************************************
2 alienBlaster
3 Copyright (C) 2004
4 Paul Grathwohl, Arne Hormann, Daniel Kuehn, Soenke Schwardt
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ***************************************************************************/
20 using namespace std;
21
22 #include <iostream>
23 #include "shot.h"
24 #include "surfaceDB.h"
25 #include "racers.h"
26 #include "racer.h"
27 #include "explosions.h"
28 #include "explosion.h"
29 #include "enemys.h"
30 #include "enemy.h"
31 #include "smokePuffs.h"
32 #include "boundingBox.h"
33
Shot(ShotTypes shotType,int playerNr,Vector2D position,float angle)34 Shot::Shot( ShotTypes shotType, int playerNr, Vector2D position, float angle ) {
35
36 this->shotType = shotType;
37 fromWhichPlayer = playerNr;
38
39 pos = position;
40 collidedWithGround = false;
41 deflectedBySonicFromPlayer1 = false;
42 deflectedBySonicFromPlayer2 = false;
43
44 generatesSmokePuffs = false;
45 timeToNextSmokePuff = 100;
46
47 switch (shotType) {
48 // primary shots
49 case SHOT_NORMAL:
50 {
51 vel = Vector2D( VEL_SHOT_NORMAL, angle, POLAR);
52 timeToLive = LIFETIME_SHOT_NORMAL;
53 sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
54 break;
55 }
56 case SHOT_NORMAL_HEAVY:
57 {
58 vel = Vector2D( VEL_SHOT_NORMAL_HEAVY, angle, POLAR);
59 timeToLive = LIFETIME_SHOT_NORMAL_HEAVY;
60 sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL_HEAVY );
61 break;
62 }
63 case SHOT_DOUBLE:
64 {
65 vel = Vector2D(VEL_SHOT_DOUBLE, angle, POLAR);
66 timeToLive = LIFETIME_SHOT_DOUBLE;
67 sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE );
68 break;
69 }
70 case SHOT_DOUBLE_HEAVY:
71 {
72 vel = Vector2D(VEL_SHOT_DOUBLE_HEAVY, angle, POLAR);
73 timeToLive = LIFETIME_SHOT_DOUBLE_HEAVY;
74 sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE_HEAVY );
75 break;
76 }
77 case SHOT_TRIPLE:
78 {
79 vel = Vector2D(VEL_SHOT_TRIPLE, angle, POLAR);
80 timeToLive = LIFETIME_SHOT_TRIPLE;
81 sprite = surfaceDB.loadSurface( FN_SHOT_TRIPLE );
82 break;
83 }
84
85 // primary shots heavy fighter
86 case SHOT_HF_NORMAL:
87 {
88 vel = Vector2D( VEL_SHOT_HF_NORMAL, angle, POLAR);
89 timeToLive = LIFETIME_SHOT_HF_NORMAL;
90 sprite = surfaceDB.loadSurface( FN_SHOT_HF_NORMAL );
91 break;
92 }
93 case SHOT_HF_DOUBLE:
94 {
95 vel = Vector2D( VEL_SHOT_HF_DOUBLE, angle, POLAR);
96 timeToLive = LIFETIME_SHOT_HF_DOUBLE;
97 sprite = surfaceDB.loadSurface( FN_SHOT_HF_DOUBLE );
98 break;
99 }
100 case SHOT_HF_TRIPLE:
101 {
102 vel = Vector2D(VEL_SHOT_HF_TRIPLE, angle, POLAR);
103 timeToLive = LIFETIME_SHOT_HF_TRIPLE;
104 sprite = surfaceDB.loadSurface( FN_SHOT_HF_TRIPLE );
105 break;
106 }
107 case SHOT_HF_QUATTRO:
108 {
109 vel = Vector2D(VEL_SHOT_HF_QUATTRO, angle, POLAR);
110 timeToLive = LIFETIME_SHOT_HF_QUATTRO;
111 sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUATTRO );
112 break;
113 }
114 case SHOT_HF_QUINTO:
115 {
116 vel = Vector2D(VEL_SHOT_HF_QUINTO, angle, POLAR);
117 timeToLive = LIFETIME_SHOT_HF_QUINTO;
118 sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUINTO );
119 break;
120 }
121
122 // secondary shots
123 case SHOT_DUMBFIRE:
124 {
125 vel = Vector2D(VEL_SHOT_DUMBFIRE, angle, POLAR);
126 timeToLive = LIFETIME_SHOT_DUMBFIRE;
127 sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE );
128 generatesSmokePuffs = true;
129 break;
130 }
131 case SHOT_DUMBFIRE_DOUBLE:
132 {
133 vel = Vector2D(VEL_SHOT_DUMBFIRE_DOUBLE, angle, POLAR);
134 timeToLive = LIFETIME_SHOT_DUMBFIRE_DOUBLE;
135 sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE_DOUBLE );
136 generatesSmokePuffs = true;
137 break;
138 }
139 case SHOT_KICK_ASS_ROCKET:
140 {
141 vel = Vector2D(VEL_SHOT_KICK_ASS_ROCKET, angle, POLAR);
142 timeToLive = LIFETIME_SHOT_KICK_ASS_ROCKET;
143 sprite = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET );
144 spriteShadow = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET_SHADOW, true );
145 break;
146 }
147 case SHOT_HELLFIRE:
148 {
149 vel = Vector2D(VEL_SHOT_HELLFIRE / 2.0, angle, POLAR);
150 timeToLive = LIFETIME_SHOT_HELLFIRE;
151 sprite = surfaceDB.loadSurface( FN_SHOT_HELLFIRE );
152 spriteShadow = surfaceDB.loadSurface( FN_SHOT_HELLFIRE_SHADOW, true );
153 generatesSmokePuffs = true;
154 break;
155 }
156 case SHOT_MACHINE_GUN:
157 {
158 vel = Vector2D(VEL_SHOT_MACHINE_GUN, angle, POLAR);
159 timeToLive = LIFETIME_SHOT_MACHINE_GUN;
160 sprite = surfaceDB.loadSurface( FN_SHOT_MACHINE_GUN );
161 break;
162 }
163 case SHOT_ENERGY_BEAM:
164 {
165 vel = Vector2D(VEL_SHOT_ENERGY_BEAM, angle, POLAR);
166 timeToLive = LIFETIME_SHOT_ENERY_BEAM;
167 sprite = surfaceDB.loadSurface( FN_SHOT_ENERGY_BEAM, true );
168 break;
169 }
170
171 // secondary shots heavy fighter
172 case SHOT_HF_DUMBFIRE:
173 {
174 vel = Vector2D(VEL_SHOT_HF_DUMBFIRE, angle, POLAR);
175 timeToLive = LIFETIME_SHOT_HF_DUMBFIRE;
176 sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE );
177 generatesSmokePuffs = true;
178 break;
179 }
180 case SHOT_HF_DUMBFIRE_DOUBLE:
181 {
182 vel = Vector2D(VEL_SHOT_HF_DUMBFIRE_DOUBLE, angle, POLAR);
183 timeToLive = LIFETIME_SHOT_HF_DUMBFIRE_DOUBLE;
184 sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE_DOUBLE );
185 generatesSmokePuffs = true;
186 break;
187 }
188 case SHOT_HF_KICK_ASS_ROCKET:
189 {
190 vel = Vector2D(VEL_SHOT_HF_KICK_ASS_ROCKET, angle, POLAR);
191 timeToLive = LIFETIME_SHOT_HF_KICK_ASS_ROCKET;
192 sprite = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET );
193 spriteShadow = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET_SHADOW, true );
194 break;
195 }
196 case SHOT_HF_LASER:
197 {
198 vel = Vector2D(VEL_SHOT_HF_LASER, angle, POLAR);
199 timeToLive = LIFETIME_SHOT_HF_LASER;
200 sprite = surfaceDB.loadSurface( FN_SHOT_HF_LASER );
201 break;
202 }
203
204 // special shots
205 case SPECIAL_SHOT_HEATSEEKER:
206 {
207 vel = Vector2D(VEL_SPECIAL_SHOT_HEATSEEKER, angle, POLAR);
208 timeToLive = LIFETIME_SPECIAL_SHOT_HEATSEEKER;
209 sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_HEATSEEKER );
210 generatesSmokePuffs = true;
211 break;
212 }
213 case SPECIAL_SHOT_NUKE:
214 {
215 vel = Vector2D( VEL_SPECIAL_SHOT_NUKE, angle, POLAR );
216 timeToLive = LIFETIME_SPECIAL_SHOT_NUKE;
217 sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE );
218 spriteShadow = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE_SHADOW, true );
219 break;
220 }
221
222 // enemy shots
223 case ENEMY_SHOT_NORMAL:
224 {
225 vel = Vector2D(VEL_ENEMY_SHOT_NORMAL, angle, POLAR);
226 timeToLive = LIFETIME_ENEMY_SHOT_NORMAL;
227 sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_NORMAL );
228 break;
229 }
230 case ENEMY_SHOT_TANK_ROCKET:
231 {
232 vel = Vector2D(VEL_ENEMY_SHOT_TANK_ROCKET, angle, POLAR);
233 timeToLive = LIFETIME_ENEMY_SHOT_TANK_ROCKET;
234 sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET );
235 spriteShadow = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET_SHADOW, true );
236 generatesSmokePuffs = true;
237 break;
238 }
239
240 default:
241 {
242 vel = Vector2D(0,0);
243 timeToLive = 0;
244 sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
245 break;
246 }
247 }
248
249 }
250
251
moveAndCollide(int dT)252 void Shot::moveAndCollide( int dT ) {
253
254 if ( fromWhichPlayer == 666 ) {
255 moveAndCollideEnemyShot( dT );
256 } else {
257 moveAndCollidePlayerShot( dT );
258 }
259
260 generateSmokePuff( dT );
261
262 timeToLive -= dT;
263 }
264
265
moveAndCollidePlayerShot(int dT)266 void Shot::moveAndCollidePlayerShot( int dT ) {
267
268 Vector2D posOld = pos;
269
270 // move the shot
271 movePlayerShot( dT );
272
273 if ( !isExpired() && collidePlayerShot( posOld ) ) {
274 addExplosion();
275 }
276 }
277
278
movePlayerShot(int dT)279 void Shot::movePlayerShot( int dT ) {
280 switch (shotType) {
281 case SHOT_NORMAL:
282 case SHOT_NORMAL_HEAVY:
283 case SHOT_DOUBLE:
284 case SHOT_DOUBLE_HEAVY:
285 case SHOT_TRIPLE:
286 case SHOT_HF_NORMAL:
287 case SHOT_HF_DOUBLE:
288 case SHOT_HF_TRIPLE:
289 case SHOT_HF_QUATTRO:
290 case SHOT_HF_QUINTO:
291 case SHOT_DUMBFIRE:
292 case SHOT_DUMBFIRE_DOUBLE:
293 case SHOT_KICK_ASS_ROCKET:
294 case SHOT_MACHINE_GUN:
295 case SHOT_ENERGY_BEAM:
296 case SHOT_HF_DUMBFIRE:
297 case SHOT_HF_DUMBFIRE_DOUBLE:
298 case SHOT_HF_KICK_ASS_ROCKET:
299 case SHOT_HF_LASER:
300 {
301 pos = pos + vel * dT / 1000.0;
302 break;
303 }
304 case SHOT_HELLFIRE:
305 {
306 pos = pos + vel * dT / 1000.0;
307 if ( timeToLive < LIFETIME_SHOT_HELLFIRE - 100 ) {
308 vel = Vector2D( 0, -VEL_SHOT_HELLFIRE );
309 }
310 break;
311 }
312 case SPECIAL_SHOT_HEATSEEKER:
313 {
314 pos = pos + vel * dT / 1000.0;
315 if ( timeToLive >= LIFETIME_SPECIAL_SHOT_HEATSEEKER - 200 ) break;
316 int idxNearestEnemy = 0;
317 float distNearest = 10000;
318 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
319 // vector from the shotPosition to the enemy
320 Vector2D v = enemys->getEnemy(i)->getPos() - pos;
321 if ( getAbsAngleDifference( vel.getDirection(), v.getDirection() ) < 180 ) {
322 float dist = (enemys->getEnemy(i)->getPos() - pos).getLength();
323 if ( dist < distNearest ) {
324 distNearest = dist;
325 idxNearestEnemy = i;
326 }
327 }
328 }
329 if ( distNearest != 10000 ) {
330 float angle =
331 getAngleDifference( (enemys->getEnemy(idxNearestEnemy)->getPos()-pos).getDirection(),
332 vel.getDirection() );
333 if ( fabs(angle) < 80 * dT / 1000.0 ) {
334 vel.rotate(angle);
335 } else if ( angle < 0 ) {
336 vel.rotate( -80 * dT / 1000.0 );
337 } else {
338 vel.rotate( 80 * dT / 1000.0 );
339 }
340 }
341
342 break;
343 }
344
345 case SPECIAL_SHOT_NUKE:
346 {
347 pos = pos + vel * dT / 1000.0;
348 // Nuke is in its place!
349 if ( (pos - Vector2D( SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 )).getLength()
350 <= (vel * dT / 1000.0).getLength() ) {
351 nukeIsInPlace = true;
352 }
353 break;
354 }
355
356 default:
357 {
358 cout << "movePlayerShot: unexpected shotType: " << shotType << endl;
359 break;
360 }
361 }
362
363 // clip at the outside of the window
364 if ( !RectangleGeo( Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ),
365 Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER,
366 SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
367 timeToLive = 0;
368 }
369 }
370
371
collidePlayerShot(Vector2D posOld)372 bool Shot::collidePlayerShot( Vector2D posOld ) {
373 switch (shotType) {
374 // only against air
375 case SHOT_ENERGY_BEAM:
376 {
377 BoundingBox box( lroundf(posOld.getX()) - sprite->w / 2,
378 lroundf(pos.getY()) - sprite->w / 2,
379 sprite->w,
380 lroundf((posOld-pos).getY()) + sprite->h );
381 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
382 if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
383 enemys->getEnemy(i)->collidesWith( &box ) ) {
384 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
385 timeToLive = 0;
386 collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
387 return true;
388 }
389 }
390 break;
391 }
392 //only against air
393 case SHOT_HF_LASER:
394 {
395 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
396 if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
397 enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
398 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
399 timeToLive = 0;
400 collidedWithGround = false;
401 return true;
402 }
403 }
404 break;
405 }
406 // against air and ground
407 case SHOT_NORMAL:
408 case SHOT_NORMAL_HEAVY:
409 case SHOT_DOUBLE:
410 case SHOT_DOUBLE_HEAVY:
411 case SHOT_TRIPLE:
412 case SHOT_HF_NORMAL:
413 case SHOT_HF_DOUBLE:
414 case SHOT_HF_TRIPLE:
415 case SHOT_HF_QUATTRO:
416 case SHOT_HF_QUINTO:
417 {
418 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
419 if ( //ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
420 enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
421 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
422 timeToLive = 0;
423 collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
424 return true;
425 }
426 }
427 break;
428 }
429 // against air and ground
430 case SHOT_DUMBFIRE:
431 case SHOT_DUMBFIRE_DOUBLE:
432 case SHOT_HF_DUMBFIRE:
433 case SHOT_HF_DUMBFIRE_DOUBLE:
434 {
435 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
436 if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
437 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
438 timeToLive = 0;
439 collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
440 return true;
441 }
442 }
443 break;
444 }
445 // only against ground
446 case SHOT_KICK_ASS_ROCKET:
447 case SHOT_HF_KICK_ASS_ROCKET:
448 {
449 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
450 if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) &&
451 enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
452 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
453 timeToLive = 0;
454 collidedWithGround = true;
455 return true;
456 }
457 }
458 break;
459 }
460 // only against ground, but has to hit more exactly than kickAssRocket
461 case SHOT_HELLFIRE:
462 {
463 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
464 if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) &&
465 enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
466 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
467 timeToLive = 0;
468 collidedWithGround = true;
469 return true;
470 }
471 }
472 break;
473 }
474 // against air and ground
475 case SHOT_MACHINE_GUN:
476 {
477 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
478 if ( enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
479 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
480 timeToLive = 0;
481 collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
482 return true;
483 }
484 }
485 break;
486 }
487
488 // against air and ground
489 case SPECIAL_SHOT_HEATSEEKER:
490 {
491 for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
492 if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
493 enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
494 timeToLive = 0;
495 collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
496 return true;
497 }
498 }
499 break;
500 }
501 case SPECIAL_SHOT_NUKE: break;
502
503 default:
504 {
505 cout << "collidePlayerShot: unexpected shotType: " << shotType << endl;
506 return false;
507 }
508 }
509 return false;
510 }
511
512 ///////////////////////////
513
moveAndCollideEnemyShot(int dT)514 void Shot::moveAndCollideEnemyShot( int dT ) {
515 Vector2D posOld = pos;
516
517 moveEnemyShot( dT );
518
519 if ( !isExpired() && collideEnemyShot( posOld ) ) {
520 addExplosion();
521 }
522 }
523
524
moveEnemyShot(int dT)525 void Shot::moveEnemyShot( int dT ) {
526 switch (shotType) {
527 case ENEMY_SHOT_NORMAL: {
528 // is this shot near the deflector of a racer?
529 for ( unsigned int i = 0; i < racers->getNrRacers(); i++) {
530 if ( racers->getRacer(i)->getShipType() == HEAVY_FIGHTER ) {
531 Vector2D racerToShot = pos - racers->getRacer(i)->getPos();
532 if ( racerToShot.getLength() < RACER_DEFLECTOR_ACTIVATION_DIST ) {
533 vel += Vector2D( RACER_DEFLECTOR_POWER * (dT / 1000.0),
534 racerToShot.getDirection(), POLAR );
535 }
536 if ( racers->getRacer(i)->isDeflectorSpecialActive() ) {
537 if ( racerToShot.getLength() < ITEM_DEFLECTOR_ACTIVATION_DIST ) {
538 vel += Vector2D( ITEM_DEFLECTOR_POWER * (dT / 1000.0),
539 racerToShot.getDirection(), POLAR );
540 }
541 }
542 }
543 }
544 pos = pos + vel * dT / 1000.0;
545 break;
546 }
547 case ENEMY_SHOT_TANK_ROCKET: {
548 if ( deflectedBySonicFromPlayer1 ) {
549 Vector2D racerToShot = pos - racers->getRacer(0)->getPos();
550 if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
551 vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0),
552 racerToShot.getDirection(), POLAR );
553 deflectedBySonicFromPlayer1 = false;
554 }
555 }
556 if ( deflectedBySonicFromPlayer2 ) {
557 Vector2D racerToShot = pos - racers->getRacer(1)->getPos();
558 if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
559 vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0),
560 racerToShot.getDirection(), POLAR );
561 deflectedBySonicFromPlayer2 = false;
562 }
563 }
564 pos = pos + vel * dT / 1000.0;
565 break;
566 }
567 default: {
568 cout << "moveEnemyShot: unexpected shotType: " << shotType << endl;
569 break;
570 }
571 }
572
573 // clip at the outside of the window
574 if ( !RectangleGeo(Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ),
575 Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER,
576 SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
577 timeToLive = 0;
578 }
579 }
580
581
collideEnemyShot(Vector2D posOld)582 bool Shot::collideEnemyShot( Vector2D posOld ) {
583 switch (shotType) {
584 case ENEMY_SHOT_NORMAL:
585 {
586 for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
587 if ( racers->getRacer(i)->collidesWith( posOld, pos ) ) {
588 racers->getRacer(i)->doDamage( shotType );
589 timeToLive = 0;
590 collidedWithGround = false;
591 return true;
592 }
593 }
594 break;
595 }
596 case ENEMY_SHOT_TANK_ROCKET:
597 {
598 for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
599 if ( racers->getRacer(i)->collidesWith( Circle(pos, 5) ) ) {
600 racers->getRacer(i)->doDamage( shotType );
601 timeToLive = 0;
602 collidedWithGround = false;
603 return true;
604 }
605 }
606 break;
607 }
608 default:
609 {
610 cout << "collideEnemyShot: unexpected shotType: " << shotType << endl;
611 return false;
612 }
613 }
614 return false;
615 }
616
617 /////////////
618
addExplosion()619 void Shot::addExplosion() {
620 Explosion *explosion;
621 switch (shotType) {
622 default:
623 {
624 if ( collidedWithGround ) {
625 explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
626 vel / 10.0, EXPLOSION_NORMAL_GROUND );
627 } else {
628 if ( shotType == SHOT_HF_LASER ) {
629 // Laser is too fast...
630 explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
631 vel / 30.0, EXPLOSION_NORMAL_AIR );
632 } else {
633 explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
634 vel / 10.0, EXPLOSION_NORMAL_AIR );
635 }
636 }
637 break;
638 }
639 }
640 explosions->addExplosion( explosion );
641 }
642
643 ////////////////////
644
drawShadow(SDL_Surface * screen)645 void Shot::drawShadow(SDL_Surface *screen) {
646 switch (shotType) {
647 case SHOT_KICK_ASS_ROCKET:
648 case SHOT_HF_KICK_ASS_ROCKET:
649 case SHOT_HELLFIRE:
650 case SPECIAL_SHOT_NUKE:
651 {
652 SDL_Rect shadowR;
653 shadowR.x = lroundf(pos.getX()) - spriteShadow->w / 2 - 7;
654 shadowR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 7;
655 shadowR.w = spriteShadow->w;
656 shadowR.h = spriteShadow->h;
657 SDL_BlitSurface( spriteShadow, 0, screen, &shadowR );
658 break;
659 }
660 case ENEMY_SHOT_TANK_ROCKET:
661 {
662 SDL_Rect destR;
663 SDL_Rect srcR;
664 destR.x = lroundf(pos.getX()) - spriteShadow->w / 16 - 10;
665 destR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 10;
666 destR.w = spriteShadow->w / 8;
667 destR.h = spriteShadow->h;
668 float angle = vel.getDirection() + 202.5;
669 int idx = lroundf(angle) % 360;
670 idx = idx / 45;
671 srcR.x = idx * spriteShadow->w / 8;
672 srcR.y = 0;
673 srcR.w = spriteShadow->w / 8;
674 srcR.h = spriteShadow->h;
675
676 SDL_BlitSurface( spriteShadow, &srcR, screen, &destR );
677 break;
678 }
679 default: break;
680 }
681 }
682
drawGroundShot(SDL_Surface * screen)683 void Shot::drawGroundShot(SDL_Surface *screen) {
684 switch (shotType) {
685 case SHOT_KICK_ASS_ROCKET:
686 case SHOT_HF_KICK_ASS_ROCKET:
687 case SHOT_HELLFIRE:
688 {
689 SDL_Rect destR;
690 SDL_Rect srcR;
691 destR.x = lroundf(pos.getX()) - sprite->w / 2;
692 destR.y = lroundf(pos.getY()) - sprite->h / 2;
693 destR.w = sprite->w;
694 destR.h = sprite->h;
695 srcR.x = 0;
696 srcR.y = 0;
697 srcR.w = sprite->w;
698 srcR.h = sprite->h;
699
700 SDL_BlitSurface( sprite, &srcR, screen, &destR );
701 break;
702 }
703 default: break;
704 }
705 }
706
drawGroundAirShot(SDL_Surface * screen)707 void Shot::drawGroundAirShot(SDL_Surface *screen) {
708 switch (shotType) {
709 case SHOT_DUMBFIRE:
710 case SHOT_DUMBFIRE_DOUBLE:
711 case SHOT_HF_DUMBFIRE:
712 case SHOT_HF_DUMBFIRE_DOUBLE:
713 {
714 SDL_Rect destR;
715 SDL_Rect srcR;
716 destR.x = lroundf(pos.getX()) - sprite->w / 16;
717 destR.y = lroundf(pos.getY()) - sprite->h / 2;
718 destR.w = sprite->w / 8;
719 destR.h = sprite->h;
720 // TODO: eight directions are outdated for dumbfire, but existent in the image
721 float angle = vel.getDirection() + 202.5;
722 int idx = lroundf(angle) % 360;
723 idx = idx / 45;
724 srcR.x = idx * 8;
725 srcR.y = 0;
726 srcR.w = 8;
727 srcR.h = 8;
728
729 SDL_BlitSurface( sprite, &srcR, screen, &destR );
730 break;
731 }
732 case SHOT_MACHINE_GUN:
733 case SHOT_HF_LASER:
734 {
735 SDL_Rect destR;
736 destR.x = lroundf(pos.getX()) - sprite->w / 2;
737 destR.y = lroundf(pos.getY()) - sprite->h / 2;
738 destR.w = sprite->w;
739 destR.h = sprite->h;
740 SDL_BlitSurface( sprite, 0, screen, &destR );
741 break;
742 }
743 case SHOT_ENERGY_BEAM:
744 {
745 SDL_Rect destR;
746 destR.x = lroundf(pos.getX()) - sprite->w / 2;
747 destR.y = lroundf(pos.getY()) - sprite->h / 2;
748 destR.w = sprite->w;
749 destR.h = sprite->h;
750 SDL_BlitSurface( sprite, 0, screen, &destR );
751 destR.x = lroundf(pos.getX()) - sprite->w / 2;
752 destR.y = lroundf(pos.getY()) - sprite->h / 2;
753 SDL_BlitSurface( sprite, 0, screen, &destR );
754 break;
755 }
756 case SPECIAL_SHOT_HEATSEEKER:
757 {
758 SDL_Rect destR;
759 SDL_Rect srcR;
760 destR.x = lroundf(pos.getX()) - sprite->w / 16;
761 destR.y = lroundf(pos.getY()) - sprite->h / 2;
762 destR.w = sprite->w / 8;
763 destR.h = sprite->h;
764
765 float angle = vel.getDirection() + 202.5;
766 int idx = lroundf(angle) % 360;
767 idx = idx / 45;
768 srcR.x = idx * 8;
769 srcR.y = 0;
770 srcR.w = 8;
771 srcR.h = 8;
772
773 SDL_BlitSurface( sprite, &srcR, screen, &destR );
774 break;
775 }
776 case ENEMY_SHOT_TANK_ROCKET:
777 {
778 SDL_Rect destR;
779 SDL_Rect srcR;
780 destR.x = lroundf(pos.getX()) - sprite->w / 16;
781 destR.y = lroundf(pos.getY()) - sprite->h / 2;
782 destR.w = sprite->w / 8;
783 destR.h = sprite->h;
784
785 float angle = vel.getDirection() + 202.5;
786 int idx = lroundf(angle) % 360;
787 idx = idx / 45;
788 srcR.x = idx * sprite->w / 8;
789 srcR.y = 0;
790 srcR.w = sprite->w / 8;
791 srcR.h = sprite->h;
792
793 SDL_BlitSurface( sprite, &srcR, screen, &destR );
794 break;
795 }
796
797 default: break;
798 }
799 }
800
drawAirShot(SDL_Surface * screen)801 void Shot::drawAirShot(SDL_Surface *screen) {
802 switch (shotType) {
803 case SHOT_NORMAL:
804 case SHOT_NORMAL_HEAVY:
805 case SHOT_DOUBLE:
806 case SHOT_DOUBLE_HEAVY:
807 case SHOT_TRIPLE:
808 case SHOT_HF_NORMAL:
809 case SHOT_HF_DOUBLE:
810 case SHOT_HF_TRIPLE:
811 case SHOT_HF_QUATTRO:
812 case SHOT_HF_QUINTO:
813 case SPECIAL_SHOT_NUKE:
814 case ENEMY_SHOT_NORMAL:
815 {
816 SDL_Rect destR;
817 destR.x = lroundf(pos.getX()) - sprite->w / 2;
818 destR.y = lroundf(pos.getY()) - sprite->h / 2;
819 destR.w = sprite->w;
820 destR.h = sprite->h;
821 SDL_BlitSurface( sprite, 0, screen, &destR );
822 break;
823 }
824 default: break;
825 }
826 }
827
getShotType()828 ShotTypes Shot::getShotType() {
829 return shotType;
830 }
831
832
generateSmokePuff(int dT)833 void Shot::generateSmokePuff( int dT) {
834 if ( ! generatesSmokePuffs ) return;
835
836 timeToNextSmokePuff -= dT;
837 if ( timeToNextSmokePuff < 0 ) {
838 Vector2D relPos = -vel;
839 relPos.setLength( sprite->h / 2 );
840 if ( shotType == SHOT_HELLFIRE ||
841 shotType == ENEMY_SHOT_TANK_ROCKET ) {
842
843 smokePuffs->addSmokePuff( pos + relPos,
844 vel * SMOKE_PUFF_VELOCITY_FACTOR,
845 SMOKE_PUFF_MEDIUM );
846 timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_MEDIUM ];
847 } else {
848 smokePuffs->addSmokePuff( pos + relPos,
849 vel * SMOKE_PUFF_VELOCITY_FACTOR,
850 SMOKE_PUFF_SMALL );
851 timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_SMALL ];
852 }
853 }
854 }
855
856
857
858