2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Ship/Afterburner.cpp $
15 * C file for managing the afterburners
18 * Revision 1.3 2002/06/09 04:41:25 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:51 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 7 9/05/99 11:24p Jimb
29 * changed recharge rate for afterburners based on skill setting.
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
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.
40 * 4 11/05/98 5:55p Dave
41 * Big pass at reducing #includes
43 * 3 10/13/98 9:29a Dave
44 * Started neatening up freespace.h. Many variables renamed and
45 * reorganized. Added AlphaColors.[h,cpp]
47 * 2 10/07/98 10:53a Dave
50 * 1 10/07/98 10:51a Dave
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.
56 * 24 5/07/98 11:01a Lawrance
57 * Play afterburner fail sound once energy runs out (and key is still
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.
64 * 22 4/18/98 9:12p Lawrance
65 * Added Aureal support.
67 * 21 4/13/98 2:11p Lawrance
68 * Change afterburners so they can't be activated any faster than once
71 * 20 4/07/98 1:53p Lawrance
72 * Make energy system matter more.
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.
80 * 18 2/26/98 10:07p Hoffoss
81 * Rewrote state saving and restoring to fix bugs and simplify the code.
83 * 17 2/20/98 8:32p Lawrance
84 * Add radius parm to sound_play_3d()
86 * 16 12/28/97 5:52p Lawrance
87 * fix volume discontinuity bug when afterburners ran out
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
93 * 14 11/06/97 5:02p Dave
94 * Finished reworking standalone multiplayer sequencing. Added
95 * configurable observer-mode HUD
97 * 13 10/17/97 9:49a Lawrance
100 * 12 10/16/97 5:37p Lawrance
101 * fix sound bug where AI afterburner was affecting player afterburner
104 * 11 10/01/97 5:55p Lawrance
105 * change call to snd_play_3d() to allow for arbitrary listening position
107 * 10 9/16/97 2:27p Allender
108 * Removed unused SDL_assert that will cause problems in the future
110 * 9 8/11/97 9:50a Allender
111 * fixed afterburner snafu
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
118 * 7 8/05/97 10:48a Lawrance
119 * save afterburner data for the player
121 * 6 7/27/97 5:14p Lawrance
122 * add afterburners to the player control info
124 * 5 7/25/97 5:02p Lawrance
125 * fix bug with afterburner sound
127 * 4 7/23/97 4:30p Lawrance
128 * improve how disengage of afterburner works
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.
135 * 2 7/11/97 8:57a Lawrance
136 * make afterburner work same for player and AI ships
138 * 1 7/10/97 2:24p Lawrance
143 #include "afterburner.h"
149 #include "controlsconfig.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
156 #include "freespace.h"
159 // ----------------------------------------------------------
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;
169 // ----------------------------------------------------------
171 // ----------------------------------------------------------
173 // The minimum required fuel to engage afterburners
174 #define MIN_AFTERBURNER_FUEL_TO_ENGAGE 10
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
182 #define DISENGAGE_TIME 1500 // time in ms to play faded loop sound when afterburner disengages
185 float Skill_level_afterburner_recharge_scale[NUM_SKILL_LEVELS] = {5.0f, 3.0f, 2.0f, 1.5f, 1.0f};
187 // ----------------------------------------------------------------------------
188 // afterburner_level_init()
190 // call at the start of a mission
192 void afterburner_level_init()
194 Player_disengage_timer = 1;
195 Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
196 Player_afterburner_loop_id = -1;
197 Player_afterburner_start_time = 0;
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.
205 // parameters: *objp ==> pointer to the object starting afterburners
207 void afterburners_start(object *objp)
213 SDL_assert( objp != NULL );
215 if(objp->type == OBJ_OBSERVER)
218 SDL_assert( objp->type == OBJ_SHIP);
219 SDL_assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
221 if ( (objp->flags & OF_PLAYER_SHIP) && (objp == Player_obj) ) {
223 now = timer_get_milliseconds();
225 if ( (now - Player_afterburner_start_time) < 1300 ) {
226 snd_play( &Snds[SND_ABURN_FAIL] );
230 if ( objp->phys_info.flags & PF_AFTERBURNER_WAIT ){
235 if ( objp->phys_info.flags & PF_AFTERBURNER_ON ) {
236 return; // afterburners are already engaged, nothing to do here
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];
243 if ( !(sip->flags & SIF_AFTERBURNER) ) {
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] );
255 objp->phys_info.flags |= PF_AFTERBURNER_ON;
257 objp->phys_info.afterburner_decay = timestamp(ABURN_DECAY_TIME);
259 percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
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;
266 if ( percent_left > AFTERBURNER_PERCENT_FOR_LOOP_SND ) {
267 Player_afterburner_loop_delay = timestamp(AFTERBURNER_LOOP_DELAY);
270 Player_afterburner_loop_delay = 0;
273 snd_play( &Snds[SND_ABURN_ENGAGE], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
274 joy_ff_afterburn_on();
276 snd_play_3d( &Snds[SND_ABURN_ENGAGE], &objp->pos, &View_position, objp->radius );
279 objp->phys_info.flags |= PF_AFTERBURNER_WAIT;
282 // ----------------------------------------------------------------------------
283 // afterburners_update()
285 // Update the state of the afterburner fuel remaining for an object using the
288 // for the player ship, key_up_time() is called for the afterburner key to
289 // detect when afterburners disengage.
291 // input: *objp => pointer to the object starting afterburners
292 // fl_frametime => time in seconds of the last frame
294 void afterburners_update(object *objp, float fl_frametime)
296 SDL_assert( objp != NULL );
297 SDL_assert( objp->type == OBJ_SHIP );
298 SDL_assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
302 static int volume_chg_timer = 1;
304 shipp = &Ships[objp->instance];
306 SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES );
307 sip = &Ship_info[shipp->ship_info_index];
309 if ( (objp->flags & OF_PLAYER_SHIP ) && (Game_mode & GM_DEAD) ) {
313 if ( !(sip->flags & SIF_AFTERBURNER) ) {
314 return; // nothing to update, afterburners are not even on the ship
317 if ( objp == Player_obj ) {
318 if ( !timestamp_elapsed(Player_disengage_timer) ) {
320 remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME);
321 if ( remaining <= 0 ) {
322 afterburner_stop_sounds();
325 snd_set_volume( Player_afterburner_loop_id, remaining*Player_afterburner_vol);
329 if ( Player_disengage_timer != 1 ) {
330 afterburner_stop_sounds();
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
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);
345 if ( shipp->afterburner_fuel > sip->afterburner_fuel_capacity){
346 shipp->afterburner_fuel = sip->afterburner_fuel_capacity;
352 // Check if there is enough afterburner fuel
353 if ( shipp->afterburner_fuel <= 0 ) {
354 shipp->afterburner_fuel = 0.0f;
355 afterburners_stop(objp);
360 // afterburns are firing at this point
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;
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"));
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);
389 } // end if (timestamp_elapsed(volume_chg_timer))
393 // ----------------------------------------------------------------------------
394 // afterburners_stop() will be called when a ship disengages the afterburners.
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
401 void afterburners_stop(object *objp, int key_released)
403 SDL_assert( objp != NULL );
404 SDL_assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
409 shipp = &Ships[objp->instance];
411 SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES );
412 sip = &Ship_info[shipp->ship_info_index];
414 if ( (objp->flags & OF_PLAYER_SHIP) && key_released ) {
415 objp->phys_info.flags &= ~PF_AFTERBURNER_WAIT;
418 if ( !(sip->flags & SIF_AFTERBURNER) ) {
419 nprintf(("Warning","Ship type %s does not have afterburner capability\n", sip->name));
423 if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) {
427 objp->phys_info.flags &= ~PF_AFTERBURNER_ON;
429 percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
431 if ( objp == Player_obj ) {
433 if ( !key_released ) {
434 snd_play( &Snds[SND_ABURN_FAIL] );
437 if ( Player_afterburner_loop_id > -1 ) {
438 Player_disengage_timer = timestamp(DISENGAGE_TIME);
441 joy_ff_afterburn_off();
445 // ----------------------------------------------------------------------------
446 // afterburner_stop_sounds()
448 // Terminates any looping afterburner sounds.
449 // This should only be called when the game decides to stop all looping sounds.
451 void afterburner_stop_sounds()
453 if ( Player_afterburner_loop_id != -1 ) {
454 snd_stop(Player_afterburner_loop_id);
455 // nprintf(("Alan","STOP LOOPING SOUND\n"));
458 Player_afterburner_loop_id = -1;
459 Player_disengage_timer = 1;
460 Player_afterburner_loop_delay = 0;