]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/afterburner.cpp
Initial revision
[taylor/freespace2.git] / src / ship / afterburner.cpp
1 /*
2  * $Logfile: /Freespace2/code/Ship/Afterburner.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C file for managing the afterburners
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 7     9/05/99 11:24p Jimb
15  * changed recharge rate for afterburners based on skill setting.
16  * 
17  * 6     8/24/99 1:50a Dave
18  * Fixed client-side afterburner stuttering. Added checkbox for no version
19  * checking on PXO join. Made button info passing more friendly between
20  * client and server.
21  * 
22  * 5     7/08/99 10:53a Dave
23  * New multiplayer interpolation scheme. Not 100% done yet, but still
24  * better than the old way.
25  * 
26  * 4     11/05/98 5:55p Dave
27  * Big pass at reducing #includes
28  * 
29  * 3     10/13/98 9:29a Dave
30  * Started neatening up freespace.h. Many variables renamed and
31  * reorganized. Added AlphaColors.[h,cpp]
32  * 
33  * 2     10/07/98 10:53a Dave
34  * Initial checkin.
35  * 
36  * 1     10/07/98 10:51a Dave
37  * 
38  * 25    5/23/98 2:41p Mike
39  * Make Easy the default skill level and prevent old pilot's skill level
40  * from carrying into new pilot.
41  * 
42  * 24    5/07/98 11:01a Lawrance
43  * Play afterburner fail sound once energy runs out (and key is still
44  * down)
45  * 
46  * 23    5/04/98 11:08p Hoffoss
47  * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
48  * Updated references everywhere to it.
49  * 
50  * 22    4/18/98 9:12p Lawrance
51  * Added Aureal support.
52  * 
53  * 21    4/13/98 2:11p Lawrance
54  * Change afterburners so they can't be activated any faster than once
55  * every 1.2 seconds
56  * 
57  * 20    4/07/98 1:53p Lawrance
58  * Make energy system matter more.
59  * 
60  * 19    3/31/98 5:18p John
61  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
62  * bunch of debug stuff out of player file.  Made model code be able to
63  * unload models and malloc out only however many models are needed.
64  *  
65  * 
66  * 18    2/26/98 10:07p Hoffoss
67  * Rewrote state saving and restoring to fix bugs and simplify the code.
68  * 
69  * 17    2/20/98 8:32p Lawrance
70  * Add radius parm to sound_play_3d()
71  * 
72  * 16    12/28/97 5:52p Lawrance
73  * fix volume discontinuity bug when afterburners ran out
74  * 
75  * 15    12/22/97 9:14p Allender
76  * fix up some code relating to afterburners in multiplayer.  Clients now
77  * control their own afterburners
78  * 
79  * 14    11/06/97 5:02p Dave
80  * Finished reworking standalone multiplayer sequencing. Added
81  * configurable observer-mode HUD
82  * 
83  * 13    10/17/97 9:49a Lawrance
84  * remove debug output
85  * 
86  * 12    10/16/97 5:37p Lawrance
87  * fix sound bug where AI afterburner was affecting player afterburner
88  * sounds
89  * 
90  * 11    10/01/97 5:55p Lawrance
91  * change call to snd_play_3d() to allow for arbitrary listening position
92  * 
93  * 10    9/16/97 2:27p Allender
94  * Removed unused Assert that will cause problems in the future
95  * 
96  * 9     8/11/97 9:50a Allender
97  * fixed afterburner snafu
98  * 
99  * 8     8/08/97 9:59a Allender
100  * added afterburner code into multiplayer.  Required adding a new physics
101  * info flag to indicate afterburners ready to fire. (needed for
102  * multiplayer).  Removed some now unused variables in afterburner.cpp
103  * 
104  * 7     8/05/97 10:48a Lawrance
105  * save afterburner data for the player
106  * 
107  * 6     7/27/97 5:14p Lawrance
108  * add afterburners to the player control info
109  * 
110  * 5     7/25/97 5:02p Lawrance
111  * fix bug with afterburner sound
112  * 
113  * 4     7/23/97 4:30p Lawrance
114  * improve how disengage of afterburner works
115  * 
116  * 3     7/16/97 4:42p Mike
117  * Make afterburner shake viewer, not ship.
118  * Shake for limited time.
119  * Add timestamp_until() function to timer library.
120  * 
121  * 2     7/11/97 8:57a Lawrance
122  * make afterburner work same for player and AI ships
123  * 
124  * 1     7/10/97 2:24p Lawrance
125  *
126  * $NoKeywords: $
127  */
128
129 #include "afterburner.h"
130 #include "key.h"
131 #include "joy.h"
132 #include "joy_ff.h"
133 #include "gamesnd.h"
134 #include "ship.h"
135 #include "controlsconfig.h"
136 #include "timer.h"
137 #include "systemvars.h"
138 #include "linklist.h"
139 #include "multimsgs.h"
140 #include "3d.h"                 // needed for View_position, which is used when playing a 3D sound
141 #include "hudets.h"
142 #include "freespace.h"
143 #include "multi.h"
144
145 // ----------------------------------------------------------
146 // Global to file
147 // ----------------------------------------------------------
148 static int              Player_afterburner_loop_id;             // identifies the looping afterburner sound of the player ship
149 static int              Player_afterburner_loop_delay;  // timestamp used to time the start of the looping afterburner sound
150 static int              Player_disengage_timer;
151 static float    Player_afterburner_vol;
152 static int              Player_afterburner_start_time;
153
154
155 // ----------------------------------------------------------
156 // local constants
157 // ----------------------------------------------------------
158
159 // The minimum required fuel to engage afterburners
160 #define MIN_AFTERBURNER_FUEL_TO_ENGAGE          10
161
162 #define AFTERBURNER_DEFAULT_VOL                                 0.5f    // default starting volume (0.0f -> 1.0f)
163 #define AFTERBURNER_PERCENT_VOL_ATTENUATE               0.30f   // % at which afterburner volume is reduced
164 #define AFTERBURNER_PERCENT_FOR_LOOP_SND                0.33f
165 #define AFTERBURNER_VOLUME_UPDATE                               250     // consider changing afterburner volume every 100 ms
166 #define AFTERBURNER_LOOP_DELAY                                  200     // ms after engage, to start looping sound
167
168 #define DISENGAGE_TIME                                                          1500    // time in ms to play faded loop sound when afterburner disengages
169
170
171 float   Skill_level_afterburner_recharge_scale[NUM_SKILL_LEVELS] = {5.0f, 3.0f, 2.0f, 1.5f, 1.0f};
172
173 // ----------------------------------------------------------------------------
174 // afterburner_level_init()
175 //          
176 //      call at the start of a mission
177 //
178 void afterburner_level_init()
179 {
180         Player_disengage_timer = 1;
181         Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
182         Player_afterburner_loop_id = -1;
183         Player_afterburner_start_time = 0;
184 }
185
186 // ----------------------------------------------------------------------------
187 // afterburners_start() will be called when a ship engages the afterburners.
188 // This function should only be called once when afterburners first start.  This is
189 // to start an appropriate sound effect and do any one-time initializations.
190 //
191 // parameters:   *objp        ==> pointer to the object starting afterburners
192 //          
193 void afterburners_start(object *objp)
194 {
195         ship_info       *sip;
196         ship                    *shipp;
197         float                   percent_left;
198
199         Assert( objp != NULL );
200
201         if(objp->type == OBJ_OBSERVER)
202                 return;
203
204         Assert( objp->type == OBJ_SHIP);
205         Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
206
207         if ( (objp->flags & OF_PLAYER_SHIP) && (objp == Player_obj) ) {
208                 int now;
209                 now = timer_get_milliseconds();
210
211                 if ( (now - Player_afterburner_start_time) < 1300 ) {
212                         snd_play( &Snds[SND_ABURN_FAIL] );
213                         return;
214                 }
215
216                 if ( objp->phys_info.flags & PF_AFTERBURNER_WAIT ){
217                         return;
218                 }
219         }
220
221         if ( objp->phys_info.flags & PF_AFTERBURNER_ON )        {
222                 return;         // afterburners are already engaged, nothing to do here
223         }
224
225         shipp = &Ships[objp->instance];
226         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES );
227         sip = &Ship_info[shipp->ship_info_index];
228         
229         if ( !(sip->flags & SIF_AFTERBURNER) )  {
230                 return;
231         }
232
233         // Check if there is enough afterburner fuel
234         if ( (shipp->afterburner_fuel < MIN_AFTERBURNER_FUEL_TO_ENGAGE) && !MULTIPLAYER_CLIENT ) {
235                 if ( objp == Player_obj ) {
236                         snd_play( &Snds[SND_ABURN_FAIL] );
237                 }
238                 return;
239         }
240
241         objp->phys_info.flags |= PF_AFTERBURNER_ON;
242
243         objp->phys_info.afterburner_decay = timestamp(ABURN_DECAY_TIME);
244
245         percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
246
247         if ( objp == Player_obj ) {
248                 Player_afterburner_start_time = timer_get_milliseconds();
249                 Player_disengage_timer = 1;
250                 Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
251
252                 if ( percent_left > AFTERBURNER_PERCENT_FOR_LOOP_SND ) {
253                         Player_afterburner_loop_delay = timestamp(AFTERBURNER_LOOP_DELAY);
254                 }
255                 else {
256                         Player_afterburner_loop_delay = 0;
257                 }
258
259                 snd_play( &Snds[SND_ABURN_ENGAGE], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
260                 joy_ff_afterburn_on();
261         } else {
262                 snd_play_3d( &Snds[SND_ABURN_ENGAGE], &objp->pos, &View_position, objp->radius );
263         }
264         
265         objp->phys_info.flags |= PF_AFTERBURNER_WAIT;
266 }
267
268 // ----------------------------------------------------------------------------
269 // afterburners_update()
270 //
271 //      Update the state of the afterburner fuel remaining for an object using the
272 //      afterburner.  
273 //
274 // for the player ship, key_up_time() is called for the afterburner key to
275 // detect when afterburners disengage.
276 //
277 // input:               *objp                           => pointer to the object starting afterburners
278 //                                      fl_frametime    => time in seconds of the last frame
279 //
280 void afterburners_update(object *objp, float fl_frametime)
281 {
282         Assert( objp != NULL );
283         Assert( objp->type == OBJ_SHIP );
284         Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
285         
286         ship_info *sip;
287         ship *shipp;
288         static int volume_chg_timer = 1;
289
290         shipp = &Ships[objp->instance];
291
292         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES );
293         sip = &Ship_info[shipp->ship_info_index];
294
295         if ( (objp->flags & OF_PLAYER_SHIP ) && (Game_mode & GM_DEAD) ) {
296                 return;
297         }
298
299         if ( !(sip->flags & SIF_AFTERBURNER) )  {
300                 return;         // nothing to update, afterburners are not even on the ship
301         }
302
303         if ( objp == Player_obj ) {
304                 if ( !timestamp_elapsed(Player_disengage_timer) ) {
305                         float remaining;
306                         remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME);
307                         if ( remaining <= 0 ) {
308                                 afterburner_stop_sounds();
309                         }
310                         else {
311                                 snd_set_volume( Player_afterburner_loop_id, remaining*Player_afterburner_vol);
312                         }
313                 }
314                 else {
315                         if ( Player_disengage_timer != 1 ) {
316                                 afterburner_stop_sounds();
317                         }
318                 }
319         }
320
321         // single player, multiplayer servers, and clients for their own ships
322         if(!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER || (objp == Player_obj)){
323                 if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) {
324                         // Recover afterburner fuel
325                 
326                         if ( shipp->afterburner_fuel < sip->afterburner_fuel_capacity ) {
327                                 float recharge_scale;
328                                 recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * Skill_level_afterburner_recharge_scale[Game_skill_level];
329                                 shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale);
330
331                                 if ( shipp->afterburner_fuel >  sip->afterburner_fuel_capacity){
332                                         shipp->afterburner_fuel = sip->afterburner_fuel_capacity;
333                                 }
334                         }
335                         return;
336                 }
337                 else {
338                         // Check if there is enough afterburner fuel
339                         if ( shipp->afterburner_fuel <= 0 ) {
340                                 shipp->afterburner_fuel = 0.0f;
341                                 afterburners_stop(objp);
342                                 return;
343                         }
344                 }
345
346                 // afterburns are firing at this point
347
348                 // Reduce the afterburner fuel
349                 shipp->afterburner_fuel -= (sip->afterburner_burn_rate * fl_frametime);
350                 if ( shipp->afterburner_fuel < 0.0f ) {
351                         shipp->afterburner_fuel = 0.0f;
352                 }
353         }
354
355         if ( objp == Player_obj ) {
356                 if ( timestamp_elapsed(Player_afterburner_loop_delay) ) {
357                         Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
358                         Player_afterburner_loop_delay = 0;
359                         if ( Player_afterburner_loop_id == -1 ) {
360                                 Player_afterburner_loop_id = snd_play_looping( &Snds[SND_ABURN_LOOP], 0.0f , -1, -1);
361                                 snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol);
362 //                              nprintf(("Alan","PLAY LOOPING SOUND\n"));
363                         }
364                 }
365
366                 // Reduce the volume of the afterburner sound if near the end
367                 if ( timestamp_elapsed(volume_chg_timer) ) {
368                         float percent_afterburner_left;
369                         percent_afterburner_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
370                         volume_chg_timer = timestamp(AFTERBURNER_VOLUME_UPDATE);
371                         if ( percent_afterburner_left < AFTERBURNER_PERCENT_VOL_ATTENUATE ) {
372                                 Player_afterburner_vol = percent_afterburner_left*(1/AFTERBURNER_PERCENT_VOL_ATTENUATE)*AFTERBURNER_DEFAULT_VOL;
373                                 snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol);
374                         }
375                 }       // end if (timestamp_elapsed(volume_chg_timer))
376         }
377 }
378
379 // ----------------------------------------------------------------------------
380 // afterburners_stop() will be called when a ship disengages the afterburners.
381 //
382 // parameters:   *objp                          => pointer to the object starting afterburners
383 //                                              key_released    =>      OPTIONAL parameter (default value 0)
384 //                                                                                              This is only used for the player object, to
385 //                                                                                              manage starting/stopping
386 //
387 void afterburners_stop(object *objp, int key_released)
388 {
389         Assert( objp != NULL );
390         Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
391         
392         ship_info *sip;
393         ship *shipp;
394
395         shipp = &Ships[objp->instance];
396
397         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES );
398         sip = &Ship_info[shipp->ship_info_index];
399
400         if ( (objp->flags & OF_PLAYER_SHIP) && key_released ) {
401                 objp->phys_info.flags &= ~PF_AFTERBURNER_WAIT;
402         }
403
404         if ( !(sip->flags & SIF_AFTERBURNER) )  {
405                 nprintf(("Warning","Ship type %s does not have afterburner capability\n", sip->name));
406                 return;
407         }
408
409         if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) {
410                 return;
411         }
412
413         objp->phys_info.flags &= ~PF_AFTERBURNER_ON;
414         float percent_left;
415         percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
416
417         if ( objp == Player_obj ) {
418
419                 if ( !key_released ) {
420                         snd_play( &Snds[SND_ABURN_FAIL] );
421                 }
422
423                 if ( Player_afterburner_loop_id > -1 )  {
424                         Player_disengage_timer = timestamp(DISENGAGE_TIME);
425                 }
426
427                 joy_ff_afterburn_off();
428         }
429 }
430
431 // ----------------------------------------------------------------------------
432 // afterburner_stop_sounds() 
433 //
434 // Terminates any looping afterburner sounds.
435 // This should only be called when the game decides to stop all looping sounds.
436 //
437 void afterburner_stop_sounds()
438 {
439         if ( Player_afterburner_loop_id != -1 ) {
440                 snd_stop(Player_afterburner_loop_id);
441 //              nprintf(("Alan","STOP LOOPING SOUND\n"));
442         }
443
444         Player_afterburner_loop_id = -1;
445         Player_disengage_timer = 1;
446         Player_afterburner_loop_delay = 0;
447 }