mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 17:52:15 -10:00
This introduces a new mechanism for handling bit flags. Instead of using an int (or two, or three) to write bits into, this uses the std::bitset in a wrapper class that accepts an enum class as input. This means that certain classes of errors we've had a few of in the engine, where a coder used the wrong set of flags or checked them against the wrong variable (SF_STEALTH vs SIF_STEALTH, for example) can now be detected at a compiler level rather than not at all.
924 lines
24 KiB
C++
924 lines
24 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.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "cmdline/cmdline.h"
|
|
#include "debugconsole/console.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "io/joy_ff.h"
|
|
#include "io/timer.h"
|
|
#include "object/object.h"
|
|
#include "object/objectsnd.h"
|
|
#include "render/3d.h"
|
|
#include "ship/ship.h"
|
|
#include "sound/ds.h"
|
|
#include "sound/ds3d.h"
|
|
#include "species_defs/species_defs.h"
|
|
|
|
|
|
// // --mharris port hack--
|
|
// int ds_using_ds3d();
|
|
// int ds_get_channel(int);
|
|
// int ds3d_update_buffer(int, float, float, vec3d *, vec3d *);
|
|
// --end hack--
|
|
|
|
|
|
// Persistent sounds for objects (pointer to obj_snd is in object struct)
|
|
typedef struct _obj_snd {
|
|
_obj_snd *next, *prev;
|
|
int objnum; // object index of object that contains this sound
|
|
int id; // Index into Snds[] array
|
|
int instance; // handle of currently playing sound (a ds3d handle if USES_DS3D flag set)
|
|
int next_update; // timestamp that marks next allowed vol/pan change
|
|
float vol; // volume of sound (range: 0.0 -> 1.0)
|
|
float pan; // pan of sound (range: -1.0 -> 1.0)
|
|
int freq; // valid range: 100 -> 100000 Hz
|
|
int flags;
|
|
vec3d offset; // offset from the center of the object where the sound lives
|
|
ship_subsys *ss; //Associated subsystem
|
|
} obj_snd;
|
|
|
|
#define VOL_PAN_UPDATE 50 // time in ms to update a persistent sound vol/pan
|
|
#define MIN_PERSISTANT_VOL 0.10f
|
|
#define MIN_FORWARD_SPEED 5
|
|
#define SPEED_SOUND 600.0f // speed of sound in FreeSpace
|
|
|
|
static int MAX_OBJ_SOUNDS_PLAYING = -1; // initialized in obj_snd_level_init()
|
|
static int Num_obj_sounds_playing;
|
|
|
|
#define OBJSND_CHANGE_FREQUENCY_THRESHOLD 10
|
|
|
|
static obj_snd obj_snd_list; // head of linked list of object sound structs
|
|
static int Doppler_enabled = TRUE;
|
|
|
|
#define MAX_OBJ_SNDS 256
|
|
obj_snd Objsnds[MAX_OBJ_SNDS];
|
|
|
|
int Obj_snd_enabled = TRUE;
|
|
int Obj_snd_last_update; // timer used to run object sound updates at fixed time intervals
|
|
int Obj_snd_level_inited=0;
|
|
|
|
// ship flyby data
|
|
#define FLYBY_MIN_DISTANCE 90
|
|
#define FLYBY_MIN_SPEED 50
|
|
#define FLYBY_MIN_RELATIVE_SPEED 100
|
|
#define FLYBY_MIN_NEXT_TIME 1000 // in ms
|
|
#define FLYBY_MIN_REPEAT_TIME 4000 // in ms
|
|
int Flyby_next_sound;
|
|
int Flyby_next_repeat;
|
|
object *Flyby_last_objp;
|
|
|
|
// return the world pos of the sound source on a ship.
|
|
void obj_snd_source_pos(vec3d *sound_pos, obj_snd *osp)
|
|
{
|
|
vec3d offset_world;
|
|
object *objp = &Objects[osp->objnum];
|
|
|
|
// get sound pos in world coords
|
|
vm_vec_unrotate(&offset_world, &osp->offset, &objp->orient);
|
|
vm_vec_add(sound_pos, &objp->pos, &offset_world);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// dcf_objsnd()
|
|
//
|
|
// Debug console function for object linked persistent sounds
|
|
//
|
|
//XSTR:OFF
|
|
DCF(objsnd, "Persistent sound stuff" )
|
|
{
|
|
char buf1[4];
|
|
char buf2[MAX_NAME_LEN];
|
|
obj_snd *osp;
|
|
SCP_string arg;
|
|
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf ("Usage: objsnd [-list]\n");
|
|
dc_printf ("[-list] -- displays status of all objects with linked sounds\n");
|
|
dc_printf ("with no parameters, object sounds are toggled on/off\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf( "Object sounds are: %s\n", (Obj_snd_enabled?"ON":"OFF") );
|
|
}
|
|
|
|
if (dc_optional_string("-list")) {
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
vec3d source_pos;
|
|
float distance;
|
|
|
|
Assert(osp != NULL);
|
|
if ( osp->instance == -1 ) {
|
|
continue;
|
|
//sprintf(buf1,"OFF");
|
|
} else {
|
|
sprintf(buf1,"ON");
|
|
}
|
|
|
|
if ( Objects[osp->objnum].type == OBJ_SHIP ) {
|
|
strcpy_s(buf2, Ships[Objects[osp->objnum].instance].ship_name);
|
|
}
|
|
else if ( Objects[osp->objnum].type == OBJ_DEBRIS ) {
|
|
sprintf(buf2, "Debris");
|
|
}
|
|
else {
|
|
sprintf(buf2, "Unknown");
|
|
}
|
|
|
|
obj_snd_source_pos(&source_pos, osp);
|
|
distance = vm_vec_dist_quick( &source_pos, &View_position );
|
|
|
|
dc_printf("Object %d => name: %s vol: %.2f pan: %.2f dist: %.2f status: %s\n", osp->objnum, buf2, osp->vol, osp->pan, distance, buf1);
|
|
} // end for
|
|
|
|
dc_printf("Number object-linked sounds playing: %d\n", Num_obj_sounds_playing);
|
|
return;
|
|
}
|
|
|
|
if (!dc_maybe_stuff_string_white(arg)) {
|
|
// No arguments, toggle snd on/off
|
|
if ( Obj_snd_enabled == TRUE ) {
|
|
obj_snd_stop_all();
|
|
Obj_snd_enabled = FALSE;
|
|
} else {
|
|
Obj_snd_enabled = TRUE;
|
|
}
|
|
} else {
|
|
dc_printf("Unknown argument '%s'\n", arg.c_str());
|
|
}
|
|
}
|
|
//XSTR:ON
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Debug console function for toggling doppler effection on/off
|
|
//
|
|
DCF_BOOL( doppler, Doppler_enabled )
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_get_slot()
|
|
//
|
|
// Get a free slot in the Objsnds[] array
|
|
//
|
|
// returns -1 if no slot is available
|
|
int obj_snd_get_slot()
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
|
|
if ( !(Objsnds[i].flags & OS_USED) )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_init()
|
|
//
|
|
// Called once at level start to initialize the persistent object sound system
|
|
//
|
|
void obj_snd_level_init()
|
|
{
|
|
int i;
|
|
|
|
if (MAX_OBJ_SOUNDS_PLAYING < 0)
|
|
{
|
|
MAX_OBJ_SOUNDS_PLAYING = Cmdline_no_enhanced_sound ? 5 : 12;
|
|
}
|
|
|
|
list_init(&obj_snd_list);
|
|
for ( i = 0; i < MAX_OBJ_SNDS; i++ ) {
|
|
Objsnds[i].flags = 0;
|
|
}
|
|
|
|
Num_obj_sounds_playing = 0;
|
|
Flyby_next_sound = 1;
|
|
Flyby_next_repeat = 1;
|
|
Flyby_last_objp = NULL;
|
|
Obj_snd_last_update=0;
|
|
|
|
if ( !snd_is_inited() ) {
|
|
Obj_snd_enabled = FALSE;
|
|
return;
|
|
}
|
|
|
|
Obj_snd_level_inited=1;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_stop()
|
|
//
|
|
// Stop a persistent sound from playing.
|
|
//
|
|
// parameters: objp => pointer to object that sound is to be stopped for
|
|
//
|
|
//
|
|
void obj_snd_stop(object *objp, int index)
|
|
{
|
|
obj_snd *osp;
|
|
|
|
// sanity
|
|
if(index >= (int) objp->objsnd_num.size()){
|
|
Error(LOCATION, "Object sound index %d is bigger than the actual size %d!", index, (int) objp->objsnd_num.size());
|
|
return;
|
|
}
|
|
|
|
// if index is -1, kill all sounds for this guy
|
|
if(index == -1){
|
|
// kill all sounds for this guy
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter){
|
|
if ( *iter == -1 ){
|
|
continue;
|
|
}
|
|
|
|
osp = &Objsnds[*iter];
|
|
|
|
if ( osp->instance != -1 ) {
|
|
snd_stop(osp->instance);
|
|
osp->instance = -1;
|
|
switch(objp->type) {
|
|
case OBJ_SHIP:
|
|
case OBJ_GHOST:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
Num_obj_sounds_playing--;
|
|
Assert(Num_obj_sounds_playing >= 0);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if ( objp->objsnd_num[index] == -1 ){
|
|
return;
|
|
}
|
|
|
|
osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
if ( osp->instance != -1 ) {
|
|
snd_stop(osp->instance);
|
|
osp->instance = -1;
|
|
switch(objp->type) {
|
|
case OBJ_SHIP:
|
|
case OBJ_GHOST:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
Num_obj_sounds_playing--;
|
|
Assert(Num_obj_sounds_playing >= 0);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_stop_all()
|
|
//
|
|
// Stop all object-linked persistent sounds from playing
|
|
//
|
|
//
|
|
void obj_snd_stop_all()
|
|
{
|
|
object* A;
|
|
|
|
for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
|
|
obj_snd_stop(A, -1);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_get_freq()
|
|
//
|
|
// Calculate the frequency of a sound to be played, based on the relative velocities
|
|
// of the source and observor
|
|
//
|
|
// returns: frequency of the sound
|
|
//
|
|
int obj_snd_get_freq(int source_freq, object* source, object* observor, vec3d *source_pos)
|
|
{
|
|
vec3d v_os, v_so; // os == observor to source, so == source to observor
|
|
float vo, vs, freq;
|
|
|
|
vm_vec_normalized_dir(&v_os, source_pos, &observor->pos);
|
|
vm_vec_normalized_dir(&v_so, &observor->pos, source_pos);
|
|
|
|
vo = vm_vec_dot(&v_os, &observor->phys_info.vel);
|
|
vs = vm_vec_dot(&v_so, &source->phys_info.vel);
|
|
|
|
freq = source_freq * ( (SPEED_SOUND + vo) / (SPEED_SOUND - vs) );
|
|
return fl2i(freq);
|
|
}
|
|
|
|
|
|
/**
|
|
* Stop a playing object sound, if it is quieter than sound at new_distance
|
|
*
|
|
* @param new_vol Volume of requested sound to play
|
|
*
|
|
* @return 1 A sound was stopped
|
|
* @return 0 A sound was not stopped
|
|
*/
|
|
int obj_snd_stop_lowest_vol(float new_vol)
|
|
{
|
|
obj_snd *osp;
|
|
object *objp = NULL;
|
|
obj_snd *lowest_vol_osp = NULL;
|
|
float lowest_vol;
|
|
int obj_snd_index = -1;
|
|
|
|
lowest_vol = 1000.0f;
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
Assert(osp->objnum != -1);
|
|
objp = &Objects[osp->objnum];
|
|
|
|
if ( (osp->instance != -1) && (osp->vol < lowest_vol) ) {
|
|
lowest_vol = osp->vol;
|
|
lowest_vol_osp = osp;
|
|
}
|
|
}
|
|
|
|
if (lowest_vol_osp != NULL)
|
|
objp = &Objects[lowest_vol_osp->objnum];
|
|
|
|
if ( (lowest_vol < new_vol) && (objp != NULL) ) {
|
|
int idx = 0;
|
|
// determine what index in this guy the sound is
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == (lowest_vol_osp - Objsnds)){
|
|
obj_snd_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((obj_snd_index == -1) || (obj_snd_index >= (int) objp->objsnd_num.size())){
|
|
Int3(); // get dave
|
|
} else {
|
|
obj_snd_stop(objp, obj_snd_index);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//int Debug_1 = 0, Debug_2 = 0;
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// maybe_play_flyby_snd()
|
|
//
|
|
// Based on how close the object is to the camera (and relative speed), maybe
|
|
// play a flyby sound. Only play flyby sound for OBJ_SHIP objects.
|
|
//
|
|
// NOTE: global data Flyby_last_objp, Flyby_next_sound, Flyby_next_repeat are
|
|
// used.
|
|
//
|
|
void maybe_play_flyby_snd(float closest_dist, object *closest_objp, object *listener_objp)
|
|
{
|
|
if ( closest_objp == NULL || closest_objp->type != OBJ_SHIP || closest_objp == listener_objp ) {
|
|
return;
|
|
}
|
|
|
|
if ( closest_dist < FLYBY_MIN_DISTANCE ) {
|
|
float relative_speed;
|
|
vec3d diff;
|
|
vm_vec_sub(&diff, &listener_objp->phys_info.vel, &closest_objp->phys_info.vel);
|
|
|
|
|
|
relative_speed = vm_vec_mag_quick(&diff);
|
|
if ( relative_speed > FLYBY_MIN_RELATIVE_SPEED ) {
|
|
if ( timestamp_elapsed(Flyby_next_sound) ) {
|
|
|
|
if ( closest_objp == Flyby_last_objp ) {
|
|
if ( timestamp_elapsed(Flyby_next_repeat) ) {
|
|
Flyby_next_repeat = timestamp(FLYBY_MIN_REPEAT_TIME);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
Assert(closest_objp->type == OBJ_SHIP);
|
|
if(closest_objp->type != OBJ_SHIP){
|
|
return;
|
|
}
|
|
|
|
// pick a random species-based sound
|
|
|
|
ship_info *sip = &Ship_info[Ships[closest_objp->instance].ship_info_index];
|
|
game_snd *snd;
|
|
|
|
if (sip->flags[Ship::Info_Flags::Bomber])
|
|
snd = &Species_info[sip->species].snd_flyby_bomber;
|
|
else
|
|
snd = &Species_info[sip->species].snd_flyby_fighter;
|
|
|
|
// play da sound
|
|
snd_play_3d(snd, &closest_objp->pos, &View_position);
|
|
|
|
//float dist = vm_vec_dist(&closest_objp->pos, &View_position);
|
|
//nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, species, ship_size, dist));
|
|
// nprintf(("AI", "Frame %i: Playing flyby sound, species = %i, size = %i, dist = %7.3f\n", Framecount, Debug_1, Debug_2, dist));
|
|
//Debug_1 = (Debug_1+1)%3;
|
|
//Debug_2 = (Debug_2+1)%2;
|
|
|
|
joy_ff_fly_by(100 - (int) (100.0f * closest_dist / FLYBY_MIN_DISTANCE));
|
|
|
|
Flyby_next_sound = timestamp(FLYBY_MIN_NEXT_TIME);
|
|
Flyby_last_objp = closest_objp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_do_frame()
|
|
//
|
|
// Called once per frame to process the persistent sound objects
|
|
//
|
|
void obj_snd_do_frame()
|
|
{
|
|
float closest_dist, distance, speed_vol_multiplier, rot_vol_mult, percent_max, alive_vol_mult;
|
|
obj_snd *osp;
|
|
object *objp, *closest_objp;
|
|
game_snd *gs;
|
|
ship *sp;
|
|
int channel, go_ahead_flag;
|
|
vec3d source_pos;
|
|
float add_distance;
|
|
|
|
if ( Obj_snd_enabled == FALSE )
|
|
return;
|
|
|
|
int now = timer_get_milliseconds();
|
|
if ( (now - Obj_snd_last_update) > 100 ) {
|
|
Obj_snd_last_update=now;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
closest_dist = 1000000.0f;
|
|
closest_objp = NULL;
|
|
|
|
object *observer_obj = NULL;
|
|
if (Viewer_obj != NULL) {
|
|
observer_obj = Viewer_obj;
|
|
} else if (Viewer_mode & VM_OTHER_SHIP && Player_ai->target_objnum != -1) {
|
|
// apparently Viewer_obj is still null when Viewing Other Ship externally
|
|
observer_obj = &Objects[Player_ai->target_objnum];
|
|
} else {
|
|
observer_obj = Player_obj;
|
|
}
|
|
|
|
for ( osp = GET_FIRST(&obj_snd_list); osp !=END_OF_LIST(&obj_snd_list); osp = GET_NEXT(osp) ) {
|
|
Assert(osp != NULL);
|
|
objp = &Objects[osp->objnum];
|
|
if ( Player_obj == objp && observer_obj == Player_obj ) {
|
|
// we don't play the engine sound if the view is from the player
|
|
continue;
|
|
}
|
|
|
|
gs = &Snds[osp->id];
|
|
|
|
obj_snd_source_pos(&source_pos, osp);
|
|
distance = vm_vec_dist_quick( &source_pos, &View_position );
|
|
|
|
// how much extra distance do we add before attentuation?
|
|
add_distance = 0.0f;
|
|
if(osp->flags & OS_MAIN){
|
|
add_distance = objp->radius;
|
|
}
|
|
|
|
distance -= add_distance;
|
|
if ( distance < 0.0f ) {
|
|
distance = 0.0f;
|
|
}
|
|
|
|
// save closest distance (used for flyby sound) if this is a small ship (and not the observer)
|
|
if ( (objp->type == OBJ_SHIP) && (distance < closest_dist) && (objp != observer_obj) ) {
|
|
if ( Ship_info[Ships[objp->instance].ship_info_index].is_small_ship() ) {
|
|
closest_dist = distance;
|
|
closest_objp = objp;
|
|
}
|
|
}
|
|
|
|
// If the object is a ship, we don't want to start the engine sound unless the ship is
|
|
// moving (unless flag SIF_BIG_SHIP is set)
|
|
speed_vol_multiplier = 1.0f;
|
|
rot_vol_mult = 1.0f;
|
|
alive_vol_mult = 1.0f;
|
|
if ( objp->type == OBJ_SHIP ) {
|
|
ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
|
|
if ( !(sip->is_big_or_huge()) ) {
|
|
if ( objp->phys_info.max_vel.xyz.z <= 0.0f ) {
|
|
percent_max = 0.0f;
|
|
}
|
|
else
|
|
percent_max = objp->phys_info.fspeed / objp->phys_info.max_vel.xyz.z;
|
|
|
|
if ( sip->min_engine_vol == -1.0f) {
|
|
// Retail behavior: volume ramps from 0.5 (when stationary) to 1.0 (when at half speed)
|
|
if ( percent_max >= 0.5f ) {
|
|
speed_vol_multiplier = 1.0f;
|
|
} else {
|
|
speed_vol_multiplier = 0.5f + (percent_max); // linear interp: 0.5->1.0 when 0.0->0.5
|
|
}
|
|
} else {
|
|
// Volume ramps from min_engine_vol (when stationary) to 1.0 (when at full speed)
|
|
speed_vol_multiplier = sip->min_engine_vol + ((1.0f - sip->min_engine_vol) * percent_max);
|
|
}
|
|
}
|
|
if (osp->ss != NULL)
|
|
{
|
|
if (osp->flags & OS_TURRET_BASE_ROTATION)
|
|
{
|
|
if (osp->ss->base_rotation_rate_pct > 0.0f)
|
|
rot_vol_mult = ((0.25f + (0.75f * osp->ss->base_rotation_rate_pct)) * osp->ss->system_info->turret_base_rotation_snd_mult);
|
|
else
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
if (osp->flags & OS_TURRET_GUN_ROTATION)
|
|
{
|
|
if (osp->ss->gun_rotation_rate_pct > 0.0f)
|
|
rot_vol_mult = ((0.25f + (0.75f * osp->ss->gun_rotation_rate_pct)) * osp->ss->system_info->turret_gun_rotation_snd_mult);
|
|
else
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
if (osp->flags & OS_SUBSYS_ROTATION )
|
|
{
|
|
if (osp->ss->flags & SSF_ROTATES) {
|
|
rot_vol_mult = 1.0f;
|
|
} else {
|
|
rot_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_ALIVE)
|
|
{
|
|
if (osp->ss->current_hits > 0.0f) {
|
|
alive_vol_mult = 1.0f;
|
|
} else {
|
|
alive_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_DEAD)
|
|
{
|
|
if (osp->ss->current_hits <= 0.0f) {
|
|
alive_vol_mult = 1.0f;
|
|
} else {
|
|
alive_vol_mult = 0.0f;
|
|
}
|
|
}
|
|
if (osp->flags & OS_SUBSYS_DAMAGED)
|
|
{
|
|
alive_vol_mult = osp->ss->current_hits / osp->ss->max_hits;
|
|
CLAMP(alive_vol_mult, 0.0f, 1.0f);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
go_ahead_flag = TRUE;
|
|
float max_vol,new_vol;
|
|
if ( osp->instance == -1 ) {
|
|
if ( distance < Snds[osp->id].max ) {
|
|
max_vol = Snds[osp->id].default_volume;
|
|
if ( distance <= Snds[osp->id].min ) {
|
|
new_vol = max_vol;
|
|
}
|
|
else {
|
|
new_vol = max_vol - (distance - Snds[osp->id].min) * max_vol / (Snds[osp->id].max - Snds[osp->id].min);
|
|
}
|
|
|
|
if ( new_vol < 0.1f ) {
|
|
continue;
|
|
}
|
|
|
|
switch( objp->type ) {
|
|
case OBJ_SHIP:
|
|
case OBJ_DEBRIS:
|
|
case OBJ_ASTEROID:
|
|
if ( Num_obj_sounds_playing >= MAX_OBJ_SOUNDS_PLAYING ) {
|
|
go_ahead_flag = obj_snd_stop_lowest_vol(new_vol);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
break;
|
|
} // end switch
|
|
|
|
if ( go_ahead_flag ) {
|
|
osp->instance = snd_play_3d(gs, &source_pos, &View_position, add_distance, &objp->phys_info.vel, 1, 1.0f, SND_PRIORITY_TRIPLE_INSTANCE, NULL, 1.0f, 0, true);
|
|
if ( osp->instance != -1 ) {
|
|
Num_obj_sounds_playing++;
|
|
}
|
|
}
|
|
Assert(Num_obj_sounds_playing <= MAX_OBJ_SOUNDS_PLAYING);
|
|
|
|
} // end if ( distance < Snds[osp->id].max )
|
|
} // if ( osp->instance == -1 )
|
|
else {
|
|
if ( distance > Snds[osp->id].max ) {
|
|
int sound_index = -1;
|
|
int idx = 0;
|
|
|
|
// determine which sound index it is for this guy
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == (osp - Objsnds)){
|
|
sound_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert(sound_index != -1);
|
|
obj_snd_stop(objp, sound_index); // currently playing sound has gone past maximum
|
|
}
|
|
}
|
|
|
|
if ( osp->instance == -1 )
|
|
continue;
|
|
|
|
sp = NULL;
|
|
if ( objp->type == OBJ_SHIP )
|
|
sp = &Ships[objp->instance];
|
|
|
|
|
|
channel = ds_get_channel(osp->instance);
|
|
// for DirectSound3D sounds, re-establish the maximum speed based on the
|
|
// speed_vol_multiplier
|
|
if ( sp == NULL || ( (sp != NULL) && (sp->flags[Ship::Ship_Flags::Engines_on]) ) ) {
|
|
snd_set_volume( osp->instance, gs->default_volume*speed_vol_multiplier*rot_vol_mult*alive_vol_mult );
|
|
}
|
|
else {
|
|
// engine sound is disabled
|
|
snd_set_volume( osp->instance, 0.0f );
|
|
}
|
|
|
|
vec3d vel = objp->phys_info.vel;
|
|
|
|
// Don't play doppler effect for cruisers or capitals
|
|
if ( sp ) {
|
|
if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
|
|
vel = vmd_zero_vector;
|
|
}
|
|
}
|
|
|
|
ds3d_update_buffer(channel, i2fl(gs->min), i2fl(gs->max), &source_pos, &vel);
|
|
snd_get_3d_vol_and_pan(gs, &source_pos, &osp->vol, &osp->pan, add_distance);
|
|
} // end for
|
|
|
|
// see if we want to play a flyby sound
|
|
maybe_play_flyby_snd(closest_dist, closest_objp, observer_obj);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_assign()
|
|
//
|
|
// Assign a persistent sound to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being assigned to
|
|
// i => Index into Snds[] array
|
|
// fname => filename of sound to play ( so DS3D can load the sound )
|
|
//
|
|
// returns: -1 => sound could not be assigned (possible, since only MAX_OBJECT_SOUNDS persistent
|
|
// sound can be assigned per object).
|
|
// >= 0 => sound was successfully assigned
|
|
//
|
|
int obj_snd_assign(int objnum, int sndnum, vec3d *pos, int main, int flags, ship_subsys *associated_sub)
|
|
{
|
|
if(objnum < 0 || objnum > MAX_OBJECTS)
|
|
return -1;
|
|
|
|
if(sndnum < 0)
|
|
return -1;
|
|
|
|
if ( Obj_snd_enabled == FALSE )
|
|
return -1;
|
|
|
|
//Initialize object sound list, if not initialized
|
|
if (!Obj_snd_level_inited)
|
|
obj_snd_level_init();
|
|
|
|
obj_snd *snd = NULL;
|
|
object *objp = &Objects[objnum];
|
|
int sound_index;
|
|
int idx = 0;
|
|
|
|
// try and find a valid objsound index
|
|
sound_index = -1;
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
if(*iter == -1){
|
|
sound_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// no sound slot free, make a new one!
|
|
if ( sound_index == -1 ){
|
|
sound_index = (int) objp->objsnd_num.size();
|
|
objp->objsnd_num.push_back(-1);
|
|
}
|
|
|
|
objp->objsnd_num[sound_index] = obj_snd_get_slot();
|
|
if ( objp->objsnd_num[sound_index] == -1 ) {
|
|
nprintf(("Sound", "SOUND ==> No free object-linked sounds left\n"));
|
|
return -1;
|
|
}
|
|
snd = &Objsnds[objp->objsnd_num[sound_index]];
|
|
snd->flags = OS_USED;
|
|
|
|
if(main){
|
|
snd->flags |= OS_MAIN;
|
|
}
|
|
|
|
if(flags > 0){
|
|
snd->flags |= flags;
|
|
}
|
|
|
|
snd->id = sndnum;
|
|
|
|
snd->instance = -1;
|
|
snd->vol = 0.0f;
|
|
snd->objnum = OBJ_INDEX(objp);
|
|
snd->next_update = 1;
|
|
snd->offset = *pos;
|
|
snd->ss = associated_sub;
|
|
// vm_vec_sub(&snd->offset, pos, &objp->pos);
|
|
|
|
// add objp to the obj_snd_list
|
|
list_append( &obj_snd_list, snd );
|
|
|
|
return sound_index;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete()
|
|
//
|
|
// Remove a persistent sound that has been assigned to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being removed from.
|
|
// index => index of sound in objsnd_num
|
|
//
|
|
void obj_snd_delete(int objnum, int index)
|
|
{
|
|
//Sanity checking
|
|
Assert(objnum > -1 && objnum < MAX_OBJECTS);
|
|
|
|
object *objp = &Objects[objnum];
|
|
|
|
Assert(index > -1 && index < (int) objp->objsnd_num.size());
|
|
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
//Stop the sound
|
|
obj_snd_stop(objp, index);
|
|
|
|
// remove objp from the obj_snd_list
|
|
list_remove( &obj_snd_list, osp );
|
|
osp->objnum = -1;
|
|
osp->flags = 0;
|
|
osp = NULL;
|
|
objp->objsnd_num[index] = -1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete_type()
|
|
//
|
|
// Remove every similar persistent sound that has been assigned to an object.
|
|
//
|
|
// parameters: objnum => index of object that sound is being removed from.
|
|
// sndnum => index of sound that we're trying to completely get rid of
|
|
// -1 to delete all persistent sounds on ship.
|
|
//
|
|
//
|
|
void obj_snd_delete_type(int objnum, int sndnum, ship_subsys *ss)
|
|
{
|
|
object *objp;
|
|
obj_snd *osp;
|
|
|
|
if(objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return;
|
|
|
|
objp = &Objects[objnum];
|
|
|
|
size_t idx = 0;
|
|
//Go through the list and get sounds that match criteria
|
|
for(SCP_vector<int>::iterator iter = objp->objsnd_num.begin(); iter != objp->objsnd_num.end(); ++iter, ++idx){
|
|
// no sound
|
|
if ( *iter == -1 ){
|
|
continue;
|
|
}
|
|
|
|
osp = &Objsnds[*iter];
|
|
|
|
// if we're just deleting a specific sound type
|
|
// and this is not one of them. skip it.
|
|
//Also check if this is assigned to the right subsystem, if one has been given.
|
|
if(((sndnum != -1) && (osp->id != sndnum))
|
|
|| ((ss != NULL) && (osp->ss != ss))){
|
|
continue;
|
|
}
|
|
|
|
obj_snd_delete(objnum, idx);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_delete_all()
|
|
//
|
|
// Remove all persistent sounds
|
|
//
|
|
void obj_snd_delete_all()
|
|
{
|
|
int idx;
|
|
for(idx=0; idx<MAX_OBJ_SNDS; idx++){
|
|
if(Objsnds[idx].flags & OS_USED){
|
|
obj_snd_delete_type(Objsnds[idx].objnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_close()
|
|
//
|
|
// Called once at game close to de-initialize the persistent object sound system
|
|
//
|
|
void obj_snd_level_close()
|
|
{
|
|
if ( !Obj_snd_level_inited ) {
|
|
return;
|
|
}
|
|
obj_snd_delete_all();
|
|
Obj_snd_level_inited=0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_is_playing()
|
|
//
|
|
// Determines if a given object-linked sound is currently playing
|
|
//
|
|
int obj_snd_is_playing(int object, int index)
|
|
{
|
|
if ( obj_snd_return_instance(object, index) < 0 )
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_return_instance()
|
|
//
|
|
// Returns the sound instance for a given object-linked sound
|
|
//
|
|
int obj_snd_return_instance(int objnum, int index)
|
|
{
|
|
if ( objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return -1;
|
|
|
|
if ( index < 0 || index >= MAX_OBJ_SNDS )
|
|
return -1;
|
|
|
|
object *objp = &Objects[objnum];
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
|
|
return osp->instance;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// obj_snd_update_offset()
|
|
//
|
|
// Updates offset of the given object sound
|
|
//
|
|
int obj_snd_update_offset(int objnum, int index, vec3d *new_offset)
|
|
{
|
|
if ( objnum < 0 || objnum >= MAX_OBJECTS)
|
|
return 0;
|
|
|
|
if ( index < 0 || index >= MAX_OBJ_SNDS )
|
|
return 0;
|
|
|
|
object *objp = &Objects[objnum];
|
|
obj_snd *osp = &Objsnds[objp->objsnd_num[index]];
|
|
osp->offset = *new_offset;
|
|
|
|
return 1;
|
|
}
|