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