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