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