/*************************************************************************** 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 #include "global.h" #include "enemys.h" #include "enemy.h" #include "racers.h" #include "racer.h" #include "wrecks.h" #include "wreck.h" #include "surfaceDB.h" #include "formation.h" #include "banners.h" #include "options.h" Enemys::Enemys() { timeToNextEnemy = GENERATE_ENEMY_DELAY + (rand() % (GENERATE_ENEMY_RAND_DELAY+1)); timeToNextFormation = GENERATE_FORMATION_DELAY + (rand() % (GENERATE_FORMATION_RAND_DELAY + 1)); bossActive = 0; enemysKilled = 0; enemysGenerated = 0; } Enemys::~Enemys() { vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { delete *i; } vector::iterator f; for (f = formations.begin(); f != formations.end(); ++f) { delete *f; } } void Enemys::addEnemy( Enemy *newEnemy ) { if ( newEnemy ) { enemys.push_back( newEnemy ); enemysGenerated++; } } bool Enemys::minibossDead(){ for ( unsigned int i = 0; i < enemys.size(); i++ ) { if ( enemys[ i ]->getType() == BOSS_2 ) return false; } return true; } bool Enemys::bossDead() { bool dead = true; for ( unsigned int i = 0; i < enemys.size(); i++ ) { if ( enemys[ i ]->getType() >= NR_ENEMY_TYPES_NORMAL ) { dead = false; break; } } if (dead) bossActive = 0; return dead; } void Enemys::bossTime( int bossNr ) { bossActive = bossNr; Enemy *boss; Wreck *wreck; switch (bossNr) { // Final boss case 1: { wreck = new Wreck( Vector2D( SCREEN_WIDTH / 2 , -120 ), WRECK_BOSS_1_BACKGROUND ); wrecks->addWreck( wreck ); boss = new Enemy( Vector2D( 321, -102 ), Vector2D( 0, SCROLL_SPEED ), BOSS_1_MAIN_GUN ); addEnemy( boss ); boss = new Enemy( Vector2D( 248, -73 ), Vector2D( 0, SCROLL_SPEED ), BOSS_1_SHOT_BATTERY_LEFT ); addEnemy( boss ); boss = new Enemy( Vector2D( 393, -73 ), Vector2D( 0, SCROLL_SPEED ), BOSS_1_SHOT_BATTERY_RIGHT ); addEnemy( boss ); boss = new Enemy( Vector2D( 50, -85 ), Vector2D( 0, SCROLL_SPEED ), BOSS_1_ROCKET_LAUNCHER ); addEnemy( boss ); boss = new Enemy( Vector2D( 590, -85 ), Vector2D( 0, SCROLL_SPEED ), BOSS_1_ROCKET_LAUNCHER ); addEnemy( boss ); break; } // Miniboss case 2: { boss = new Enemy( Vector2D( SCREEN_WIDTH / 2 , -100 ), Vector2D( 0, SCROLL_SPEED ), BOSS_2 ); addEnemy( boss ); break; } default: { cout << "Enemys::bossTime: unexpected bossNr: " << bossNr << endl; break; } } } void Enemys::generateEnemys( int dT ) { if ( bossActive != 0 ) return; timeToNextFormation -= dT; if ( timeToNextFormation < 0 ) { FormationTypes whichFormation = (FormationTypes)(rand() % NR_FORMATION_TYPES); int flags = 0; if ( rand() % 2 == 0 ) flags = flags | FORMATION_CHANGE_ON_KILL; if ( rand() % 2 == 0 ) { flags = flags | FORMATION_CHANGE_SPONTANEOUS; if ( rand() % 2 == 0 ) flags = flags | FORMATION_CHANGE_OFTEN; else flags = flags | FORMATION_CHANGE_SELDOM; } FormationShotPatterns shotPattern = (FormationShotPatterns)getRandValue( FORMATION_SP_CHANCES, NR_FORMATION_SP ); Formation *newV = new Formation( whichFormation, Vector2D( 80 + (rand() % (SCREEN_WIDTH - 160)), -100 ), Vector2D( 0, 40 ), FORMATION_MAX_NR_ENEMYS[ whichFormation ] - (rand() % 3), (FormationEnemySets)(rand() % NR_FORMATION_ENEMY_SETS), flags, shotPattern ); formations.push_back( newV ); timeToNextFormation = GENERATE_FORMATION_DELAY + (rand() % (GENERATE_FORMATION_RAND_DELAY + 1)); } timeToNextEnemy -= dT; if ( timeToNextEnemy < 0 ) { int enemyType = getRandValue( ENEMY_APPEAR_CHANCES, NR_ENEMY_TYPES_NORMAL ); Enemy *newOne = 0; switch ((EnemyTypes)enemyType) { case FIGHTER: { bool collides; float xpos; for ( int i = 0; i < 10; i++ ) { collides = false; xpos = 20 + rand() % (SCREEN_WIDTH - 40); // check if collides with a formation for ( unsigned int f = 0; f < formations.size(); f++ ) { Vector2D formationCenter = formations[ f ]->getCenter(); collides = formationCenter.getY() < 150 && fabs(xpos - formationCenter.getX()) < 150; if (collides) break; } } if ( !collides ) { newOne = new Enemy( Vector2D( xpos, -20 ), // position Vector2D( (rand() % 5) - 2, rand() % 7 + 3 ) * 10, // velocity FIGHTER ); } break; } case BOMBER: { bool collides; float xpos; for ( int i = 0; i < 10; i++ ) { collides = false; xpos = 20 + rand() % (SCREEN_WIDTH - 40); // check if collides with a formation for ( unsigned int f = 0; f < formations.size(); f++ ) { Vector2D formationCenter = formations[ f ]->getCenter(); collides = formationCenter.getY() < 150 && fabs(xpos - formationCenter.getX()) < 150; if (collides) break; } } if ( !collides ) { newOne = new Enemy( Vector2D( xpos, -20 ), // position Vector2D( (rand() % 5) - 2, rand() % 3 + 3 ) * 10, // velocity BOMBER ); } break; } case TANK: { // SdlCompat_AcceleratedSurface *spriteTank = surfaceDB.loadSurface( FN_ENEMY_TANK ); // SdlCompat_AcceleratedSurface *spriteTankWreck = surfaceDB.loadSurface( FN_WRECK_TANK ); string fn1, fn2; levelConf->getStr( LVL_ENEMY_TANK, fn1 ); levelConf->getStr( LVL_WRECK_TANK, fn2 ); SdlCompat_AcceleratedSurface *spriteTank = surfaceDB.loadSurface( fn1 ); SdlCompat_AcceleratedSurface *spriteTankWreck = surfaceDB.loadSurface( fn2 ); int halfWidthTank = spriteTank->w / 2; int halfHeightTank = spriteTank->h / 2; int halfWidthTankWreck = spriteTankWreck->w / 2; int halfHeightTankWreck = spriteTankWreck->h / 2; int xPos; for ( int i = 0; i < 10; i++ ) { bool collides = false; xPos = halfWidthTank + rand() % (SCREEN_WIDTH - spriteTank->w); // check if collides with another tank for ( unsigned int t = 0; t < enemys.size(); t++ ) { if ( enemys[ t ]->getType() == TANK ) { Vector2D enemyTankPos = enemys[ t ]->getPos(); collides = !(enemyTankPos.getX() - halfWidthTank > xPos + halfWidthTank) && !(enemyTankPos.getX() + halfWidthTank < xPos - halfWidthTank) && !(enemyTankPos.getY() - halfHeightTank > 0); if (collides) break; } } if (!collides) { // check if collides with an old wreck for ( unsigned int w = 0; w < wrecks->getNrWrecks(); w++ ) { if ( wrecks->getWreck( w )->getType() == WRECK_TANK ) { Vector2D wreckPos = wrecks->getWreck(w)->getPos(); collides = !(wreckPos.getX() - halfWidthTankWreck > xPos + halfWidthTank) && !(wreckPos.getX() + halfWidthTankWreck < xPos - halfWidthTank) && !(wreckPos.getY() - halfHeightTankWreck > 0); if (collides) break; } } } // no collision -> generate this enemy if ( !collides ) { // the tank and the background have to be at the same pixel-fraction // to avoid a waggle-effect float correlateToBackground = actBackgroundPos - truncf(actBackgroundPos); newOne = new Enemy( Vector2D( xPos, -halfHeightTank - correlateToBackground ), Vector2D (0, SCROLL_SPEED), // tanks are not moving TANK ); break; } } break; } default: { cout << "generateEnemys(): unexpected enemyType: " << enemyType << endl; break; } } addEnemy( newOne ); // +1 for security timeToNextEnemy = GENERATE_ENEMY_DELAY + (rand() % (GENERATE_ENEMY_RAND_DELAY+1)); } } void Enemys::updateEnemys( int dT ) { vector::iterator f; for (f = formations.begin(); f != formations.end(); ++f) { (*f)->update( dT ); } vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { (*i)->update( dT ); } } void Enemys::doNukeDamage() { vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { (*i)->doDamage( SPECIAL_SHOT_NUKE, -1 ); } } void Enemys::deleteExpiredEnemys() { unsigned int i = 0; while ( i < enemys.size() ) { if ( enemys[i]->isExpired() ) { if ( enemys[i]->isDead() ) { enemysKilled++; if ( arcadeGame ) { if ( enemysKilled % 50 == 0 ) { banners->addBanner( BANNER_ENEMYS_KILLED, BANNER_MODE_RANDOM, ARCADE_BONUS_FOR_ENEMYS_KILLED ); racers->receivePointsArcade( ARCADE_POINTS_FOR_ENEMYS_KILLED ); } } } for ( unsigned int f = 0; f < formations.size(); f++ ) { formations[f]->enemyKilled( enemys[i] ); } delete enemys[i]; enemys.erase(enemys.begin() + i); } else { i++; } } unsigned int f = 0; while ( f < formations.size() ) { if ( formations[f]->isExpired() ) { // assert, that the formation is not deleted, because all // enemys in the formation flew out of the screen if ( arcadeGame && formations[f]->getCenter().getY() < SCREEN_HEIGHT + 400 ) { banners->addBanner( (BannerTexts)(rand() % NR_BANNER_TEXTS), BANNER_MODE_RANDOM, ARCADE_BONUS_FOR_FORMATION_DESTRUCTION ); racers->receivePointsArcade( ARCADE_POINTS_FOR_FORMATION_DESTRUCTION ); } delete formations[f]; formations.erase(formations.begin() + f); } else { f++; } } } void Enemys::drawGroundEnemys(SdlCompat_AcceleratedSurface *screen) { vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { (*i)->drawGroundEnemy(screen); } } void Enemys::drawAirEnemys(SdlCompat_AcceleratedSurface *screen) { vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { (*i)->drawAirEnemy(screen); } } void Enemys::drawShadows(SdlCompat_AcceleratedSurface *screen) { vector::iterator i; for (i = enemys.begin(); i != enemys.end(); ++i) { (*i)->drawShadow(screen); } } void Enemys::drawBossStats( SdlCompat_AcceleratedSurface *screen ) { for ( unsigned int i = 0; i < enemys.size(); i++ ) { if ( enemys[ i ]->getType() >= NR_ENEMY_TYPES_NORMAL ) { enemys[ i ]->drawStats( screen ); } } } void Enemys::drawAllStats( SdlCompat_AcceleratedSurface *screen ) { for ( unsigned int i = 0; i < enemys.size(); i++ ) { enemys[ i ]->drawStats( screen ); } }