// Description: // Basic enemy controller. Controls enemy movement along paths. // // Copyright (C) 2001 Frank Becker // // 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 of the License, 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 // #include #include #include #include #include #include #include #include #include #include #include #include #include //Rolling is currently disabled since it looks totally crappy in some //situations. The idea was for the bugs to lean into the curve (like //a motorcycle for example).. #define WITH_ROLLING 0 SimpleEnemy::SimpleEnemy( LEnemy *le): BaseEnemy(le, "SimpleEnemy") { XTRACE(); ModelManager &mm = *ModelManagerS::instance(); string fullModelName = "models/" + _lEnemy->modelName; _model = mm.getModel( fullModelName); #ifdef WITH_ROLLING norm(_dir); #endif _enemyGroup->newParticle( this, _offset.x, _offset.y, 0); } SimpleEnemy::~SimpleEnemy() { XTRACE(); } void SimpleEnemy::init( ParticleInfo *p) { // XTRACE(); p->position.z = -100.0; p->extra.x = 0; p->extra.y = 0; p->damage = 75; vec3 minpt, maxpt; _model->getBoundingBox( minpt, maxpt); p->radius = (maxpt.x-minpt.x)/1.8; //2.0; make badies a little easied to hit p->tod = -1; updatePrevs(p); } bool SimpleEnemy::update( ParticleInfo *p) { // XTRACE(); if( p->tod == 0) { GameState::numObjects--; if( (_moveState==eAttack) || (_moveState==eRetreat)) { GameState::enemyAttackCount--; } delete this; return false; } float dt = GameState::startOfGameStep - _pathStartTime; if( dt < 0) { return true; } _enableDraw = true; float len = _activePath->length(); _prevDir = _dir; if( dt > len) { //LOG_INFO << "dt = " << dt << ", len = " << len << endl; getNextPath(); dt -= len; _pathStartTime += len; _activePath->getTangent( dt, _prevDir); } _activePath->getPos( dt, _pos); _activePath->getTangent( dt, _dir); #ifdef WITH_ROLLING norm(_dir); if( _moveState != eIdle) { //The area of the triangle for the previous and current direction //vector is an indication of how sharp a turn the enemy is making. //Use this to roll the enemy. Point3D c = cross( _prevDir, _dir); float a = 250*sqrt(dot( c, c)); //is enemy turning left or right if( c.z < 0) { a = -a; } p->extra.y = a; // LOG_INFO << "rot = " << p->extra.y << endl; } else { p->extra.y = 0.0; } #endif ///LOG_INFO << "dt = " << dt << ", len = " << len << endl; ///LOG_INFO << "-p1 = " << _pos.x << "," << _pos.y << endl; ///LOG_INFO << "-off = " << _offset.x << "," << _offset.y << endl; updatePrevs(p); p->position.x = _pos.x + _offset.x; p->position.y = _pos.y + _offset.y; // p->position.z = _pos.z + _offset.z; if( _extraUpdateAfterTransport) { //We transport the enemy to a staging area. We need to update again //to make sure we use the new position in the interpolation step updatePrevs(p); _extraUpdateAfterTransport = false; } static ParticleGroup *ebg= ParticleGroupManagerS::instance()->getParticleGroup( SHOOTABLE_ENEMY_BULLETS_GROUP); if( (GameState::enemyBulletCount < Skill::getMaxBullets()) && ( (_moveState == eIdle) || (_moveState == eAttack) || (GameState::skill!=Skill::eRookie))) { p->extra.x += 0.1f*GAME_STEP_SCALE; if( p->extra.x > 0.5) { if( (Random::random() & 0xff) < Skill::getFireProbability()) { ebg->newParticle( // "PlasmaBullet",p->position.x,p->position.y,p->position.z); "BallOfFire",p->position.x,p->position.y,p->position.z); AudioS::instance()->playSample( "sounds/laser3.wav"); } p->extra.x = 0; } } return true; } void SimpleEnemy::draw( ParticleInfo *p) { // XTRACE(); if( _enableDraw) { ParticleInfo pi; interpolate( p, pi); glPushMatrix(); glTranslatef( pi.position.x, pi.position.y, pi.position.z); if( _moveState != eIdle) { float gf = GameState::frameFraction; Point3D _dirInterp; _dirInterp.x = _prevDir.x + (_dir.x - _prevDir.x) * gf; _dirInterp.y = _prevDir.y + (_dir.y - _prevDir.y) * gf; _dirInterp.z = _prevDir.z + (_dir.z - _prevDir.z) * gf; alignWith( _dirInterp); #if WITH_ROLLING glRotatef( pi.extra.y, 0,1,0); #endif } _model->draw(); glPopMatrix(); } } void SimpleEnemy::hit( ParticleInfo *p, int damage, int /*radIndex*/) { // XTRACE(); static ParticleGroup *effects = ParticleGroupManagerS::instance()->getParticleGroup( EFFECTS_GROUP2); _energy -= damage; if( _energy <= 0) { static ParticleGroup *bonus = ParticleGroupManagerS::instance()->getParticleGroup( BONUS_GROUP); p->tod = 0; if( _moveState == eAttack) { ScoreKeeperS::instance()->addToCurrentScore( 200); } else { ScoreKeeperS::instance()->addToCurrentScore( 100); } if( (Random::random() & 0xff) > 10) bonus->newParticle( "Bonus1", p->position.x, p->position.y, p->position.z); else bonus->newParticle( "SuperBonus", p->position.x, p->position.y, p->position.z); //spawn explosion for( int i=0; i<(int)(GameState::horsePower/2.0); i++) { effects->newParticle( "ExplosionPiece", p->position.x, p->position.y, p->position.z); } AudioS::instance()->playSample( "sounds/explosion.wav"); } else { //a hit but no kill ScoreKeeperS::instance()->addToCurrentScore( 50); ParticleInfo pi; pi.position.x = p->position.x; pi.position.y = p->position.y; pi.position.z = p->position.z; pi.velocity.x = _dir.x*0.6f*GAME_STEP_SCALE; pi.velocity.y = _dir.y*0.6f*GAME_STEP_SCALE; pi.velocity.z = _dir.z*0.6f*GAME_STEP_SCALE; effects->newParticle( "MiniSmoke", pi); } }