/*************************************************************************** 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 #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 ]; } } }