mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
546 lines
17 KiB
C++
546 lines
17 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* $Logfile: /Freespace2/code/Ship/Afterburner.cpp $
|
|
* $Revision: 2.22 $
|
|
* $Date: 2006-11-06 06:36:44 $
|
|
* $Author: taylor $
|
|
*
|
|
* C file for managing the afterburners
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 2.21 2006/03/18 20:24:54 wmcoolmon
|
|
* Nitpicketiness
|
|
*
|
|
* Revision 2.20 2005/12/29 08:08:42 wmcoolmon
|
|
* Codebase commit, most notably including objecttypes.tbl
|
|
*
|
|
* Revision 2.19 2005/11/21 02:43:30 Goober5000
|
|
* change from "setting" to "profile"; this way makes more sense
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.18 2005/11/21 00:46:05 Goober5000
|
|
* add ai_settings.tbl
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.17 2005/10/10 17:21:10 taylor
|
|
* remove NO_NETWORK
|
|
*
|
|
* Revision 2.16 2005/07/22 10:18:35 Goober5000
|
|
* CVS header tweaks
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.15 2005/07/16 04:41:04 Goober5000
|
|
* cleanup and clarification of phreak's fix of a bug propagated by me and penguin
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.14 2005/07/13 03:35:30 Goober5000
|
|
* remove PreProcDefine #includes in FS2
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.13 2005/07/05 17:04:08 phreak
|
|
* fixed an afterburner bug where AI ships would have infinite AB fuel if NO_NETWORK
|
|
* was defined.
|
|
*
|
|
* Revision 2.12 2005/04/25 00:31:14 wmcoolmon
|
|
* Dynamically allocated engine washes; subsystem sounds; armor fixes. Line 4268 of ship.cpp, apparently, works properly; bears further looking into.
|
|
*
|
|
* Revision 2.11 2005/03/02 21:24:46 taylor
|
|
* more network/inferno goodness for Windows, takes care of a few warnings too
|
|
*
|
|
* Revision 2.10 2004/07/26 20:47:50 Kazan
|
|
* remove MCD complete
|
|
*
|
|
* Revision 2.9 2004/07/12 16:33:04 Kazan
|
|
* MCD - define _MCD_CHECK to use memory tracking
|
|
*
|
|
* Revision 2.8 2004/03/05 09:01:51 Goober5000
|
|
* Uber pass at reducing #includes
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.7 2004/01/31 03:56:46 phreak
|
|
* changed "now" to unsigned int
|
|
*
|
|
* Revision 2.6 2003/11/23 00:59:01 Goober5000
|
|
* fixed a bug introduced by Penguin for running with NO_NETWORK defined
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.5 2003/09/13 06:02:03 Goober5000
|
|
* clean rollback of all of argv's stuff
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.3 2003/08/21 06:11:32 Goober5000
|
|
* removed an extraneous thingy
|
|
* --Goober5000
|
|
*
|
|
* Revision 2.2 2003/08/06 17:37:08 phreak
|
|
* preliminary work on tertiary weapons. it doesn't really function yet, but i want to get something committed
|
|
*
|
|
* Revision 2.1 2002/08/01 01:41:09 penguin
|
|
* The big include file move
|
|
*
|
|
* Revision 2.0 2002/06/03 04:02:28 penguin
|
|
* Warpcore CVS sync
|
|
*
|
|
* Revision 1.2 2002/05/13 21:09:28 mharris
|
|
* I think the last of the networking code has ifndef NO_NETWORK...
|
|
*
|
|
* Revision 1.1 2002/05/02 18:03:12 mharris
|
|
* Initial checkin - converted filenames and includes to lower case
|
|
*
|
|
*
|
|
* 7 9/05/99 11:24p Jimb
|
|
* changed recharge rate for afterburners based on skill setting.
|
|
*
|
|
* 6 8/24/99 1:50a Dave
|
|
* Fixed client-side afterburner stuttering. Added checkbox for no version
|
|
* checking on PXO join. Made button info passing more friendly between
|
|
* client and server.
|
|
*
|
|
* 5 7/08/99 10:53a Dave
|
|
* New multiplayer interpolation scheme. Not 100% done yet, but still
|
|
* better than the old way.
|
|
*
|
|
* 4 11/05/98 5:55p Dave
|
|
* Big pass at reducing #includes
|
|
*
|
|
* 3 10/13/98 9:29a Dave
|
|
* Started neatening up freespace.h. Many variables renamed and
|
|
* reorganized. Added AlphaColors.[h,cpp]
|
|
*
|
|
* 2 10/07/98 10:53a Dave
|
|
* Initial checkin.
|
|
*
|
|
* 1 10/07/98 10:51a Dave
|
|
*
|
|
* 25 5/23/98 2:41p Mike
|
|
* Make Easy the default skill level and prevent old pilot's skill level
|
|
* from carrying into new pilot.
|
|
*
|
|
* 24 5/07/98 11:01a Lawrance
|
|
* Play afterburner fail sound once energy runs out (and key is still
|
|
* down)
|
|
*
|
|
* 23 5/04/98 11:08p Hoffoss
|
|
* Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
|
|
* Updated references everywhere to it.
|
|
*
|
|
* 22 4/18/98 9:12p Lawrance
|
|
* Added Aureal support.
|
|
*
|
|
* 21 4/13/98 2:11p Lawrance
|
|
* Change afterburners so they can't be activated any faster than once
|
|
* every 1.2 seconds
|
|
*
|
|
* 20 4/07/98 1:53p Lawrance
|
|
* Make energy system matter more.
|
|
*
|
|
* 19 3/31/98 5:18p John
|
|
* Removed demo/save/restore. Made NDEBUG defined compile. Removed a
|
|
* bunch of debug stuff out of player file. Made model code be able to
|
|
* unload models and malloc out only however many models are needed.
|
|
*
|
|
*
|
|
* 18 2/26/98 10:07p Hoffoss
|
|
* Rewrote state saving and restoring to fix bugs and simplify the code.
|
|
*
|
|
* 17 2/20/98 8:32p Lawrance
|
|
* Add radius parm to sound_play_3d()
|
|
*
|
|
* 16 12/28/97 5:52p Lawrance
|
|
* fix volume discontinuity bug when afterburners ran out
|
|
*
|
|
* 15 12/22/97 9:14p Allender
|
|
* fix up some code relating to afterburners in multiplayer. Clients now
|
|
* control their own afterburners
|
|
*
|
|
* 14 11/06/97 5:02p Dave
|
|
* Finished reworking standalone multiplayer sequencing. Added
|
|
* configurable observer-mode HUD
|
|
*
|
|
* 13 10/17/97 9:49a Lawrance
|
|
* remove debug output
|
|
*
|
|
* 12 10/16/97 5:37p Lawrance
|
|
* fix sound bug where AI afterburner was affecting player afterburner
|
|
* sounds
|
|
*
|
|
* 11 10/01/97 5:55p Lawrance
|
|
* change call to snd_play_3d() to allow for arbitrary listening position
|
|
*
|
|
* 10 9/16/97 2:27p Allender
|
|
* Removed unused Assert that will cause problems in the future
|
|
*
|
|
* 9 8/11/97 9:50a Allender
|
|
* fixed afterburner snafu
|
|
*
|
|
* 8 8/08/97 9:59a Allender
|
|
* added afterburner code into multiplayer. Required adding a new physics
|
|
* info flag to indicate afterburners ready to fire. (needed for
|
|
* multiplayer). Removed some now unused variables in afterburner.cpp
|
|
*
|
|
* 7 8/05/97 10:48a Lawrance
|
|
* save afterburner data for the player
|
|
*
|
|
* 6 7/27/97 5:14p Lawrance
|
|
* add afterburners to the player control info
|
|
*
|
|
* 5 7/25/97 5:02p Lawrance
|
|
* fix bug with afterburner sound
|
|
*
|
|
* 4 7/23/97 4:30p Lawrance
|
|
* improve how disengage of afterburner works
|
|
*
|
|
* 3 7/16/97 4:42p Mike
|
|
* Make afterburner shake viewer, not ship.
|
|
* Shake for limited time.
|
|
* Add timestamp_until() function to timer library.
|
|
*
|
|
* 2 7/11/97 8:57a Lawrance
|
|
* make afterburner work same for player and AI ships
|
|
*
|
|
* 1 7/10/97 2:24p Lawrance
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
|
|
#include "ship/afterburner.h"
|
|
#include "io/joy_ff.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "ship/ship.h"
|
|
#include "object/object.h"
|
|
#include "io/timer.h"
|
|
#include "render/3d.h" // needed for View_position, which is used when playing a 3D sound
|
|
#include "hud/hudets.h"
|
|
#include "freespace2/freespace.h"
|
|
#include "network/multi.h"
|
|
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
// Global to file
|
|
// ----------------------------------------------------------
|
|
static int Player_afterburner_loop_id; // identifies the looping afterburner sound of the player ship
|
|
static int Player_afterburner_loop_delay; // timestamp used to time the start of the looping afterburner sound
|
|
static int Player_disengage_timer;
|
|
static float Player_afterburner_vol;
|
|
static int Player_afterburner_start_time;
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
// local constants
|
|
// ----------------------------------------------------------
|
|
|
|
// The minimum required fuel to engage afterburners
|
|
#define MIN_AFTERBURNER_FUEL_TO_ENGAGE 10
|
|
|
|
#define AFTERBURNER_DEFAULT_VOL 0.5f // default starting volume (0.0f -> 1.0f)
|
|
#define AFTERBURNER_PERCENT_VOL_ATTENUATE 0.30f // % at which afterburner volume is reduced
|
|
#define AFTERBURNER_PERCENT_FOR_LOOP_SND 0.33f
|
|
#define AFTERBURNER_VOLUME_UPDATE 250 // consider changing afterburner volume every 100 ms
|
|
#define AFTERBURNER_LOOP_DELAY 200 // ms after engage, to start looping sound
|
|
|
|
#define DISENGAGE_TIME 1500 // time in ms to play faded loop sound when afterburner disengages
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// afterburner_level_init()
|
|
//
|
|
// call at the start of a mission
|
|
//
|
|
void afterburner_level_init()
|
|
{
|
|
Player_disengage_timer = 1;
|
|
Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
|
|
Player_afterburner_loop_id = -1;
|
|
Player_afterburner_start_time = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// afterburners_start() will be called when a ship engages the afterburners.
|
|
// This function should only be called once when afterburners first start. This is
|
|
// to start an appropriate sound effect and do any one-time initializations.
|
|
//
|
|
// parameters: *objp ==> pointer to the object starting afterburners
|
|
//
|
|
void afterburners_start(object *objp)
|
|
{
|
|
ship_info *sip;
|
|
ship *shipp;
|
|
float percent_left;
|
|
|
|
Assert( objp != NULL );
|
|
|
|
if(objp->type == OBJ_OBSERVER)
|
|
return;
|
|
|
|
Assert( objp->type == OBJ_SHIP);
|
|
Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
|
|
|
|
if ( (objp->flags & OF_PLAYER_SHIP) && (objp == Player_obj) ) {
|
|
unsigned int now;
|
|
now = timer_get_milliseconds();
|
|
|
|
if ( (now - Player_afterburner_start_time) < 1300 ) {
|
|
snd_play( &Snds[SND_ABURN_FAIL] );
|
|
return;
|
|
}
|
|
|
|
if ( objp->phys_info.flags & PF_AFTERBURNER_WAIT ){
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( objp->phys_info.flags & PF_AFTERBURNER_ON ) {
|
|
return; // afterburners are already engaged, nothing to do here
|
|
}
|
|
|
|
//boosters take precedence
|
|
if (objp->phys_info.flags & PF_BOOSTER_ON)
|
|
return;
|
|
|
|
shipp = &Ships[objp->instance];
|
|
Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < Num_ship_classes );
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
if ( !(sip->flags & SIF_AFTERBURNER) ) {
|
|
return;
|
|
}
|
|
|
|
// Check if there is enough afterburner fuel
|
|
if ( (shipp->afterburner_fuel < MIN_AFTERBURNER_FUEL_TO_ENGAGE) && !MULTIPLAYER_CLIENT ) {
|
|
if ( objp == Player_obj ) {
|
|
snd_play( &Snds[SND_ABURN_FAIL] );
|
|
}
|
|
return;
|
|
}
|
|
|
|
objp->phys_info.flags |= PF_AFTERBURNER_ON;
|
|
|
|
objp->phys_info.afterburner_decay = timestamp(ABURN_DECAY_TIME);
|
|
|
|
percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
|
|
|
|
//Do anim
|
|
model_anim_start_type(shipp, TRIGGER_TYPE_AFTERBURNER, ANIMATION_SUBTYPE_ALL, 1);
|
|
|
|
if ( objp == Player_obj ) {
|
|
Player_afterburner_start_time = timer_get_milliseconds();
|
|
Player_disengage_timer = 1;
|
|
Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
|
|
|
|
if ( percent_left > AFTERBURNER_PERCENT_FOR_LOOP_SND ) {
|
|
Player_afterburner_loop_delay = timestamp(AFTERBURNER_LOOP_DELAY);
|
|
}
|
|
else {
|
|
Player_afterburner_loop_delay = 0;
|
|
}
|
|
|
|
snd_play( &Snds[SND_ABURN_ENGAGE], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
|
|
joy_ff_afterburn_on();
|
|
} else {
|
|
snd_play_3d( &Snds[SND_ABURN_ENGAGE], &objp->pos, &View_position, objp->radius );
|
|
}
|
|
|
|
objp->phys_info.flags |= PF_AFTERBURNER_WAIT;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// afterburners_update()
|
|
//
|
|
// Update the state of the afterburner fuel remaining for an object using the
|
|
// afterburner.
|
|
//
|
|
// for the player ship, key_up_time() is called for the afterburner key to
|
|
// detect when afterburners disengage.
|
|
//
|
|
// input: *objp => pointer to the object starting afterburners
|
|
// fl_frametime => time in seconds of the last frame
|
|
//
|
|
void afterburners_update(object *objp, float fl_frametime)
|
|
{
|
|
Assert( objp != NULL );
|
|
Assert( objp->type == OBJ_SHIP );
|
|
Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
|
|
|
|
ship_info *sip;
|
|
ship *shipp;
|
|
static int volume_chg_timer = 1;
|
|
|
|
shipp = &Ships[objp->instance];
|
|
|
|
Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < Num_ship_classes );
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
if ( (objp->flags & OF_PLAYER_SHIP ) && (Game_mode & GM_DEAD) ) {
|
|
return;
|
|
}
|
|
|
|
if ( !(sip->flags & SIF_AFTERBURNER) ) {
|
|
return; // nothing to update, afterburners are not even on the ship
|
|
}
|
|
|
|
//shut the afterburners off if we're using the booster tertiary
|
|
if ( objp->phys_info.flags & PF_BOOSTER_ON)
|
|
{
|
|
if (objp==Player_obj) afterburner_stop_sounds();
|
|
afterburners_stop(objp);
|
|
return;
|
|
}
|
|
|
|
if ( objp == Player_obj ) {
|
|
if ( !timestamp_elapsed(Player_disengage_timer) ) {
|
|
float remaining;
|
|
remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME);
|
|
if ( remaining <= 0 ) {
|
|
afterburner_stop_sounds();
|
|
}
|
|
else {
|
|
snd_set_volume( Player_afterburner_loop_id, remaining*Player_afterburner_vol);
|
|
}
|
|
}
|
|
else {
|
|
if ( Player_disengage_timer != 1 ) {
|
|
afterburner_stop_sounds();
|
|
}
|
|
}
|
|
}
|
|
|
|
// single player, multiplayer servers, and clients for their own ships
|
|
if(!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER || (objp == Player_obj)) {
|
|
if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) {
|
|
// Recover afterburner fuel
|
|
|
|
if ( shipp->afterburner_fuel < sip->afterburner_fuel_capacity ) {
|
|
float recharge_scale;
|
|
recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile->afterburner_recharge_scale[Game_skill_level];
|
|
shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale);
|
|
|
|
if ( shipp->afterburner_fuel > sip->afterburner_fuel_capacity){
|
|
shipp->afterburner_fuel = sip->afterburner_fuel_capacity;
|
|
}
|
|
}
|
|
return;
|
|
} else {
|
|
// Check if there is enough afterburner fuel
|
|
if ( shipp->afterburner_fuel <= 0 ) {
|
|
shipp->afterburner_fuel = 0.0f;
|
|
afterburners_stop(objp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// afterburners are firing at this point
|
|
|
|
// Reduce the afterburner fuel
|
|
shipp->afterburner_fuel -= (sip->afterburner_burn_rate * fl_frametime);
|
|
if ( shipp->afterburner_fuel < 0.0f ) {
|
|
shipp->afterburner_fuel = 0.0f;
|
|
}
|
|
}
|
|
|
|
if ( objp == Player_obj ) {
|
|
if ( timestamp_elapsed(Player_afterburner_loop_delay) ) {
|
|
Player_afterburner_vol = AFTERBURNER_DEFAULT_VOL;
|
|
Player_afterburner_loop_delay = 0;
|
|
if ( Player_afterburner_loop_id == -1 ) {
|
|
Player_afterburner_loop_id = snd_play_looping( &Snds[SND_ABURN_LOOP], 0.0f , -1, -1);
|
|
snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol);
|
|
// nprintf(("Alan","PLAY LOOPING SOUND\n"));
|
|
}
|
|
}
|
|
|
|
// Reduce the volume of the afterburner sound if near the end
|
|
if ( timestamp_elapsed(volume_chg_timer) ) {
|
|
float percent_afterburner_left;
|
|
percent_afterburner_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
|
|
volume_chg_timer = timestamp(AFTERBURNER_VOLUME_UPDATE);
|
|
if ( percent_afterburner_left < AFTERBURNER_PERCENT_VOL_ATTENUATE ) {
|
|
Player_afterburner_vol = percent_afterburner_left*(1/AFTERBURNER_PERCENT_VOL_ATTENUATE)*AFTERBURNER_DEFAULT_VOL;
|
|
snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol);
|
|
}
|
|
} // end if (timestamp_elapsed(volume_chg_timer))
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// afterburners_stop() will be called when a ship disengages the afterburners.
|
|
//
|
|
// parameters: *objp => pointer to the object starting afterburners
|
|
// key_released => OPTIONAL parameter (default value 0)
|
|
// This is only used for the player object, to
|
|
// manage starting/stopping
|
|
//
|
|
void afterburners_stop(object *objp, int key_released)
|
|
{
|
|
Assert( objp != NULL );
|
|
Assert( objp->instance >= 0 && objp->instance < MAX_SHIPS );
|
|
|
|
ship_info *sip;
|
|
ship *shipp;
|
|
|
|
shipp = &Ships[objp->instance];
|
|
|
|
Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < Num_ship_classes );
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
if ( (objp->flags & OF_PLAYER_SHIP) && key_released ) {
|
|
objp->phys_info.flags &= ~PF_AFTERBURNER_WAIT;
|
|
}
|
|
|
|
if ( !(sip->flags & SIF_AFTERBURNER) ) {
|
|
nprintf(("Warning","Ship type %s does not have afterburner capability\n", sip->name));
|
|
return;
|
|
}
|
|
|
|
if ( !(objp->phys_info.flags & PF_AFTERBURNER_ON) ) {
|
|
return;
|
|
}
|
|
|
|
objp->phys_info.flags &= ~PF_AFTERBURNER_ON;
|
|
float percent_left;
|
|
percent_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
|
|
|
|
//Do anim
|
|
model_anim_start_type(shipp, TRIGGER_TYPE_AFTERBURNER, ANIMATION_SUBTYPE_ALL, -1);
|
|
|
|
if ( objp == Player_obj ) {
|
|
|
|
if ( !key_released ) {
|
|
snd_play( &Snds[SND_ABURN_FAIL] );
|
|
}
|
|
|
|
if ( Player_afterburner_loop_id > -1 ) {
|
|
Player_disengage_timer = timestamp(DISENGAGE_TIME);
|
|
}
|
|
|
|
joy_ff_afterburn_off();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// afterburner_stop_sounds()
|
|
//
|
|
// Terminates any looping afterburner sounds.
|
|
// This should only be called when the game decides to stop all looping sounds.
|
|
//
|
|
void afterburner_stop_sounds()
|
|
{
|
|
if ( Player_afterburner_loop_id != -1 ) {
|
|
snd_stop(Player_afterburner_loop_id);
|
|
// nprintf(("Alan","STOP LOOPING SOUND\n"));
|
|
}
|
|
|
|
Player_afterburner_loop_id = -1;
|
|
Player_disengage_timer = 1;
|
|
Player_afterburner_loop_delay = 0;
|
|
}
|