/* $Id: guidedmissile.cpp,v 1.9 2005/10/21 08:02:53 pohlt Exp $ */ #include "guidedmissile.hpp" #include "avatar.hpp" #include "player.hpp" #include "world.hpp" #include "smoke.hpp" /**********************************************************/ #define ACCELERATE_FUEL 15.0 #define STEER_FUEL 100.0 #define CHASING_THRUST_IMPACT 75 #define TURNING_SPEED 10.0 // dependent constants #define TOTAL_FUEL (ACCELERATE_FUEL + STEER_FUEL) /**********************************************************/ GuidedMissile::GuidedMissile() { // // take the values from class Missile // m_health = 200000; m_mass = 100.0; // // m_collRectX = -4; // m_collRectY = -4; // // m_collRectWidth = 9; // m_collRectHeight = 9; // } /**********************************************************/ bool GuidedMissile::initialize( const Vector& pos, const Vector& vel, const Uint8 owner, const Sint32 nShrapnels, const Sint32 damage, const Vector& thrust ) { m_angle = ATAN2_REAL( thrust.y, thrust.x ); if( Missile::initialize( pos, vel, owner, nShrapnels, damage, TOTAL_FUEL, thrust ) ) { Avatar* avatar = m_worldPointer->getPlayerByID( m_owner )->getAvatar(); // turn on guided missile mode avatar->setState( Avatar::MODE_GM, true ); setFocusPriority( avatar->getFocusPriority() + FOCUS_PRIO_GUIDED_MISSILE ); m_worldPointer->scanObjectsForFocus(); return true; } else return false; } /**********************************************************/ void GuidedMissile::update() { preBallistics(); bool detonate = false, ignition = false; const Player* pilotPlayer = m_worldPointer->getPlayerByID( m_owner ); Avatar* pilot = NULL; if( pilotPlayer ) pilot = pilotPlayer->getAvatar(); const real DT = m_worldPointer->getDT( m_pos ); if( m_accelTimer.getTimeToLive() > ( TOTAL_FUEL - ACCELERATE_FUEL ) ) { ignition = true; } else { if( pilot ) { pilot->setStateMask( ~0 ); if( !m_accelTimer.isElapsed() ) { if( pilot->isMovingLeft() ) m_angle -= TURNING_SPEED*M_PI/180.0; if( pilot->isMovingRight() ) m_angle += TURNING_SPEED*M_PI/180.0; if( m_angle >= 2.0*M_PI ) m_angle -= 2.0*M_PI; if( m_angle < 0.0 ) m_angle += 2.0*M_PI; if( pilot->isJumping() ) { m_thrust.setAngle( m_angle, CHASING_THRUST_IMPACT ); ignition = true; } } if( pilot->isShooting() || ! pilot->isLiving() ) detonate = true; } else detonate = true; } // explode? if( m_health <= 0 || detonate ) { explode(); } else { // engine is running if( ignition ) { // accelerate in direction of thrust m_force += m_thrust; if ( m_accelTimer.hasSignal() ) { // throw out some booster sparks int numParticles = static_cast( 15 * DT ); for( int i = 0; i < numParticles; i++ ) { m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos, m_vel - m_thrust*0.1 + m_worldPointer->getRandom().getVector( 1.5 ), Particles::BOUNCE_EARTH | //Particles::BOUNCE_OBJECT | Particles::REDUNDANT | Particles::SPARK, 2, 0, Particles::m_N_SPARK_COLORS/2 + m_worldPointer->getRandom().getUint32() % (Particles::m_N_SPARK_COLORS/2+1), 1, 0xffffff)); } // generate a smoke cloud Smoke* cloud = static_cast(m_worldPointer->newObject(SMOKE_30)); cloud->setPos( m_pos + localRnd.getVector( 4.0 )); cloud->setVel( m_vel + localRnd.getVector( 2.0 )); if( pilotPlayer ) cloud->setColor( pilotPlayer->getPlayerColor() ); } // decrement "fuel" m_accelTimer.countDown( DT ); } doBallistics(); // increment the animation frame if( ++m_Frame >= m_SequenceLength ) m_Frame = 0; // select new missile sprite updateSpriteSelection( !m_accelTimer.isElapsed() ); postBallistics(); } } /**********************************************************/ void GuidedMissile::explode() { Audio::getInstance()->playSound( m_explodeSample, m_pos ); m_worldPointer->newObject( EXPLOSION_80 )->setPos( m_pos ); for( int s = 0; s < m_nShrapnels; s++ ) { m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos, m_worldPointer->getRandom().getVector( 15 + 5*m_worldPointer->getRandom().getNormedReal() ), // + getSpeed(), otherwise shrapnels will just fly forward Particles::EXPL_EARTH | Particles::EXPL_OBJECT | Particles::DAMAGE | Particles::INVISIBLE, 0, m_owner, 2, m_damage, 0xffffff )); } // turn off guided missile mode const Player* pilotPlayer = m_worldPointer->getPlayerByID( m_owner ); Avatar* pilot = NULL; if( pilotPlayer ) pilot = pilotPlayer->getAvatar(); if( pilot ) pilot->setState( Avatar::MODE_GM, false ); m_removeMeAfterUpdate = true; } /**********************************************************/ void GuidedMissile::updateSpriteSelection( const bool activeEngine ) { // If the engine is running, face in the negative direction // of the thrust vector, otherwise in direction of movement. const real angle = (activeEngine ? m_angle : ATAN2_REAL( m_vel.y, m_vel.x )); // set the sequence index. // NOTE: setSequence also updates m_SequenceLength. setSequence( (ROUND( angle*(18.0/M_PI) - 0.5 )+36) % 36 ); // only reset the frame index, if we cannot continue // this movement in a different aiming angle if( getFrame() >= m_SequenceLength ) setFrame( 0 ); } /**********************************************************/ void GuidedMissile::serialize( Uint8*& bufferPointer ) const { // expands to a check of the buffer movement START_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer ); Missile::serialize( bufferPointer ); Serialize::serialize( m_angle, bufferPointer ); // expands to tag serialization SERIALIZE_OBJECT_TAG( bufferPointer ); // expands to a check of the buffer movement END_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer, GuidedMissile ); } /**********************************************************/ void GuidedMissile::deserialize( Uint8*& bufferPointer ) { Missile::deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, m_angle ); // expands to tag deserialization DESERIALIZE_OBJECT_TAG( bufferPointer ); } /**********************************************************/ Uint32 GuidedMissile::getSerializeBufferSize() const { return Missile::getSerializeBufferSize() + Serialize::sizeOf( m_angle ) // adds additional size for debugging (see serialize.hpp), // but only, if the tags are used PLUS_TAG_SIZE( 1 ); } /**********************************************************/