mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
As noticed in issue #2458, intercom sounds did not stop when pausing the game or loading a mission. I fixed this by augmenting `main_hall_stop_ambient()` to also stop the intercom. Additionally, it turns out that the intercom delays were not being calculated correctly due to a very bizarre random number formula. I overloaded `rand32()` to add a min/max variant and used this instead.
514 lines
17 KiB
C++
514 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.
|
|
*
|
|
*/
|
|
|
|
#include "globalincs/systemvars.h"
|
|
|
|
#include "debugconsole/console.h"
|
|
#include "globalincs/pstypes.h"
|
|
#include "graphics/2d.h"
|
|
#include "io/timer.h"
|
|
#include "nebula/neb.h"
|
|
#include "options/Option.h"
|
|
|
|
fix Missiontime;
|
|
fix Skybox_timestamp;
|
|
fix Frametime;
|
|
int Framecount=0;
|
|
|
|
int Game_mode;
|
|
|
|
int Game_restoring = 0; // If set, this means we are restoring data from disk
|
|
|
|
int Viewer_mode; // Viewer's mode, see VM_xxxx flags.
|
|
|
|
//CUTSCENE STUFF
|
|
//Cutscene flags
|
|
int Cutscene_bar_flags = CUB_NONE;
|
|
//Time for gradual change in seconds
|
|
float Cutscene_delta_time = 1.0f;
|
|
//How far along a change is (0 to 1)
|
|
float Cutscene_bars_progress = 1.0f;
|
|
|
|
//FADEIN STUFF
|
|
shader Viewer_shader;
|
|
FadeType Fade_type = FI_NONE;
|
|
int Fade_start_timestamp = 0;
|
|
int Fade_end_timestamp = 0;
|
|
|
|
// The detail level. Anything below zero draws simple models earlier than it
|
|
// should. Anything above zero draws higher detail models longer than it should.
|
|
// -2=lowest
|
|
// -1=low
|
|
// 0=normal (medium)
|
|
// 1=high
|
|
// 2=extra high
|
|
int Game_detail_level = 0;
|
|
uint Game_detail_flags = DETAIL_DEFAULT; // see systemvars.h for explanation
|
|
|
|
angles Viewer_slew_angles; // Angles of viewer relative to forward.
|
|
vei Viewer_external_info; // Viewer angles to ship in external view.
|
|
vci Viewer_chase_info; // View chase camera information
|
|
vec3d leaning_position;
|
|
|
|
int Is_standalone;
|
|
int Rand_count;
|
|
|
|
int Interface_last_tick = -1; // last timer tick on flip
|
|
|
|
#ifndef NDEBUG
|
|
// for debugging, used to print the currently processing filename on the loading screen
|
|
char Processing_filename[MAX_PATH_LEN];
|
|
#endif
|
|
|
|
// override states to skip rendering of certain elements, but without disabling them completely
|
|
bool Basemap_override = false;
|
|
bool Envmap_override = false;
|
|
bool Specmap_override = false;
|
|
bool Normalmap_override = false;
|
|
bool Heightmap_override = false;
|
|
bool Glowpoint_override = false;
|
|
bool Glowpoint_use_depth_buffer = true;
|
|
bool PostProcessing_override = false;
|
|
bool Shadow_override = false;
|
|
bool Trail_render_override = false;
|
|
|
|
bool Basemap_color_override_set = false;
|
|
float Basemap_color_override[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
|
|
bool Glowmap_color_override_set = false;
|
|
float Glowmap_color_override[3] = {0.0f, 0.0f, 0.0f};
|
|
|
|
bool Specmap_color_override_set = false;
|
|
float Specmap_color_override[3] = {0.0f, 0.0f, 0.0f};
|
|
|
|
bool Gloss_override_set = false;
|
|
float Gloss_override = 0.0f;
|
|
|
|
// Values used for noise for thruster animations
|
|
float Noise[NOISE_NUM_FRAMES] = {
|
|
0.468225f,
|
|
0.168765f,
|
|
0.318945f,
|
|
0.292866f,
|
|
0.553357f,
|
|
0.468225f,
|
|
0.180456f,
|
|
0.418465f,
|
|
0.489958f,
|
|
1.000000f,
|
|
0.468225f,
|
|
0.599820f,
|
|
0.664718f,
|
|
0.294215f,
|
|
0.000000f
|
|
};
|
|
|
|
|
|
int myrand()
|
|
{
|
|
int rval;
|
|
rval = rand();
|
|
Rand_count++;
|
|
// nprintf(("Alan","RAND: %d\n", rval));
|
|
return rval;
|
|
}
|
|
|
|
// returns a random number between 0 and 0x7fffffff, or something close to it.
|
|
int rand32()
|
|
{
|
|
if (RAND_MAX == 0x7fff) {
|
|
int random32;
|
|
// this gets two random 16 bit numbers and stuffs them into a 32 bit number
|
|
random32 = (rand() << 16) | rand();
|
|
//since rand() returns between 0 and 0x7fff, there needs to be a thing to randomly generate the 16th bit
|
|
random32 |= ((rand() & 1) << 15);
|
|
|
|
return random32;
|
|
}
|
|
else {
|
|
return rand();
|
|
}
|
|
}
|
|
|
|
int rand32(int low, int high)
|
|
{
|
|
int diff;
|
|
|
|
// get diff - don't allow negative or zero
|
|
diff = high - low;
|
|
if (diff < 0)
|
|
diff = 0;
|
|
|
|
// To get a range of values between min and max, inclusive:
|
|
// random value = min + random number % (max - min + 1)
|
|
return (low + rand32() % (diff + 1));
|
|
}
|
|
|
|
// Variables for the loading callback hooks
|
|
static int cf_timestamp = -1;
|
|
static void (*cf_callback)(int count) = NULL;
|
|
static int cf_in_callback = 0;
|
|
static int cb_counter = 0;
|
|
static int cb_last_counter = 0;
|
|
static int cb_delta_step = -1;
|
|
|
|
// Call this with the name of a function. That function will
|
|
// then get called around 10x per second. The callback function
|
|
// gets passed a 'count' which is how many times game_busy has
|
|
// been called since the callback was set. It gets called
|
|
// one last time with count=-1 when you turn off the callback
|
|
// by calling game_busy_callback(NULL). Game_busy_callback
|
|
// returns the current count, so you can tell how many times
|
|
// game_busy got called.
|
|
int game_busy_callback( void (*callback)(int count), int delta_step )
|
|
{
|
|
if ( !callback ) {
|
|
|
|
// Call it once more to finalize things
|
|
cf_in_callback++;
|
|
(*cf_callback)(-1);
|
|
cf_in_callback--;
|
|
|
|
cf_timestamp = -1;
|
|
cf_callback = NULL;
|
|
} else {
|
|
cb_counter = 0;
|
|
cb_last_counter = 0;
|
|
cb_delta_step = delta_step;
|
|
cf_timestamp = timer_get_milliseconds()+(1000/10);
|
|
cf_callback = callback;
|
|
|
|
// Call it once
|
|
cf_in_callback++;
|
|
(*cf_callback)(0); // pass 0 first time!
|
|
cf_in_callback--;
|
|
|
|
}
|
|
|
|
return cb_counter;
|
|
}
|
|
|
|
// Call whenever loading to display cursor
|
|
void game_busy(const char *filename)
|
|
{
|
|
if ( cf_in_callback != 0 ) return; // don't call callback if we're already in it.
|
|
if ( cf_timestamp < 0 ) return;
|
|
if ( !cf_callback ) return;
|
|
|
|
cb_counter++;
|
|
|
|
// mprintf(( "CB_COUNTER=%d\n", cb_counter ));
|
|
|
|
#ifndef NDEBUG
|
|
if (filename != NULL)
|
|
strcpy_s(Processing_filename, filename);
|
|
#endif
|
|
|
|
int t1 = timer_get_milliseconds();
|
|
|
|
if ( (t1 > cf_timestamp) || ((cb_counter > cb_last_counter+155) && (cb_delta_step > 0)) ) {
|
|
cb_last_counter = cb_counter;
|
|
cf_in_callback++;
|
|
(*cf_callback)(cb_counter);
|
|
cf_in_callback--;
|
|
cf_timestamp = t1 + (1000/10);
|
|
}
|
|
}
|
|
|
|
#if MAX_DETAIL_LEVEL != 4
|
|
#error MAX_DETAIL_LEVEL is assumed to be 4 in SystemVars.cpp
|
|
#endif
|
|
|
|
#if NUM_DEFAULT_DETAIL_LEVELS != 4
|
|
#error NUM_DEFAULT_DETAIL_LEVELS is assumed to be 4 in SystemVars.cpp
|
|
#endif
|
|
|
|
// Detail level stuff
|
|
detail_levels Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS] = {
|
|
{ // Low
|
|
0, // setting
|
|
// ===== Analogs (0-MAX_DETAIL_LEVEL) ====
|
|
0, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail
|
|
0, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest
|
|
0, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling
|
|
0, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
0, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
0, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
0, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max
|
|
2, // lighting; // 0=min, MAX_DETAIL_LEVEL=max
|
|
|
|
// ==== Booleans ====
|
|
0, // targetview_model; // 0=off, 1=on
|
|
0, // planets_suns; // 0=off, 1=on
|
|
0, // weapon_extras
|
|
},
|
|
{ // Medium
|
|
1, // setting
|
|
// ===== Analogs (0-MAX_DETAIL_LEVEL) ====
|
|
2, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail
|
|
2, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest
|
|
2, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling
|
|
2, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
2, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
2, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
2, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max
|
|
3, // lighting; // 0=min, MAX_DETAIL_LEVEL=max
|
|
|
|
// ==== Booleans ====
|
|
1, // targetview_model; // 0=off, 1=on
|
|
1, // planets_suns; // 0=off, 1=on
|
|
1, // weapon extras
|
|
},
|
|
{ // High level
|
|
2, // setting
|
|
// ===== Analogs (0-MAX_DETAIL_LEVEL) ====
|
|
2, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail
|
|
2, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest
|
|
3, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling
|
|
3, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
3, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
4, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
3, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max
|
|
4, // lighting; // 0=min, MAX_DETAIL_LEVEL=max
|
|
|
|
// ==== Booleans ====
|
|
1, // targetview_model; // 0=off, 1=on
|
|
1, // planets_suns; // 0=off, 1=on
|
|
1, // weapon_extras
|
|
},
|
|
{ // Highest level
|
|
3, // setting
|
|
// ===== Analogs (0-MAX_DETAIL_LEVEL) ====
|
|
3, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail
|
|
3, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest
|
|
4, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling
|
|
4, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
4, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
4, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number
|
|
4, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max
|
|
4, // lighting; // 0=min, MAX_DETAIL_LEVEL=max
|
|
|
|
// ==== Booleans ====
|
|
1, // targetview_model; // 0=off, 1=on
|
|
1, // planets_suns; // 0=off, 1=on
|
|
1, // weapon_extras
|
|
},
|
|
};
|
|
|
|
// Global used to access detail levels in game and libs
|
|
detail_levels Detail = Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS - 1];
|
|
|
|
const SCP_vector<std::pair<int, SCP_string>> DetailLevelValues = {{ 0, "Minimum" },
|
|
{ 1, "Low" },
|
|
{ 2, "Medium" },
|
|
{ 3, "High" },
|
|
{ 4, "Ultra" }, };
|
|
|
|
const auto ModelDetailOption =
|
|
options::OptionBuilder<int>("Graphics.Detail", "Model Detail", "Detail level of models").importance(8).category(
|
|
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
|
|
Detail.detail_distance = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
const auto TexturesOption = options::OptionBuilder<int>("Graphics.Texture",
|
|
"3D Hardware Textures",
|
|
"Level of detail of textures").importance(6).category("Graphics").values(
|
|
DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
|
|
Detail.hardware_textures = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
const auto ParticlesOption = options::OptionBuilder<int>("Graphics.Particles",
|
|
"Particles",
|
|
"Level of detail for particles").importance(5).category(
|
|
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
|
|
Detail.num_particles = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
const auto SmallDebrisOption =
|
|
options::OptionBuilder<int>("Graphics.SmallDebris", "Impact Effects", "Level of detail of impact effects").category(
|
|
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).importance(4).change_listener([](int val,
|
|
bool) {
|
|
Detail.num_small_debris = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
const auto ShieldEffectsOption = options::OptionBuilder<int>("Graphics.ShieldEffects",
|
|
"Shield Hit Effects",
|
|
"Level of detail of shield impacts").importance(3).category(
|
|
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
|
|
Detail.shield_effects = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
const auto StarsOption =
|
|
options::OptionBuilder<int>("Graphics.Stars", "Stars", "Number of stars in the mission").importance(2).category(
|
|
"Graphics").values(DetailLevelValues).default_val(MAX_DETAIL_LEVEL).change_listener([](int val, bool) {
|
|
Detail.num_stars = val;
|
|
return true;
|
|
}).finish();
|
|
|
|
// Call this with:
|
|
// 0 - lowest
|
|
// NUM_DETAIL_LEVELS - highest
|
|
// To set the parameters in Detail to some set of defaults
|
|
void detail_level_set(int level)
|
|
{
|
|
if ( level < 0 ) {
|
|
Detail.setting = -1;
|
|
return;
|
|
}
|
|
Assert( level >= 0 );
|
|
Assert( level < NUM_DEFAULT_DETAIL_LEVELS );
|
|
|
|
Detail = Detail_defaults[level];
|
|
|
|
// reset nebula stuff
|
|
neb2_set_detail_level(level);
|
|
}
|
|
|
|
// Returns the current detail level or -1 if custom.
|
|
int current_detail_level()
|
|
{
|
|
// return Detail.setting;
|
|
int i;
|
|
|
|
for (i=0; i<NUM_DEFAULT_DETAIL_LEVELS; i++ ) {
|
|
if ( memcmp( &Detail, &Detail_defaults[i], sizeof(detail_levels) )==0 ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
DCF(detail_level,"Change the detail level")
|
|
{
|
|
int value;
|
|
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf( "Usage: detail_level [n]\n");
|
|
dc_printf("[n] -- is detail level.\n");
|
|
dc_printf("\t0 is 'normal' detail,\n");
|
|
dc_printf("\tnegative values are lower, and\n");
|
|
dc_printf("\tpositive values are higher.\n\n");
|
|
|
|
dc_printf("No parameter resets it to default.\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf("Detail level set to %d\n", Game_detail_level);
|
|
return;
|
|
}
|
|
|
|
if (dc_maybe_stuff_int(&value)) {
|
|
Game_detail_level = value;
|
|
dc_printf("Detail level set to %i\n", Game_detail_level);
|
|
|
|
} else {
|
|
Game_detail_level = 0;
|
|
dc_printf("Detail level reset\n");
|
|
}
|
|
}
|
|
|
|
DCF(detail, "Turns on/off parts of the game for speed testing" )
|
|
{
|
|
int value;
|
|
|
|
if (dc_optional_string_either("help", "--help")) {
|
|
dc_printf( "Usage: detail [n]\n");
|
|
dc_printf("[n] is detail bit to toggle:\n" );
|
|
dc_printf( " 1: draw the stars\n" );
|
|
dc_printf( " 2: draw the nebulas\n" );
|
|
dc_printf( " 4: draw the motion debris\n" );
|
|
dc_printf( " 8: draw planets\n" );
|
|
dc_printf( " 16: draw models not as blobs\n" );
|
|
dc_printf( " 32: draw lasers not as pixels\n" );
|
|
dc_printf( " 64: clear screen background after each frame\n" );
|
|
dc_printf( " 128: draw hud stuff\n" );
|
|
dc_printf( " 256: draw fireballs\n" );
|
|
dc_printf( " 512: do collision detection\n\n" );
|
|
|
|
dc_printf("No argument will toggle between highest/lowest detail settings\n");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
dc_printf("Detail flags set to 0x%08x\n", Game_detail_flags);
|
|
dc_printf( " 1: draw the stars: %s\n", ((Game_detail_flags & 1) ? "on" : "off"));
|
|
dc_printf( " 2: draw the nebulas: %s\n", ((Game_detail_flags & 2)?"on" : "off"));
|
|
dc_printf( " 4: draw the motion debris: %s\n", ((Game_detail_flags & 4) ? "on" : "off"));
|
|
dc_printf( " 8: draw planets: %s\n", ((Game_detail_flags & 8) ? "on" : "off"));
|
|
dc_printf( " 16: draw models not as blobs: %s\n", ((Game_detail_flags & 16) ? "on" : "off"));
|
|
dc_printf( " 32: draw lasers not as pixels: %s\n", ((Game_detail_flags & 32) ? "on" : "off"));
|
|
dc_printf( " 64: clear screen background after each frame: %s\n", ((Game_detail_flags & 64) ? "on" : "off"));
|
|
dc_printf( " 128: draw hud stuff: %s\n", ((Game_detail_flags & 128) ? "on" : "off"));
|
|
dc_printf( " 256: draw fireballs: %s\n", ((Game_detail_flags & 256) ? "on" : "off"));
|
|
dc_printf( " 512: do collision detection: %s\n", ((Game_detail_flags & 512) ? "on" : "off"));
|
|
return;
|
|
}
|
|
|
|
if (dc_maybe_stuff_int(&value)) {
|
|
Game_detail_flags ^= value;
|
|
|
|
} else {
|
|
if (Game_detail_flags == DETAIL_DEFAULT) {
|
|
Game_detail_flags = DETAIL_FLAG_CLEAR;
|
|
dc_printf( "Detail flags set lowest (except has screen clear)\n" );
|
|
} else {
|
|
Game_detail_flags = DETAIL_DEFAULT;
|
|
dc_printf( "Detail flags set highest\n" );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Goober5000
|
|
// (Taylor says that for optimization purposes malloc/free should be used rather than vm_malloc/vm_free here)
|
|
// NOTE: Because this uses memcpy, it should only be used to sort POD elements!
|
|
void insertion_sort(void *array_base, size_t array_size, size_t element_size, int (*fncompare)(const void *, const void *))
|
|
{
|
|
int i, j;
|
|
void *current;
|
|
char *array_byte_base;
|
|
|
|
// this is used to avoid having to cast array_base to (char *) all the time
|
|
array_byte_base = (char *) array_base;
|
|
|
|
// allocate space for the element being moved
|
|
current = malloc(element_size);
|
|
if (current == NULL)
|
|
{
|
|
Int3();
|
|
return;
|
|
}
|
|
|
|
// loop
|
|
for (i = 1; (unsigned) i < array_size; i++)
|
|
{
|
|
// grab the current element
|
|
memcpy(current, array_byte_base + (i * element_size), element_size);
|
|
|
|
// bump other elements toward the end of the array
|
|
for (j = i - 1; (j >= 0) && (fncompare(array_byte_base + (j * element_size), current) > 0); j--)
|
|
{
|
|
memcpy(array_byte_base + ((j + 1) * element_size), array_byte_base + (j * element_size), element_size);
|
|
}
|
|
|
|
// insert the current element at the correct place
|
|
memcpy(array_byte_base + ((j + 1) * element_size), current, element_size);
|
|
}
|
|
|
|
// free the allocated space
|
|
free(current);
|
|
}
|