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 ];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|