1 /* $Id: guidedmissile.cpp,v 1.9 2005/10/21 08:02:53 pohlt Exp $ */
2 
3 #include "guidedmissile.hpp"
4 #include "avatar.hpp"
5 #include "player.hpp"
6 #include "world.hpp"
7 #include "smoke.hpp"
8 
9 /**********************************************************/
10 
11 #define  ACCELERATE_FUEL         15.0
12 #define  STEER_FUEL             100.0
13 #define  CHASING_THRUST_IMPACT   75
14 #define  TURNING_SPEED           10.0
15 // dependent constants
16 #define  TOTAL_FUEL             (ACCELERATE_FUEL + STEER_FUEL)
17 
18 /**********************************************************/
19 
GuidedMissile()20 GuidedMissile::GuidedMissile()
21 {
22 //
23 // take the values from class Missile
24 //
25 	m_health = 200000;
26 	m_mass   = 100.0;
27 //
28 //	m_collRectX = -4;
29 //	m_collRectY = -4;
30 //
31 //	m_collRectWidth  = 9;
32 //	m_collRectHeight = 9;
33 //
34 }
35 
36 /**********************************************************/
37 
initialize(const Vector & pos,const Vector & vel,const Uint8 owner,const Sint32 nShrapnels,const Sint32 damage,const Vector & thrust)38 bool GuidedMissile::initialize( const Vector& pos,
39                                 const Vector& vel,
40                                 const Uint8   owner,
41                                 const Sint32  nShrapnels,
42                                 const Sint32  damage,
43                                 const Vector& thrust )
44 {
45 	m_angle = ATAN2_REAL( thrust.y, thrust.x );
46 
47 	if( Missile::initialize( pos, vel, owner, nShrapnels,
48 	                         damage, TOTAL_FUEL, thrust ) ) {
49 		Avatar* avatar = m_worldPointer->getPlayerByID( m_owner )->getAvatar();
50 
51 		 // turn on guided missile mode
52 		avatar->setState( Avatar::MODE_GM, true );
53 		setFocusPriority( avatar->getFocusPriority() + FOCUS_PRIO_GUIDED_MISSILE );
54 		m_worldPointer->scanObjectsForFocus();
55 		return true;
56 	}
57 	else return false;
58 }
59 
60 /**********************************************************/
61 
update()62 void GuidedMissile::update() {
63 	preBallistics();
64 
65 	bool detonate = false,
66 	     ignition = false;
67 	const Player* pilotPlayer = m_worldPointer->getPlayerByID( m_owner );
68 	Avatar* pilot = NULL;
69 	if( pilotPlayer ) pilot = pilotPlayer->getAvatar();
70 
71 	const real DT = m_worldPointer->getDT( m_pos );
72 
73 	if( m_accelTimer.getTimeToLive() > ( TOTAL_FUEL - ACCELERATE_FUEL ) ) {
74 		ignition = true;
75 	}
76 	else {
77 		if( pilot ) {
78 			pilot->setStateMask( ~0 );
79 			if( !m_accelTimer.isElapsed() ) {
80 				if( pilot->isMovingLeft()  ) m_angle -= TURNING_SPEED*M_PI/180.0;
81 				if( pilot->isMovingRight() ) m_angle += TURNING_SPEED*M_PI/180.0;
82 				if( m_angle >= 2.0*M_PI ) m_angle -= 2.0*M_PI;
83 				if( m_angle <  0.0      ) m_angle += 2.0*M_PI;
84 				if( pilot->isJumping()     ) {
85 					m_thrust.setAngle( m_angle, CHASING_THRUST_IMPACT );
86 					ignition = true;
87 				}
88 			}
89 			if( pilot->isShooting() || ! pilot->isLiving() ) detonate = true;
90 		}
91 		else detonate = true;
92 	}
93 
94 	// explode?
95 	if( m_health <= 0 || detonate ) {
96 		explode();
97 	}
98 	else {
99 		// engine is running
100 		if( ignition ) {
101 			// accelerate in direction of thrust
102 			m_force += m_thrust;
103 			if ( m_accelTimer.hasSignal() ) {
104 				// throw out some booster sparks
105 				int numParticles = static_cast<int>( 15 * DT );
106 				for( int i = 0; i < numParticles; i++ ) {
107 					m_worldPointer->getParticles()->addParticle( Particles::ParticleData(
108 						m_pos,
109 						m_vel - m_thrust*0.1 + m_worldPointer->getRandom().getVector( 1.5 ),
110 						Particles::BOUNCE_EARTH | //Particles::BOUNCE_OBJECT |
111 						Particles::REDUNDANT | Particles::SPARK, 2, 0,
112 						Particles::m_N_SPARK_COLORS/2 + m_worldPointer->getRandom().getUint32() % (Particles::m_N_SPARK_COLORS/2+1), 1,
113 						0xffffff));
114 				}
115 				// generate a smoke cloud
116 				Smoke* cloud = static_cast<Smoke*>(m_worldPointer->newObject(SMOKE_30));
117 				cloud->setPos( m_pos + localRnd.getVector( 4.0 ));
118 				cloud->setVel( m_vel + localRnd.getVector( 2.0 ));
119 				if( pilotPlayer ) cloud->setColor( pilotPlayer->getPlayerColor() );
120 			}
121 
122 			// decrement "fuel"
123 			m_accelTimer.countDown( DT );
124 		}
125 
126 		doBallistics();
127 
128 		// increment the animation frame
129 		if( ++m_Frame >= m_SequenceLength ) m_Frame = 0;
130 		// select new missile sprite
131 		updateSpriteSelection( !m_accelTimer.isElapsed() );
132 
133 		postBallistics();
134 	}
135 }
136 
137 /**********************************************************/
138 
explode()139 void GuidedMissile::explode() {
140 	Audio::getInstance()->playSound( m_explodeSample, m_pos );
141 	m_worldPointer->newObject( EXPLOSION_80 )->setPos( m_pos );
142 	for( int s = 0; s < m_nShrapnels; s++ ) {
143 		m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos,
144 		     m_worldPointer->getRandom().getVector( 15 + 5*m_worldPointer->getRandom().getNormedReal() ), // + getSpeed(),  otherwise shrapnels will just fly forward
145 		     Particles::EXPL_EARTH | Particles::EXPL_OBJECT | Particles::DAMAGE |
146 		     Particles::INVISIBLE,
147 		     0, m_owner, 2, m_damage, 0xffffff ));
148 	}
149 
150 	// turn off guided missile mode
151 	const Player* pilotPlayer = m_worldPointer->getPlayerByID( m_owner );
152 	Avatar* pilot = NULL;
153 	if( pilotPlayer ) pilot = pilotPlayer->getAvatar();
154 	if( pilot ) pilot->setState( Avatar::MODE_GM, false );
155 
156 	m_removeMeAfterUpdate = true;
157 }
158 
159 /**********************************************************/
160 
updateSpriteSelection(const bool activeEngine)161 void GuidedMissile::updateSpriteSelection( const bool activeEngine )
162 {
163 	// If the engine is running, face in the negative direction
164 	// of the thrust vector, otherwise in direction of movement.
165 	const real angle = (activeEngine
166 	                    ? m_angle
167 	                    : ATAN2_REAL( m_vel.y, m_vel.x ));
168 	// set the sequence index.
169 	// NOTE: setSequence also updates m_SequenceLength.
170 	setSequence( (ROUND( angle*(18.0/M_PI) - 0.5 )+36) % 36 );
171 
172 	// only reset the frame index, if we cannot continue
173 	// this movement in a different aiming angle
174 	if( getFrame() >= m_SequenceLength )  setFrame( 0 );
175 }
176 
177 /**********************************************************/
178 
serialize(Uint8 * & bufferPointer) const179 void GuidedMissile::serialize( Uint8*& bufferPointer ) const {
180 	// expands to a check of the buffer movement
181 	START_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer );
182 
183 	Missile::serialize( bufferPointer );
184 
185 	Serialize<real>::serialize( m_angle, bufferPointer );
186 
187 	// expands to tag serialization
188 	SERIALIZE_OBJECT_TAG( bufferPointer );
189 	// expands to a check of the buffer movement
190 	END_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer, GuidedMissile );
191 }
192 
193 /**********************************************************/
194 
deserialize(Uint8 * & bufferPointer)195 void GuidedMissile::deserialize( Uint8*& bufferPointer ) {
196 	Missile::deserialize( bufferPointer );
197 
198 	Serialize<real>::deserialize( bufferPointer, m_angle );
199 
200 	// expands to tag deserialization
201 	DESERIALIZE_OBJECT_TAG( bufferPointer );
202 }
203 
204 /**********************************************************/
205 
getSerializeBufferSize() const206 Uint32 GuidedMissile::getSerializeBufferSize() const {
207 	return   Missile::getSerializeBufferSize()
208 	       + Serialize<real>::sizeOf( m_angle )
209 	         // adds additional size for debugging (see serialize.hpp),
210 	         // but only, if the tags are used
211 	         PLUS_TAG_SIZE( 1 );
212 }
213 
214 /**********************************************************/
215