and 30 FPS on HTC Evo (it could run faster but they limited max FPS in GL renderer). See file SdlForwardCompat.h to have an idea how to make your SDL 1.2 app HW accelerated with SDL 1.3 - it's a compilation of hacks but whatever.
858 lines
23 KiB
C++
858 lines
23 KiB
C++
/***************************************************************************
|
|
alienBlaster
|
|
Copyright (C) 2004
|
|
Paul Grathwohl, Arne Hormann, Daniel Kuehn, Soenke Schwardt
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
***************************************************************************/
|
|
using namespace std;
|
|
|
|
#include <iostream>
|
|
#include "shot.h"
|
|
#include "surfaceDB.h"
|
|
#include "racers.h"
|
|
#include "racer.h"
|
|
#include "explosions.h"
|
|
#include "explosion.h"
|
|
#include "enemys.h"
|
|
#include "enemy.h"
|
|
#include "smokePuffs.h"
|
|
#include "boundingBox.h"
|
|
|
|
Shot::Shot( ShotTypes shotType, int playerNr, Vector2D position, float angle ) {
|
|
|
|
this->shotType = shotType;
|
|
fromWhichPlayer = playerNr;
|
|
|
|
pos = position;
|
|
collidedWithGround = false;
|
|
deflectedBySonicFromPlayer1 = false;
|
|
deflectedBySonicFromPlayer2 = false;
|
|
|
|
generatesSmokePuffs = false;
|
|
timeToNextSmokePuff = 100;
|
|
|
|
switch (shotType) {
|
|
// primary shots
|
|
case SHOT_NORMAL:
|
|
{
|
|
vel = Vector2D( VEL_SHOT_NORMAL, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_NORMAL;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
|
|
break;
|
|
}
|
|
case SHOT_NORMAL_HEAVY:
|
|
{
|
|
vel = Vector2D( VEL_SHOT_NORMAL_HEAVY, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_NORMAL_HEAVY;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL_HEAVY );
|
|
break;
|
|
}
|
|
case SHOT_DOUBLE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_DOUBLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_DOUBLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE );
|
|
break;
|
|
}
|
|
case SHOT_DOUBLE_HEAVY:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_DOUBLE_HEAVY, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_DOUBLE_HEAVY;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_DOUBLE_HEAVY );
|
|
break;
|
|
}
|
|
case SHOT_TRIPLE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_TRIPLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_TRIPLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_TRIPLE );
|
|
break;
|
|
}
|
|
|
|
// primary shots heavy fighter
|
|
case SHOT_HF_NORMAL:
|
|
{
|
|
vel = Vector2D( VEL_SHOT_HF_NORMAL, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_NORMAL;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_NORMAL );
|
|
break;
|
|
}
|
|
case SHOT_HF_DOUBLE:
|
|
{
|
|
vel = Vector2D( VEL_SHOT_HF_DOUBLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_DOUBLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_DOUBLE );
|
|
break;
|
|
}
|
|
case SHOT_HF_TRIPLE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_TRIPLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_TRIPLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_TRIPLE );
|
|
break;
|
|
}
|
|
case SHOT_HF_QUATTRO:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_QUATTRO, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_QUATTRO;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUATTRO );
|
|
break;
|
|
}
|
|
case SHOT_HF_QUINTO:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_QUINTO, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_QUINTO;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_QUINTO );
|
|
break;
|
|
}
|
|
|
|
// secondary shots
|
|
case SHOT_DUMBFIRE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_DUMBFIRE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_DUMBFIRE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SHOT_DUMBFIRE_DOUBLE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_DUMBFIRE_DOUBLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_DUMBFIRE_DOUBLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_DUMBFIRE_DOUBLE );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SHOT_KICK_ASS_ROCKET:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_KICK_ASS_ROCKET, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_KICK_ASS_ROCKET;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET );
|
|
spriteShadow = surfaceDB.loadSurface( FN_SHOT_KICK_ASS_ROCKET_SHADOW, true );
|
|
break;
|
|
}
|
|
case SHOT_HELLFIRE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HELLFIRE / 2.0, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HELLFIRE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HELLFIRE );
|
|
spriteShadow = surfaceDB.loadSurface( FN_SHOT_HELLFIRE_SHADOW, true );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SHOT_MACHINE_GUN:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_MACHINE_GUN, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_MACHINE_GUN;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_MACHINE_GUN );
|
|
break;
|
|
}
|
|
case SHOT_ENERGY_BEAM:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_ENERGY_BEAM, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_ENERY_BEAM;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_ENERGY_BEAM, true );
|
|
break;
|
|
}
|
|
|
|
// secondary shots heavy fighter
|
|
case SHOT_HF_DUMBFIRE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_DUMBFIRE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_DUMBFIRE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SHOT_HF_DUMBFIRE_DOUBLE:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_DUMBFIRE_DOUBLE, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_DUMBFIRE_DOUBLE;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_DUMBFIRE_DOUBLE );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SHOT_HF_KICK_ASS_ROCKET:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_KICK_ASS_ROCKET, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_KICK_ASS_ROCKET;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET );
|
|
spriteShadow = surfaceDB.loadSurface( FN_SHOT_HF_KICK_ASS_ROCKET_SHADOW, true );
|
|
break;
|
|
}
|
|
case SHOT_HF_LASER:
|
|
{
|
|
vel = Vector2D(VEL_SHOT_HF_LASER, angle, POLAR);
|
|
timeToLive = LIFETIME_SHOT_HF_LASER;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_HF_LASER );
|
|
break;
|
|
}
|
|
|
|
// special shots
|
|
case SPECIAL_SHOT_HEATSEEKER:
|
|
{
|
|
vel = Vector2D(VEL_SPECIAL_SHOT_HEATSEEKER, angle, POLAR);
|
|
timeToLive = LIFETIME_SPECIAL_SHOT_HEATSEEKER;
|
|
sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_HEATSEEKER );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
case SPECIAL_SHOT_NUKE:
|
|
{
|
|
vel = Vector2D( VEL_SPECIAL_SHOT_NUKE, angle, POLAR );
|
|
timeToLive = LIFETIME_SPECIAL_SHOT_NUKE;
|
|
sprite = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE );
|
|
spriteShadow = surfaceDB.loadSurface( FN_SPECIAL_SHOT_NUKE_SHADOW, true );
|
|
break;
|
|
}
|
|
|
|
// enemy shots
|
|
case ENEMY_SHOT_NORMAL:
|
|
{
|
|
vel = Vector2D(VEL_ENEMY_SHOT_NORMAL, angle, POLAR);
|
|
timeToLive = LIFETIME_ENEMY_SHOT_NORMAL;
|
|
sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_NORMAL );
|
|
break;
|
|
}
|
|
case ENEMY_SHOT_TANK_ROCKET:
|
|
{
|
|
vel = Vector2D(VEL_ENEMY_SHOT_TANK_ROCKET, angle, POLAR);
|
|
timeToLive = LIFETIME_ENEMY_SHOT_TANK_ROCKET;
|
|
sprite = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET );
|
|
spriteShadow = surfaceDB.loadSurface( FN_ENEMY_SHOT_TANK_ROCKET_SHADOW, true );
|
|
generatesSmokePuffs = true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
vel = Vector2D(0,0);
|
|
timeToLive = 0;
|
|
sprite = surfaceDB.loadSurface( FN_SHOT_NORMAL );
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void Shot::moveAndCollide( int dT ) {
|
|
|
|
if ( fromWhichPlayer == 666 ) {
|
|
moveAndCollideEnemyShot( dT );
|
|
} else {
|
|
moveAndCollidePlayerShot( dT );
|
|
}
|
|
|
|
generateSmokePuff( dT );
|
|
|
|
timeToLive -= dT;
|
|
}
|
|
|
|
|
|
void Shot::moveAndCollidePlayerShot( int dT ) {
|
|
|
|
Vector2D posOld = pos;
|
|
|
|
// move the shot
|
|
movePlayerShot( dT );
|
|
|
|
if ( !isExpired() && collidePlayerShot( posOld ) ) {
|
|
addExplosion();
|
|
}
|
|
}
|
|
|
|
|
|
void Shot::movePlayerShot( int dT ) {
|
|
switch (shotType) {
|
|
case SHOT_NORMAL:
|
|
case SHOT_NORMAL_HEAVY:
|
|
case SHOT_DOUBLE:
|
|
case SHOT_DOUBLE_HEAVY:
|
|
case SHOT_TRIPLE:
|
|
case SHOT_HF_NORMAL:
|
|
case SHOT_HF_DOUBLE:
|
|
case SHOT_HF_TRIPLE:
|
|
case SHOT_HF_QUATTRO:
|
|
case SHOT_HF_QUINTO:
|
|
case SHOT_DUMBFIRE:
|
|
case SHOT_DUMBFIRE_DOUBLE:
|
|
case SHOT_KICK_ASS_ROCKET:
|
|
case SHOT_MACHINE_GUN:
|
|
case SHOT_ENERGY_BEAM:
|
|
case SHOT_HF_DUMBFIRE:
|
|
case SHOT_HF_DUMBFIRE_DOUBLE:
|
|
case SHOT_HF_KICK_ASS_ROCKET:
|
|
case SHOT_HF_LASER:
|
|
{
|
|
pos = pos + vel * dT / 1000.0;
|
|
break;
|
|
}
|
|
case SHOT_HELLFIRE:
|
|
{
|
|
pos = pos + vel * dT / 1000.0;
|
|
if ( timeToLive < LIFETIME_SHOT_HELLFIRE - 100 ) {
|
|
vel = Vector2D( 0, -VEL_SHOT_HELLFIRE );
|
|
}
|
|
break;
|
|
}
|
|
case SPECIAL_SHOT_HEATSEEKER:
|
|
{
|
|
pos = pos + vel * dT / 1000.0;
|
|
if ( timeToLive >= LIFETIME_SPECIAL_SHOT_HEATSEEKER - 200 ) break;
|
|
int idxNearestEnemy = 0;
|
|
float distNearest = 10000;
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
// vector from the shotPosition to the enemy
|
|
Vector2D v = enemys->getEnemy(i)->getPos() - pos;
|
|
if ( getAbsAngleDifference( vel.getDirection(), v.getDirection() ) < 180 ) {
|
|
float dist = (enemys->getEnemy(i)->getPos() - pos).getLength();
|
|
if ( dist < distNearest ) {
|
|
distNearest = dist;
|
|
idxNearestEnemy = i;
|
|
}
|
|
}
|
|
}
|
|
if ( distNearest != 10000 ) {
|
|
float angle =
|
|
getAngleDifference( (enemys->getEnemy(idxNearestEnemy)->getPos()-pos).getDirection(),
|
|
vel.getDirection() );
|
|
if ( fabs(angle) < 80 * dT / 1000.0 ) {
|
|
vel.rotate(angle);
|
|
} else if ( angle < 0 ) {
|
|
vel.rotate( -80 * dT / 1000.0 );
|
|
} else {
|
|
vel.rotate( 80 * dT / 1000.0 );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SPECIAL_SHOT_NUKE:
|
|
{
|
|
pos = pos + vel * dT / 1000.0;
|
|
// Nuke is in its place!
|
|
if ( (pos - Vector2D( SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 )).getLength()
|
|
<= (vel * dT / 1000.0).getLength() ) {
|
|
nukeIsInPlace = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
cout << "movePlayerShot: unexpected shotType: " << shotType << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clip at the outside of the window
|
|
if ( !RectangleGeo( Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ),
|
|
Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER,
|
|
SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
|
|
timeToLive = 0;
|
|
}
|
|
}
|
|
|
|
|
|
bool Shot::collidePlayerShot( Vector2D posOld ) {
|
|
switch (shotType) {
|
|
// only against air
|
|
case SHOT_ENERGY_BEAM:
|
|
{
|
|
BoundingBox box( lroundf(posOld.getX()) - sprite->w / 2,
|
|
lroundf(pos.getY()) - sprite->w / 2,
|
|
sprite->w,
|
|
lroundf((posOld-pos).getY()) + sprite->h );
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
|
|
enemys->getEnemy(i)->collidesWith( &box ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//only against air
|
|
case SHOT_HF_LASER:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
|
|
enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = false;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// against air and ground
|
|
case SHOT_NORMAL:
|
|
case SHOT_NORMAL_HEAVY:
|
|
case SHOT_DOUBLE:
|
|
case SHOT_DOUBLE_HEAVY:
|
|
case SHOT_TRIPLE:
|
|
case SHOT_HF_NORMAL:
|
|
case SHOT_HF_DOUBLE:
|
|
case SHOT_HF_TRIPLE:
|
|
case SHOT_HF_QUATTRO:
|
|
case SHOT_HF_QUINTO:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( //ENEMY_FLYING[ enemys->getEnemy(i)->getType() ] &&
|
|
enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// against air and ground
|
|
case SHOT_DUMBFIRE:
|
|
case SHOT_DUMBFIRE_DOUBLE:
|
|
case SHOT_HF_DUMBFIRE:
|
|
case SHOT_HF_DUMBFIRE_DOUBLE:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// only against ground
|
|
case SHOT_KICK_ASS_ROCKET:
|
|
case SHOT_HF_KICK_ASS_ROCKET:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) &&
|
|
enemys->getEnemy(i)->collidesWith( Circle(pos, 15) ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = true;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// only against ground, but has to hit more exactly than kickAssRocket
|
|
case SHOT_HELLFIRE:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( (!ENEMY_FLYING[ enemys->getEnemy(i)->getType() ]) &&
|
|
enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = true;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// against air and ground
|
|
case SHOT_MACHINE_GUN:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( enemys->getEnemy(i)->collidesWith( posOld, pos ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// against air and ground
|
|
case SPECIAL_SHOT_HEATSEEKER:
|
|
{
|
|
for ( unsigned int i = 0; i < enemys->getNrEnemys(); i++ ) {
|
|
if ( enemys->getEnemy(i)->collidesWith( Circle(pos, 5) ) ) {
|
|
enemys->getEnemy(i)->doDamage( shotType, fromWhichPlayer );
|
|
timeToLive = 0;
|
|
collidedWithGround = !ENEMY_FLYING[ enemys->getEnemy(i)->getType() ];
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPECIAL_SHOT_NUKE: break;
|
|
|
|
default:
|
|
{
|
|
cout << "collidePlayerShot: unexpected shotType: " << shotType << endl;
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////
|
|
|
|
void Shot::moveAndCollideEnemyShot( int dT ) {
|
|
Vector2D posOld = pos;
|
|
|
|
moveEnemyShot( dT );
|
|
|
|
if ( !isExpired() && collideEnemyShot( posOld ) ) {
|
|
addExplosion();
|
|
}
|
|
}
|
|
|
|
|
|
void Shot::moveEnemyShot( int dT ) {
|
|
switch (shotType) {
|
|
case ENEMY_SHOT_NORMAL: {
|
|
// is this shot near the deflector of a racer?
|
|
for ( unsigned int i = 0; i < racers->getNrRacers(); i++) {
|
|
if ( racers->getRacer(i)->getShipType() == HEAVY_FIGHTER ) {
|
|
Vector2D racerToShot = pos - racers->getRacer(i)->getPos();
|
|
if ( racerToShot.getLength() < RACER_DEFLECTOR_ACTIVATION_DIST ) {
|
|
vel += Vector2D( RACER_DEFLECTOR_POWER * (dT / 1000.0),
|
|
racerToShot.getDirection(), POLAR );
|
|
}
|
|
if ( racers->getRacer(i)->isDeflectorSpecialActive() ) {
|
|
if ( racerToShot.getLength() < ITEM_DEFLECTOR_ACTIVATION_DIST ) {
|
|
vel += Vector2D( ITEM_DEFLECTOR_POWER * (dT / 1000.0),
|
|
racerToShot.getDirection(), POLAR );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pos = pos + vel * dT / 1000.0;
|
|
break;
|
|
}
|
|
case ENEMY_SHOT_TANK_ROCKET: {
|
|
if ( deflectedBySonicFromPlayer1 ) {
|
|
Vector2D racerToShot = pos - racers->getRacer(0)->getPos();
|
|
if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
|
|
vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0),
|
|
racerToShot.getDirection(), POLAR );
|
|
deflectedBySonicFromPlayer1 = false;
|
|
}
|
|
}
|
|
if ( deflectedBySonicFromPlayer2 ) {
|
|
Vector2D racerToShot = pos - racers->getRacer(1)->getPos();
|
|
if ( racerToShot.getLength() < RACER_SONIC_ACTIVATION_DIST ) {
|
|
vel += Vector2D( RACER_SONIC_POWER * (dT / 1000.0),
|
|
racerToShot.getDirection(), POLAR );
|
|
deflectedBySonicFromPlayer2 = false;
|
|
}
|
|
}
|
|
pos = pos + vel * dT / 1000.0;
|
|
break;
|
|
}
|
|
default: {
|
|
cout << "moveEnemyShot: unexpected shotType: " << shotType << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clip at the outside of the window
|
|
if ( !RectangleGeo(Vector2D( -SHOT_SCREEN_BORDER, -SHOT_SCREEN_BORDER ),
|
|
Vector2D( SCREEN_WIDTH + SHOT_SCREEN_BORDER,
|
|
SCREEN_HEIGHT + SHOT_SCREEN_BORDER )).isInside(pos) ) {
|
|
timeToLive = 0;
|
|
}
|
|
}
|
|
|
|
|
|
bool Shot::collideEnemyShot( Vector2D posOld ) {
|
|
switch (shotType) {
|
|
case ENEMY_SHOT_NORMAL:
|
|
{
|
|
for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
|
|
if ( racers->getRacer(i)->collidesWith( posOld, pos ) ) {
|
|
racers->getRacer(i)->doDamage( shotType );
|
|
timeToLive = 0;
|
|
collidedWithGround = false;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ENEMY_SHOT_TANK_ROCKET:
|
|
{
|
|
for ( unsigned int i = 0; i < racers->getNrRacers(); i++ ) {
|
|
if ( racers->getRacer(i)->collidesWith( Circle(pos, 5) ) ) {
|
|
racers->getRacer(i)->doDamage( shotType );
|
|
timeToLive = 0;
|
|
collidedWithGround = false;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
cout << "collideEnemyShot: unexpected shotType: " << shotType << endl;
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////
|
|
|
|
void Shot::addExplosion() {
|
|
Explosion *explosion;
|
|
switch (shotType) {
|
|
default:
|
|
{
|
|
if ( collidedWithGround ) {
|
|
explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
|
|
vel / 10.0, EXPLOSION_NORMAL_GROUND );
|
|
} else {
|
|
if ( shotType == SHOT_HF_LASER ) {
|
|
// Laser is too fast...
|
|
explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
|
|
vel / 30.0, EXPLOSION_NORMAL_AIR );
|
|
} else {
|
|
explosion = new Explosion( FN_EXPLOSION_NORMAL, pos,
|
|
vel / 10.0, EXPLOSION_NORMAL_AIR );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
explosions->addExplosion( explosion );
|
|
}
|
|
|
|
////////////////////
|
|
|
|
void Shot::drawShadow(SdlCompat_AcceleratedSurface *screen) {
|
|
switch (shotType) {
|
|
case SHOT_KICK_ASS_ROCKET:
|
|
case SHOT_HF_KICK_ASS_ROCKET:
|
|
case SHOT_HELLFIRE:
|
|
case SPECIAL_SHOT_NUKE:
|
|
{
|
|
SDL_Rect shadowR;
|
|
shadowR.x = lroundf(pos.getX()) - spriteShadow->w / 2 - 7;
|
|
shadowR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 7;
|
|
shadowR.w = spriteShadow->w;
|
|
shadowR.h = spriteShadow->h;
|
|
SDL_BlitSurface( spriteShadow, 0, screen, &shadowR );
|
|
break;
|
|
}
|
|
case ENEMY_SHOT_TANK_ROCKET:
|
|
{
|
|
SDL_Rect destR;
|
|
SDL_Rect srcR;
|
|
destR.x = lroundf(pos.getX()) - spriteShadow->w / 16 - 10;
|
|
destR.y = lroundf(pos.getY()) - spriteShadow->h / 2 + 10;
|
|
destR.w = spriteShadow->w / 8;
|
|
destR.h = spriteShadow->h;
|
|
float angle = vel.getDirection() + 202.5;
|
|
int idx = lroundf(angle) % 360;
|
|
idx = idx / 45;
|
|
srcR.x = idx * spriteShadow->w / 8;
|
|
srcR.y = 0;
|
|
srcR.w = spriteShadow->w / 8;
|
|
srcR.h = spriteShadow->h;
|
|
|
|
SDL_BlitSurface( spriteShadow, &srcR, screen, &destR );
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void Shot::drawGroundShot(SdlCompat_AcceleratedSurface *screen) {
|
|
switch (shotType) {
|
|
case SHOT_KICK_ASS_ROCKET:
|
|
case SHOT_HF_KICK_ASS_ROCKET:
|
|
case SHOT_HELLFIRE:
|
|
{
|
|
SDL_Rect destR;
|
|
SDL_Rect srcR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 2;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w;
|
|
destR.h = sprite->h;
|
|
srcR.x = 0;
|
|
srcR.y = 0;
|
|
srcR.w = sprite->w;
|
|
srcR.h = sprite->h;
|
|
|
|
SDL_BlitSurface( sprite, &srcR, screen, &destR );
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void Shot::drawGroundAirShot(SdlCompat_AcceleratedSurface *screen) {
|
|
switch (shotType) {
|
|
case SHOT_DUMBFIRE:
|
|
case SHOT_DUMBFIRE_DOUBLE:
|
|
case SHOT_HF_DUMBFIRE:
|
|
case SHOT_HF_DUMBFIRE_DOUBLE:
|
|
{
|
|
SDL_Rect destR;
|
|
SDL_Rect srcR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 16;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w / 8;
|
|
destR.h = sprite->h;
|
|
// TODO: eight directions are outdated for dumbfire, but existent in the image
|
|
float angle = vel.getDirection() + 202.5;
|
|
int idx = lroundf(angle) % 360;
|
|
idx = idx / 45;
|
|
srcR.x = idx * 8;
|
|
srcR.y = 0;
|
|
srcR.w = 8;
|
|
srcR.h = 8;
|
|
|
|
SDL_BlitSurface( sprite, &srcR, screen, &destR );
|
|
break;
|
|
}
|
|
case SHOT_MACHINE_GUN:
|
|
case SHOT_HF_LASER:
|
|
{
|
|
SDL_Rect destR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 2;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w;
|
|
destR.h = sprite->h;
|
|
SDL_BlitSurface( sprite, 0, screen, &destR );
|
|
break;
|
|
}
|
|
case SHOT_ENERGY_BEAM:
|
|
{
|
|
SDL_Rect destR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 2;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w;
|
|
destR.h = sprite->h;
|
|
SDL_BlitSurface( sprite, 0, screen, &destR );
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 2;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
SDL_BlitSurface( sprite, 0, screen, &destR );
|
|
break;
|
|
}
|
|
case SPECIAL_SHOT_HEATSEEKER:
|
|
{
|
|
SDL_Rect destR;
|
|
SDL_Rect srcR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 16;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w / 8;
|
|
destR.h = sprite->h;
|
|
|
|
float angle = vel.getDirection() + 202.5;
|
|
int idx = lroundf(angle) % 360;
|
|
idx = idx / 45;
|
|
srcR.x = idx * 8;
|
|
srcR.y = 0;
|
|
srcR.w = 8;
|
|
srcR.h = 8;
|
|
|
|
SDL_BlitSurface( sprite, &srcR, screen, &destR );
|
|
break;
|
|
}
|
|
case ENEMY_SHOT_TANK_ROCKET:
|
|
{
|
|
SDL_Rect destR;
|
|
SDL_Rect srcR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 16;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w / 8;
|
|
destR.h = sprite->h;
|
|
|
|
float angle = vel.getDirection() + 202.5;
|
|
int idx = lroundf(angle) % 360;
|
|
idx = idx / 45;
|
|
srcR.x = idx * sprite->w / 8;
|
|
srcR.y = 0;
|
|
srcR.w = sprite->w / 8;
|
|
srcR.h = sprite->h;
|
|
|
|
SDL_BlitSurface( sprite, &srcR, screen, &destR );
|
|
break;
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void Shot::drawAirShot(SdlCompat_AcceleratedSurface *screen) {
|
|
switch (shotType) {
|
|
case SHOT_NORMAL:
|
|
case SHOT_NORMAL_HEAVY:
|
|
case SHOT_DOUBLE:
|
|
case SHOT_DOUBLE_HEAVY:
|
|
case SHOT_TRIPLE:
|
|
case SHOT_HF_NORMAL:
|
|
case SHOT_HF_DOUBLE:
|
|
case SHOT_HF_TRIPLE:
|
|
case SHOT_HF_QUATTRO:
|
|
case SHOT_HF_QUINTO:
|
|
case SPECIAL_SHOT_NUKE:
|
|
case ENEMY_SHOT_NORMAL:
|
|
{
|
|
SDL_Rect destR;
|
|
destR.x = lroundf(pos.getX()) - sprite->w / 2;
|
|
destR.y = lroundf(pos.getY()) - sprite->h / 2;
|
|
destR.w = sprite->w;
|
|
destR.h = sprite->h;
|
|
SDL_BlitSurface( sprite, 0, screen, &destR );
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
ShotTypes Shot::getShotType() {
|
|
return shotType;
|
|
}
|
|
|
|
|
|
void Shot::generateSmokePuff( int dT) {
|
|
if ( ! generatesSmokePuffs ) return;
|
|
|
|
timeToNextSmokePuff -= dT;
|
|
if ( timeToNextSmokePuff < 0 ) {
|
|
Vector2D relPos = -vel;
|
|
relPos.setLength( sprite->h / 2 );
|
|
if ( shotType == SHOT_HELLFIRE ||
|
|
shotType == ENEMY_SHOT_TANK_ROCKET ) {
|
|
|
|
smokePuffs->addSmokePuff( pos + relPos,
|
|
vel * SMOKE_PUFF_VELOCITY_FACTOR,
|
|
SMOKE_PUFF_MEDIUM );
|
|
timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_MEDIUM ];
|
|
} else {
|
|
smokePuffs->addSmokePuff( pos + relPos,
|
|
vel * SMOKE_PUFF_VELOCITY_FACTOR,
|
|
SMOKE_PUFF_SMALL );
|
|
timeToNextSmokePuff += SMOKE_PUFF_DELAY_TO_NEXT_PUFF[ SMOKE_PUFF_SMALL ];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|