1 /* $Id: homingmissile.cpp,v 1.23.4.1 2006/01/20 11:33:52 chfreund Exp $ */
2
3 #include "homingmissile.hpp"
4 #include "avatar.hpp"
5 #include "player.hpp"
6 #include "world.hpp"
7 #include "smoke.hpp"
8
9 /**********************************************************/
10
11 #define ACCELERATE_FUEL 10.0
12 #define MIN_ACCELERATION 5
13 #define CHASE_FUEL 100.0
14 #define CHASING_THRUST_IMPACT 100.0
15 #define RANGE_OF_SIGHT 220.0
16 #define SPEED_PREDICTION 3.0
17 // dependent constants
18 #define TOTAL_FUEL (ACCELERATE_FUEL + CHASE_FUEL)
19 #define RANGE_OF_SIGHT_SQUARE (RANGE_OF_SIGHT * RANGE_OF_SIGHT)
20
21 // "nearly 1.0". Since the accumulated time is incremented in each
22 // call of update, there might be some roundoff errors. To make sure
23 // that the condition "if(accumulatedDT >= DELTA_T_PER_ANIMATION_STEP)"
24 // is true whenever it should be true, we use 0.99 instead of 1.0
25 #define DELTA_T_PER_ANIMATION_STEP 0.99
26
27 /**********************************************************/
28
HomingMissile()29 HomingMissile::HomingMissile()
30 {
31 //
32 // take the values from class Missile
33 //
34 // m_health = 300000;
35 m_mass = 100.0;
36 //
37 // m_collRectX = -4;
38 // m_collRectY = -4;
39 //
40 // m_collRectWidth = 9;
41 // m_collRectHeight = 9;
42 //
43 }
44
45 /**********************************************************/
46
initialize(const Vector & pos,const Vector & vel,const Uint8 owner,const Sint32 nShrapnels,const Sint32 damage,const Vector & thrust)47 bool HomingMissile::initialize( const Vector& pos,
48 const Vector& vel,
49 const Uint8 owner,
50 const Sint32 nShrapnels,
51 const Sint32 damage,
52 const Vector& thrust )
53 {
54 return
55 Missile::initialize( pos, vel, owner, nShrapnels,
56 damage, TOTAL_FUEL, thrust );
57 }
58
59 /**********************************************************/
60
update()61 void HomingMissile::update()
62 {
63 preBallistics();
64
65 if( m_health <= 0 ) {
66 explode();
67 }
68 else {
69 // check for prey
70 const std::vector<Player*>& players = m_worldPointer->getPlayerList();
71 const Avatar* prey = NULL;
72 real minDist2 = RANGE_OF_SIGHT_SQUARE;
73 // only check for prey, if we could chase it
74 if( !m_accelTimer.isElapsed() ) {
75 for( Uint8 p = 0; p < players.size(); p++ ) {
76 // do not chase the missile's owner
77 if( m_owner != players[p]->getPlayerID() ) {
78 const Avatar* avatar = players[p]->getAvatar();
79 if( avatar->isLiving() && avatar->isWeaponVisible() ) {
80 const real dist2 = (avatar->getPos() - m_pos).abs2();
81 // search the nearest avatar
82 // NOTE that due to the initialization of minDistSquare
83 // distsquare will also be < RANGE_OF_SIGHT_SQUARE.
84 if( dist2 < minDist2 ) {
85 minDist2 = dist2;
86 prey = avatar;
87 }
88 }
89 }
90 }
91 }
92
93 bool ignition = false;
94 // we found a prey
95 if( prey != NULL ) {
96 // if we still have fuel AND
97 if( !m_accelTimer.isElapsed() ) {
98 // we are not in the initial start phase
99 if( m_accelTimer.getTimeToLive() < (TOTAL_FUEL - MIN_ACCELERATION) ) {
100 // turn thrust
101 m_thrust = prey->getPos();
102 m_thrust += prey->getVel() * SPEED_PREDICTION;
103 m_thrust -= m_pos;
104 m_thrust -= m_vel * SPEED_PREDICTION;
105 m_thrust.normalize() *= CHASING_THRUST_IMPACT;
106 // ignition towards the prey
107 ignition = true;
108 } else {
109 // ignition in the initial start phase
110 ignition = true;
111 prey = NULL;
112 }
113 }
114 // we did not find a prey
115 } else {
116 // we are still in the neutral acceleration phase
117 if( m_accelTimer.getTimeToLive() > CHASE_FUEL ) {
118 ignition = true;
119 }
120 }
121
122 // engine is running
123 if( ignition ) {
124 // accelerate in direction of thrust
125 m_force += m_thrust;
126 if ( m_accelTimer.hasSignal() ) {
127 // throw out some booster sparks
128 for( int i = 0; i < 15; i++ ) {
129 m_worldPointer->getParticles()->addParticle( Particles::ParticleData(
130 m_pos,
131 m_vel - m_thrust*0.1 + m_worldPointer->getRandom().getVector( 1.5 ),
132 Particles::BOUNCE_EARTH | //Particles::BOUNCE_OBJECT |
133 Particles::REDUNDANT | Particles::SPARK, 2, 0,
134 Particles::m_N_SPARK_COLORS/2 + m_worldPointer->getRandom().getUint32() % (Particles::m_N_SPARK_COLORS/2+1), 1,
135 0xffffff));
136 }
137 // generate a smoke cloud
138 Smoke* cloud = static_cast<Smoke*>(m_worldPointer->newObject(SMOKE_30));
139 cloud->setPos( m_pos + localRnd.getVector( 4.0 ));
140 cloud->setVel( m_vel + localRnd.getVector( 2.0 ));
141 // color the cloud, if we chase someone
142 if( prey != NULL ) {
143 cloud->setColor( prey->getPlayerColor() );
144 // lock the avatar (maybe it has got some authomatic defense)
145 ((Avatar*)prey)->getLockedByHomingMissile( this, minDist2 );
146 }
147 }
148 // decrement "fuel"
149 m_accelTimer.countDown( m_worldPointer->getDT( m_pos ) );
150 }
151
152 doBallistics();
153
154 // eventually increment the animation frame
155 m_AccumulatedDT += m_worldPointer->getDT( m_pos );
156 if( m_AccumulatedDT >= DELTA_T_PER_ANIMATION_STEP ) {
157 m_AccumulatedDT = 0.0;
158 if( ++m_Frame >= m_SequenceLength ) m_Frame = 0;
159 }
160 // select new missile sprite
161 updateSpriteSelection( ignition );
162
163 postBallistics();
164 }
165 }
166
167 /**********************************************************/
168
explode()169 void HomingMissile::explode() {
170 Audio::getInstance()->playSound( m_explodeSample, m_pos );
171 m_worldPointer->newObject( EXPLOSION_80 )->setPos( m_pos );
172 for( int s = 0; s < m_nShrapnels; s++ ) {
173 m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos,
174 m_worldPointer->getRandom().getVector( 15 + 5*m_worldPointer->getRandom().getNormedReal() ), // + getSpeed(), otherwise shrapnels will just fly forward
175 Particles::EXPL_EARTH | Particles::EXPL_OBJECT | Particles::DAMAGE |
176 Particles::INVISIBLE,
177 0, m_owner, 2, m_damage, 0xffffff ));
178 }
179
180 m_removeMeAfterUpdate = true;
181 }
182
183 /**********************************************************/
184
updateSpriteSelection(const bool activeEngine)185 void HomingMissile::updateSpriteSelection( const bool activeEngine )
186 {
187 // If the engine is running, face in the negative direction
188 // of the thrust vector, otherwise in direction of movement.
189 const real angle = (activeEngine
190 ? ATAN2_REAL( m_thrust.y, m_thrust.x )
191 : ATAN2_REAL( m_vel.y, m_vel.x ));
192 // set the sequence index.
193 // NOTE: setSequence also updates m_SequenceLength.
194 setSequence( (ROUND( angle*(18.0/M_PI) - 0.5 )+36) % 36 );
195
196 // only reset the frame index, if we cannot continue
197 // this movement in a different aiming angle
198 if( getFrame() >= m_SequenceLength ) setFrame( 0 );
199 }
200
201 /**********************************************************/
202
dump(std::ostream & out) const203 void HomingMissile::dump( std::ostream& out ) const {
204 using namespace std;
205 out << "### HomingMissile object ###" << endl;
206 Missile::dump( out );
207 }
208