From 70291fe7ceea3cf81a8831c99cd4e4dc10fcba3a Mon Sep 17 00:00:00 2001 From: Cliff Gordon Date: Tue, 28 Apr 2015 16:19:54 -0500 Subject: [PATCH] Changes from the svn zookeeper branch applied to a new git branch. --- code/ai/ai_profiles.cpp | 9 + code/ai/ai_profiles.h | 5 + code/ai/aicode.cpp | 50 +++- code/bmpman/bm_internal.h | 1 + code/bmpman/bmpman.cpp | 106 +++++++- code/bmpman/bmpman.h | 16 +- code/cfile/cfile.cpp | 1 + code/cfile/cfile.h | 3 +- code/cfile/cfilesystem.cpp | 1 + code/controlconfig/controlsconfig.cpp | 51 +++- code/controlconfig/controlsconfig.h | 4 +- code/controlconfig/controlsconfigcommon.cpp | 257 +++++++++++--------- code/debris/debris.cpp | 4 + code/gamesnd/eventmusic.cpp | 5 +- code/gamesnd/eventmusic.h | 3 +- code/graphics/2d.cpp | 5 +- code/graphics/generic.cpp | 8 +- code/hud/hud.cpp | 2 +- code/hud/hudlock.cpp | 4 + code/hud/hudparse.cpp | 10 + code/hud/hudparse.h | 1 + code/hud/hudreticle.cpp | 78 +++++- code/hud/hudreticle.h | 3 + code/hud/hudtarget.cpp | 2 +- code/lab/lab.cpp | 11 +- code/mission/missionparse.cpp | 13 +- code/mission/missionparse.h | 2 + code/missionui/missionscreencommon.cpp | 5 + code/mod_table/mod_table.cpp | 6 +- code/model/model.h | 3 +- code/model/modelcollide.cpp | 17 +- code/model/modelinterp.cpp | 82 ++++--- code/model/modelread.cpp | 31 ++- code/object/collideshipship.cpp | 4 + code/object/collideshipweapon.cpp | 24 +- code/object/objectshield.cpp | 16 +- code/object/objectsnd.cpp | 17 +- code/parse/lua.cpp | 25 +- code/parse/sexp.cpp | 4 +- code/parse/sexp.h | 2 +- code/playerman/managepilot.cpp | 4 + code/playerman/playercontrol.cpp | 8 +- code/ship/ship.cpp | 159 +++++++++++- code/ship/ship.h | 24 +- code/sound/sound.cpp | 10 +- code/sound/sound.h | 2 + code/weapon/weapon.h | 4 + code/weapon/weapons.cpp | 30 ++- 48 files changed, 886 insertions(+), 246 deletions(-) diff --git a/code/ai/ai_profiles.cpp b/code/ai/ai_profiles.cpp index 5effacb39d..7a92475922 100644 --- a/code/ai/ai_profiles.cpp +++ b/code/ai/ai_profiles.cpp @@ -465,6 +465,15 @@ void parse_ai_profiles_tbl(const char *filename) set_flag(profile, "$strict turret-tagged-only targeting:", AIPF2_STRICT_TURRET_TAGGED_ONLY_TARGETING, AIP_FLAG2); + profile->bay_arrive_speed_mult = 1.0f; + profile->bay_depart_speed_mult = 1.0f; + if (optional_string("$bay arrive start speed multiplier:")) { + stuff_float(&profile->bay_arrive_speed_mult); + } + if (optional_string("$bay depart end speed multiplier:")) { + stuff_float(&profile->bay_depart_speed_mult); + } + // ---------- // compatibility diff --git a/code/ai/ai_profiles.h b/code/ai/ai_profiles.h index b213efcea7..5624fd226c 100644 --- a/code/ai/ai_profiles.h +++ b/code/ai/ai_profiles.h @@ -151,6 +151,11 @@ typedef struct ai_profile_t { int ai_path_mode; + // Ships flying bay paths will gradually accelerate/decelerate instead of + // flying the whole path at max speed + float bay_arrive_speed_mult; + float bay_depart_speed_mult; + } ai_profile_t; diff --git a/code/ai/aicode.cpp b/code/ai/aicode.cpp index db717892c3..6fc37d5b86 100644 --- a/code/ai/aicode.cpp +++ b/code/ai/aicode.cpp @@ -4010,11 +4010,57 @@ float ai_path_1() dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp); // This path mode respects the "cap-waypoint-speed" SEXP - float max_allowed_speed = 0; + float max_allowed_speed = 0.0f; if ( aip->waypoint_speed_cap > 0) { max_allowed_speed = (float) aip->waypoint_speed_cap; } + // Maybe gradually ramp up/down the speed of a ship flying a fighterbay path + if (aip->mode == AIM_BAY_EMERGE || (aip->mode == AIM_BAY_DEPART && aip->path_cur != aip->path_start)) { + SCP_string pathName(pm->paths[Path_points[aip->path_start].path_num].name); + ship_info *gsip = &Ship_info[gshipp->ship_info_index]; + float speed_mult = 1.0f; + + if (aip->mode == AIM_BAY_EMERGE) { // Arriving + if (gsip->pathMetadata.find(pathName) != gsip->pathMetadata.end()) { + speed_mult = gsip->pathMetadata[pathName].arrive_speed_mult; + } + + if (speed_mult == FLT_MIN) { + speed_mult = The_mission.ai_profile->bay_arrive_speed_mult; + } + } else { // Departing + if (gsip->pathMetadata.find(pathName) != gsip->pathMetadata.end()) { + speed_mult = gsip->pathMetadata[pathName].depart_speed_mult; + } + + if (speed_mult == FLT_MIN) { + speed_mult = The_mission.ai_profile->bay_depart_speed_mult; + } + } + + if (speed_mult != 1.0f) { + // We use the distance between the first and last point on the path here; it's not accurate + // if the path is not straight, but should be good enough usually; can be changed if necessary. + float total_path_length = vm_vec_dist_quick(&Path_points[aip->path_start].pos, &Path_points[aip->path_start + num_points - 1].pos); + float dist_to_end; + + if (aip->mode == AIM_BAY_EMERGE) { // Arriving + dist_to_end = vm_vec_dist_quick(&Pl_objp->pos, &Path_points[aip->path_start].pos); + } else { // Departing + dist_to_end = vm_vec_dist_quick(&Pl_objp->pos, &Path_points[aip->path_start + num_points - 1].pos); + } + + // Calculate max speed, but respect the waypoint speed cap if it's lower + float max_bay_speed = sip->max_speed * (speed_mult + (1.0f - speed_mult) * (dist_to_end / total_path_length)); + if (max_allowed_speed == 0.0f) { + max_allowed_speed = max_bay_speed; + } else { + max_allowed_speed = MIN(max_allowed_speed, max_bay_speed); + } + } + } + set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip, max_allowed_speed); aip->prev_dot_to_goal = dot; @@ -7582,7 +7628,7 @@ void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dis wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; - if (num_weapon_types && (wip->wi_flags & WIF_LOCKED_HOMING)) { + if (num_weapon_types && (wip->wi_flags & WIF_LOCKED_HOMING) && !(shipp->flags2 & SF2_NO_SECONDARY_LOCKON)) { if (dist_to_enemy > 300.0f - MIN(enemy_radius, 100.0f)) aip->ai_flags |= AIF_SEEK_LOCK; else diff --git a/code/bmpman/bm_internal.h b/code/bmpman/bm_internal.h index e0f6e75202..089ac8b873 100644 --- a/code/bmpman/bm_internal.h +++ b/code/bmpman/bm_internal.h @@ -63,6 +63,7 @@ union bm_extra_info { // stuff for static animations ubyte type; //!< type for individual images char filename[MAX_FILENAME_LEN]; //!< filename for individual images + bool in_subdir; // Whether frames are in their own subdirectory } eff; } ani; diff --git a/code/bmpman/bmpman.cpp b/code/bmpman/bmpman.cpp index 376b3a6402..4395cb46c4 100644 --- a/code/bmpman/bmpman.cpp +++ b/code/bmpman/bmpman.cpp @@ -34,6 +34,7 @@ #include "pngutils/pngutils.h" #include "ship/ship.h" #include "tgautils/tgautils.h" +#include "cfile/cfilesystem.h" #ifdef _WIN32 #include @@ -48,7 +49,20 @@ /** * @todo upgrade this to an inline funciton, taking bitmap_entry and const char* as arguments */ -#define EFF_FILENAME_CHECK { if ( be->type == BM_TYPE_EFF ) strcpy_s( filename, be->info.ani.eff.filename ); else strcpy_s( filename, be->filename ); } +#define EFF_FILENAME_CHECK \ +{ \ + if ( be->type == BM_TYPE_EFF ) { \ + strcpy_s( filename, be->info.ani.eff.filename ); \ + if (be->info.ani.eff.in_subdir) { \ + if (!set_temp_subdir_pathtype(bm_bitmaps[be->info.ani.first_frame].filename)) { \ + mprintf(("BMPMAN: Failed to set temporary pathtype for %s: EFF_FILENAME_CHECK failed!\n", be->filename)); \ + } \ + } \ + } \ + else { \ + strcpy_s( filename, be->filename ); \ + } \ +} // -------------------------------------------------------------------------------------------------------------------- // Monitor variables MONITOR(NumBitmapPage) @@ -78,6 +92,8 @@ const int BM_ANI_NUM_TYPES = sizeof(bm_ani_type_list) / sizeof(ubyte); void(*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL; void(*bm_set_components_32)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL; +extern cf_pathtype Pathtypes[CF_MAX_PATH_TYPES]; + // -------------------------------------------------------------------------------------------------------------------- // Declaration of protected variables (defined in cmdline.cpp). extern int Cmdline_cache_bitmaps; @@ -1026,8 +1042,9 @@ int bm_load(const SCP_string& filename) { return bm_load(filename.c_str()); } -int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, ubyte *type) { +int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, ubyte *type, bool *in_subdir) { int frames = 0, fps = 30, keyframe = 0, rval; + bool subdir = false; char ext[8]; ubyte c_type = BM_TYPE_NONE; char file_text[1024]; @@ -1062,6 +1079,10 @@ int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int if (optional_string("$Keyframe:")) stuff_int(&keyframe); + if (optional_string( "$Subdir:" )) { + stuff_boolean(&subdir);} + + // done with EFF so unpause parsing so whatever can continue unpause_parse(); @@ -1098,6 +1119,8 @@ int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int if (key) *key = keyframe; + *in_subdir = subdir; + return 0; } @@ -1108,6 +1131,7 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke char filename[MAX_FILENAME_LEN]; int reduced = 0; int anim_fps = 0, anim_frames = 0, key = 0; + bool in_subdir = false; int anim_width = 0, anim_height = 0; ubyte type = BM_TYPE_NONE, eff_type = BM_TYPE_NONE, c_type = BM_TYPE_NONE; int bpp = 0, mm_lvl = 0, img_size = 0; @@ -1194,7 +1218,7 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke // it's an effect file, any readable image type with eff being txt if (type == BM_TYPE_EFF) { - if (bm_load_and_parse_eff(filename, dir_type, &anim_frames, &anim_fps, &key, &eff_type) != 0) { + if (bm_load_and_parse_eff(filename, dir_type, &anim_frames, &anim_fps, &key, &eff_type, &in_subdir) != 0 ) { mprintf(("BMPMAN: Error reading EFF\n")); return -1; } else { @@ -1256,6 +1280,10 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke return -1; } + if (in_subdir) { + set_temp_subdir_pathtype(filename); + } + int first_handle = bm_get_next_handle(); Assert(strlen(filename) < MAX_FILENAME_LEN); @@ -1321,6 +1349,11 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke bm_bitmaps[n + i].num_mipmaps = mm_lvl; bm_bitmaps[n + i].mem_taken = img_size; bm_bitmaps[n + i].dir_type = dir_type; + bm_bitmaps[n + i].info.ani.eff.in_subdir = in_subdir; + if (in_subdir) + bm_bitmaps[n + i].dir_type = CF_TYPE_TEMP_SUBDIR_LOOKUP; + else + bm_bitmaps[n + i].dir_type = dir_type; bm_bitmaps[n + i].load_count++; @@ -1494,8 +1527,8 @@ bitmap * bm_lock(int handle, ubyte bpp, ubyte flags, bool nodebug) { // read the file data if (gr_bm_lock(be->filename, handle, bitmapnum, bpp, flags, nodebug) == -1) { // oops, this isn't good - reset and return NULL - bm_unlock(bitmapnum); - bm_unload(bitmapnum); + bm_unlock( handle ); + bm_unload( handle ); return NULL; } @@ -2064,6 +2097,61 @@ void bm_page_in_start() { gr_bm_page_in_start(); } +/** + * Stuffs Pathtypes with an additional path leading to a subdir of the same name as the given file (minus file extension). + * Used for allowing frames of .eff animations to be found in their own subdirectories. + * + * Example: if called with "debris.eff" and that file is in data/maps, then Pathtypes[CF_TYPE_TEMP_SUBDIR_LOOKUP].path + * will be stuffed with data/maps/debris, allowing data/maps/debris/debris_0000.dds (etc) to be found + */ +bool set_temp_subdir_pathtype(const char *filename) { + // Gets the absolute path of the file, removes the part that is identical with the + // absolute root path and appends the filename to it (without extension), giving us + // a relative path that can be temporarily stuffed into Pathtypes so the frames can be found + + char fullpath[MAX_PATH]; // Absolute path to the given file + char fullrootpath[MAX_PATH]; // Absolute root path + int size, offset; // Needed for cf_find_file_location, but not used + + cf_create_default_path_string(fullrootpath, sizeof(fullrootpath) - 1, CF_TYPE_ROOT, NULL); + if (!cf_find_file_location(filename, CF_TYPE_ANY, sizeof(fullpath)-1, fullpath, &size, &offset)) { + mprintf(("BMPMAN: Failed to set temporary pathtype for %s: file not found!\n", filename)); + return false; + } + +#ifndef NDEBUG + if (strlen(fullpath) < strlen(fullrootpath)) { + mprintf(("BMPMAN: Path to %s was shorter than the root path, programmer thought it could never happen!\n%s\n%s\n", filename, fullpath, fullrootpath)); + return false; + } +#endif + + mprintf(("BMPMAN: Setting temporary pathtype for %s... ", filename)); + + char *trimmed_path; // The differing portion of fullpath and fullrootpath + trimmed_path = &fullpath[strlen(fullrootpath)]; + + char *extpos = strstr(trimmed_path, "."); // Where the file extension begins + char basepath[MAX_PATH]; // This will hold the final, relative path + + // Copy the relative path of the given file to basepath, excluding the extension + strncpy(basepath, trimmed_path, extpos-trimmed_path); + + // Make sure it's null-terminated right + basepath[(int)(extpos-trimmed_path)] = '\0'; + + // Free the old string first, if it's not the const initial empty string + if (Pathtypes[CF_TYPE_TEMP_SUBDIR_LOOKUP].path[0] != '\0') { + vm_free(Pathtypes[CF_TYPE_TEMP_SUBDIR_LOOKUP].path); + } + + Pathtypes[CF_TYPE_TEMP_SUBDIR_LOOKUP].path = vm_strdup(basepath); + + mprintf(("OK. Temporary pathtype is now %s.\n", Pathtypes[CF_TYPE_TEMP_SUBDIR_LOOKUP].path)); + + return true; +} + void bm_page_in_stop() { int i; @@ -2082,13 +2170,19 @@ void bm_page_in_stop() { if ((bm_bitmaps[i].type != BM_TYPE_NONE) && (bm_bitmaps[i].type != BM_TYPE_RENDER_TARGET_DYNAMIC) && (bm_bitmaps[i].type != BM_TYPE_RENDER_TARGET_STATIC)) { if (bm_bitmaps[i].preloaded) { if (bm_preloading) { + if (bm_bitmaps[i].type == BM_TYPE_EFF && bm_bitmaps[i].info.ani.eff.in_subdir) { + set_temp_subdir_pathtype(bm_bitmaps[i].filename); + } + if (!gr_preload(bm_bitmaps[i].handle, (bm_bitmaps[i].preloaded == 2))) { mprintf(("Out of VRAM. Done preloading.\n")); bm_preloading = 0; } } else { bm_lock(bm_bitmaps[i].handle, (bm_bitmaps[i].used_flags == BMP_AABITMAP) ? 8 : 16, bm_bitmaps[i].used_flags); - bm_unlock(bm_bitmaps[i].handle); + if (bm_bitmaps[i].ref_count >= 1) { + bm_unlock( bm_bitmaps[i].handle ); + } } n++; diff --git a/code/bmpman/bmpman.h b/code/bmpman/bmpman.h index 552fe9f931..a13025dfa6 100644 --- a/code/bmpman/bmpman.h +++ b/code/bmpman/bmpman.h @@ -415,6 +415,11 @@ void bm_get_frame_usage(int *ntotal, int *nnew); */ int bm_reload(int bitmap_handle, const char* filename); +/** + * @brief Stuffs Pathtypes with an additional path leading to a subdir of the same name as the given file (minus file extension) +*/ +bool set_temp_subdir_pathtype(const char *filename); + /** * @brief Tells bmpman to start keeping track of what bitmaps are used where */ @@ -622,16 +627,17 @@ int bm_set_render_target(int handle, int face = -1); * * @param[in] filename The filename of the .EFF * @param[in] dir_type - * @param[out] nframes (optional) If given, is set to the number of frames this .EFF has - * @param[out] nfps (optional) If given, is set to the fps of this .EFF - * @param[out] key (optional) If given, is set to the keyframe index of this .EFF - * @param[out] type (optional) If given, is set to the BM_TYPE of the .EFF + * @param[out] nframes (optional) If given, is set to the number of frames this .EFF has + * @param[out] nfps (optional) If given, is set to the fps of this .EFF + * @param[out] key (optional) If given, is set to the keyframe index of this .EFF + * @param[out] type (optional) If given, is set to the BM_TYPE of the .EFF + * @param[out] in_subdir (optional) If given, is set to true if the frames of this .EFF are in a subdir * * @returns 0 If successful, * @returns -1 if not successful * * @todo retval should be a bool */ -int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, ubyte *type); +int bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, ubyte *type, bool *in_subdir); #endif diff --git a/code/cfile/cfile.cpp b/code/cfile/cfile.cpp index 9d009da1a3..4c75140758 100644 --- a/code/cfile/cfile.cpp +++ b/code/cfile/cfile.cpp @@ -86,6 +86,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = { { CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA }, { CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc", CF_TYPE_DATA }, { CF_TYPE_FICTION, "data" DIR_SEPARATOR_STR "fiction", ".txt", CF_TYPE_DATA }, + { CF_TYPE_TEMP_SUBDIR_LOOKUP, "", ".eff", CF_TYPE_DATA }, }; diff --git a/code/cfile/cfile.h b/code/cfile/cfile.h index 8629526d26..97bcc13139 100644 --- a/code/cfile/cfile.h +++ b/code/cfile/cfile.h @@ -78,8 +78,9 @@ typedef struct { #define CF_TYPE_INTEL_ANIMS 34 #define CF_TYPE_SCRIPTS 35 #define CF_TYPE_FICTION 36 +#define CF_TYPE_TEMP_SUBDIR_LOOKUP 37 -#define CF_MAX_PATH_TYPES 37 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES +#define CF_MAX_PATH_TYPES 38 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES // TRUE if type is specified and valid diff --git a/code/cfile/cfilesystem.cpp b/code/cfile/cfilesystem.cpp index 706fadb348..5f3a9cf4e7 100644 --- a/code/cfile/cfilesystem.cpp +++ b/code/cfile/cfilesystem.cpp @@ -901,6 +901,7 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char case CF_TYPE_MULTI_CACHE: case CF_TYPE_MISSIONS: case CF_TYPE_CACHE: + case CF_TYPE_TEMP_SUBDIR_LOOKUP: cfs_slow_search = 1; break; diff --git a/code/controlconfig/controlsconfig.cpp b/code/controlconfig/controlsconfig.cpp index 9b1e04cc68..7797d83e81 100644 --- a/code/controlconfig/controlsconfig.cpp +++ b/code/controlconfig/controlsconfig.cpp @@ -956,9 +956,9 @@ void control_config_reset_defaults(int presetnum) // Reset keyboard defaults for (i=0; i 1) { + SCP_string preset_str; + int matching_preset = -1; + + for (i=0; i<(int)Control_config_presets.size(); i++) { + bool this_preset_matches = true; + config_item *this_preset = Control_config_presets[i]; + + for (int j=0; j= 0) { + sprintf(preset_str, "Controls: %s", Control_config_preset_names[matching_preset].c_str()); + } else { + sprintf(preset_str, "Controls: custom"); + + } + + gr_get_string_size(&w, NULL, preset_str.c_str()); + gr_set_color_fast(&Color_text_normal); + + if (gr_screen.res == GR_640) { + gr_string(16, (24 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU); + } else { + gr_string(24, (40 - font_height) / 2, preset_str.c_str(), GR_RESIZE_MENU); + } + } + // blit help overlay if active help_overlay_maybe_blit(Control_config_overlay_id, gr_screen.res); @@ -2207,11 +2246,11 @@ int check_control_used(int id, int key) // check what current modifiers are pressed mask = 0; - if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT)) { + if (keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]) { mask |= KEY_SHIFTED; } - if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT)) { + if (keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT]) { mask |= KEY_ALTED; } @@ -2226,7 +2265,7 @@ int check_control_used(int id, int key) z &= KEY_MASK; - if (keyd_pressed[z] || key_down_count(z)) { + if (keyd_pressed[z]) { control_used(id); return 1; } diff --git a/code/controlconfig/controlsconfig.h b/code/controlconfig/controlsconfig.h index eabd2cdda2..95369648d7 100644 --- a/code/controlconfig/controlsconfig.h +++ b/code/controlconfig/controlsconfig.h @@ -63,13 +63,14 @@ typedef struct config_item { short joy_default; //!< default joystick button bound to action char tab; //!< what tab (category) it belongs in bool hasXSTR; //!< whether we should translate this with an XSTR - char *text; //!< describes the action in the config screen + char *default_text; //!< default description of the action char type; //!< manner control should be checked in short key_id; //!< actual key bound to action short joy_id; //!< joystick button bound to action int used; //!< has control been used yet in mission? If so, this is the timestamp bool disabled; //!< whether this action should be available at all bool continuous_ongoing;//!< whether this action is a continuous one and is currently ongoing + char *text; //!< overridable description of the action } config_item; /*! @@ -292,6 +293,7 @@ extern int Control_config_overlay_id; extern config_item Control_config[]; //!< Stores the keyboard configuration extern SCP_vector Control_config_presets; // tabled control presets; pointers to config_item arrays +extern SCP_vector Control_config_preset_names; // names for Control_config_presets (identical order of items) extern char **Scan_code_text; extern char **Joy_button_text; diff --git a/code/controlconfig/controlsconfigcommon.cpp b/code/controlconfig/controlsconfigcommon.cpp index acca5ade60..a115a71add 100644 --- a/code/controlconfig/controlsconfigcommon.cpp +++ b/code/controlconfig/controlsconfigcommon.cpp @@ -45,153 +45,153 @@ int Invert_axis_defaults[JOY_NUM_AXES] = { 0, 0, 0, 0, 0, 0 }; //XSTR:OFF config_item Control_config[CCFG_MAX + 1] = { // targeting a ship - { KEY_T, -1, TARGET_TAB, true, "Target Next Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_T, -1, TARGET_TAB, true, "Target Previous Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_H, 2, TARGET_TAB, true, "Target Next Closest Hostile Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_H, -1, TARGET_TAB, true, "Target Previous Closest Hostile Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_H, -1, TARGET_TAB, true, "Toggle Auto Targeting", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_F, -1, TARGET_TAB, true, "Target Next Closest Friendly Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_F, -1, TARGET_TAB, true, "Target Previous Closest Friendly Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_Y, 4, TARGET_TAB, true, "Target Ship in Reticle", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_G, -1, TARGET_TAB, true, "Target Target's Nearest Attacker", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_Y, -1, TARGET_TAB, true, "Target Last Ship to Send Transmission", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_T, -1, TARGET_TAB, true, "Turn Off Targeting", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_T, -1, TARGET_TAB, true, "Target Next Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_T, -1, TARGET_TAB, true, "Target Previous Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_H, 2, TARGET_TAB, true, "Target Next Closest Hostile Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_H, -1, TARGET_TAB, true, "Target Previous Closest Hostile Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_H, -1, TARGET_TAB, true, "Toggle Auto Targeting", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_F, -1, TARGET_TAB, true, "Target Next Closest Friendly Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_F, -1, TARGET_TAB, true, "Target Previous Closest Friendly Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_Y, 4, TARGET_TAB, true, "Target Ship in Reticle", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_G, -1, TARGET_TAB, true, "Target Target's Nearest Attacker", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_Y, -1, TARGET_TAB, true, "Target Last Ship to Send Transmission", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_T, -1, TARGET_TAB, true, "Turn Off Targeting", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // targeting a ship's subsystem - { KEY_V, -1, TARGET_TAB, true, "Target Subsystem in Reticle", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_S, -1, TARGET_TAB, true, "Target Next Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_S, -1, TARGET_TAB, true, "Target Previous Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_S, -1, TARGET_TAB, true, "Turn Off Targeting of Subsystems", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_V, -1, TARGET_TAB, true, "Target Subsystem in Reticle", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_S, -1, TARGET_TAB, true, "Target Next Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_S, -1, TARGET_TAB, true, "Target Previous Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_S, -1, TARGET_TAB, true, "Turn Off Targeting of Subsystems", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // matching speed - { KEY_M, -1, COMPUTER_TAB, true, "Match Target Speed", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_M, -1, COMPUTER_TAB, true, "Toggle Auto Speed Matching", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_M, -1, COMPUTER_TAB, true, "Match Target Speed", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_M, -1, COMPUTER_TAB, true, "Toggle Auto Speed Matching", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // weapons - { KEY_LCTRL, 0, WEAPON_TAB, true, "Fire Primary Weapon", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_SPACEBAR, 1, WEAPON_TAB, true, "Fire Secondary Weapon", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PERIOD, -1, WEAPON_TAB, true, "Cycle Forward Primary Weapon", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_COMMA, -1, WEAPON_TAB, true, "Cycle Backward Primary Weapon", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_DIVIDE, -1, WEAPON_TAB, true, "Cycle Secondary Weapon Bank", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_DIVIDE, -1, WEAPON_TAB, true, "Cycle Secondary Weapon Firing Rate", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_X, 3, WEAPON_TAB, true, "Launch Countermeasure", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_LCTRL, 0, WEAPON_TAB, true, "Fire Primary Weapon", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_SPACEBAR, 1, WEAPON_TAB, true, "Fire Secondary Weapon", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PERIOD, -1, WEAPON_TAB, true, "Cycle Forward Primary Weapon", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_COMMA, -1, WEAPON_TAB, true, "Cycle Backward Primary Weapon", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_DIVIDE, -1, WEAPON_TAB, true, "Cycle Secondary Weapon Bank", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_DIVIDE, -1, WEAPON_TAB, true, "Cycle Secondary Weapon Firing Rate", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_X, 3, WEAPON_TAB, true, "Launch Countermeasure", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // controls - { KEY_A, -1, SHIP_TAB, true, "Forward Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_Z, -1, SHIP_TAB, true, "Reverse Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD7, -1, SHIP_TAB, true, "Bank Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD9, -1, SHIP_TAB, true, "Bank Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD8, -1, SHIP_TAB, true, "Pitch Forward", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD2, -1, SHIP_TAB, true, "Pitch Backward", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD4, -1, SHIP_TAB, true, "Turn Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD6, -1, SHIP_TAB, true, "Turn Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, + { KEY_A, -1, SHIP_TAB, true, "Forward Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_Z, -1, SHIP_TAB, true, "Reverse Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD7, -1, SHIP_TAB, true, "Bank Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD9, -1, SHIP_TAB, true, "Bank Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD8, -1, SHIP_TAB, true, "Pitch Forward", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD2, -1, SHIP_TAB, true, "Pitch Backward", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD4, -1, SHIP_TAB, true, "Turn Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD6, -1, SHIP_TAB, true, "Turn Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, // throttle controls - { KEY_BACKSP, -1, SHIP_TAB, true, "Set Throttle to Zero", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SLASH, -1, SHIP_TAB, true, "Set Throttle to Max", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_LBRACKET, -1, SHIP_TAB, true, "Set Throttle to One-Third", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_RBRACKET, -1, SHIP_TAB, true, "Set Throttle to Two-Thirds", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_EQUAL, -1, SHIP_TAB, true, "Increase Throttle 5 Percent", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_MINUS, -1, SHIP_TAB, true, "Decrease Throttle 5 Percent", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_BACKSP, -1, SHIP_TAB, true, "Set Throttle to Zero", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SLASH, -1, SHIP_TAB, true, "Set Throttle to Max", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_LBRACKET, -1, SHIP_TAB, true, "Set Throttle to One-Third", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_RBRACKET, -1, SHIP_TAB, true, "Set Throttle to Two-Thirds", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_EQUAL, -1, SHIP_TAB, true, "Increase Throttle 5 Percent", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_MINUS, -1, SHIP_TAB, true, "Decrease Throttle 5 Percent", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // squadmate messaging - { KEY_SHIFTED | KEY_A, -1, COMPUTER_TAB, true, "Attack My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_Z, -1, COMPUTER_TAB, true, "Disarm My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_D, -1, COMPUTER_TAB, true, "Disable My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_V, -1, COMPUTER_TAB, true, "Attack My Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_X, -1, COMPUTER_TAB, true, "Capture My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, true, "Engage Enemy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_W, -1, COMPUTER_TAB, true, "Form on My Wing", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_I, -1, COMPUTER_TAB, true, "Ignore My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_P, -1, COMPUTER_TAB, true, "Protect My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_C, -1, COMPUTER_TAB, true, "Cover Me", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_J, -1, COMPUTER_TAB, true, "Return to Base", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_R, -1, COMPUTER_TAB, true, "Rearm Me", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_SHIFTED | KEY_A, -1, COMPUTER_TAB, true, "Attack My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_Z, -1, COMPUTER_TAB, true, "Disarm My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_D, -1, COMPUTER_TAB, true, "Disable My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_V, -1, COMPUTER_TAB, true, "Attack My Subsystem", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_X, -1, COMPUTER_TAB, true, "Capture My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, true, "Engage Enemy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_W, -1, COMPUTER_TAB, true, "Form on My Wing", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_I, -1, COMPUTER_TAB, true, "Ignore My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_P, -1, COMPUTER_TAB, true, "Protect My Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_C, -1, COMPUTER_TAB, true, "Cover Me", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_J, -1, COMPUTER_TAB, true, "Return to Base", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_R, -1, COMPUTER_TAB, true, "Rearm Me", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, - { KEY_R, 6, TARGET_TAB, true, "Target Closest Attacking Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_R, 6, TARGET_TAB, true, "Target Closest Attacking Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // Views - { KEY_PADMULTIPLY, -1, COMPUTER_TAB, true, "Chase View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PADPERIOD, -1, COMPUTER_TAB, true, "External View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PADENTER, -1, COMPUTER_TAB, true, "Toggle External Camera Lock", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PAD0, -1, COMPUTER_TAB, true, "Free Look View", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, // Not in use anymore (Swifty) - { KEY_PADDIVIDE, -1, COMPUTER_TAB, true, "Current Target View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PADPLUS, -1, COMPUTER_TAB, true, "Increase View Distance", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PADMINUS, -1, COMPUTER_TAB, true, "Decrease View Distance", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_PAD5, -1, COMPUTER_TAB, true, "Center View", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { -1, 33, COMPUTER_TAB, true, "View Up", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { -1, 32, COMPUTER_TAB, true, "View Rear", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { -1, 34, COMPUTER_TAB, true, "View Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { -1, 35, COMPUTER_TAB, true, "View Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, + { KEY_PADMULTIPLY, -1, COMPUTER_TAB, true, "Chase View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PADPERIOD, -1, COMPUTER_TAB, true, "External View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PADENTER, -1, COMPUTER_TAB, true, "Toggle External Camera Lock", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PAD0, -1, COMPUTER_TAB, true, "Free Look View", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, // Not in use anymore (Swifty) + { KEY_PADDIVIDE, -1, COMPUTER_TAB, true, "Current Target View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PADPLUS, -1, COMPUTER_TAB, true, "Increase View Distance", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PADMINUS, -1, COMPUTER_TAB, true, "Decrease View Distance", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_PAD5, -1, COMPUTER_TAB, true, "Center View", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { -1, 33, COMPUTER_TAB, true, "View Up", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { -1, 32, COMPUTER_TAB, true, "View Rear", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { -1, 34, COMPUTER_TAB, true, "View Left", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { -1, 35, COMPUTER_TAB, true, "View Right", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, - { KEY_RAPOSTRO, -1, COMPUTER_TAB, true, "Cycle Radar Range", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_C, -1, COMPUTER_TAB, true, "Communications Menu", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { -1, -1, -1, true, "Show Objectives", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_J, -1, COMPUTER_TAB, true, "Enter Subspace (End Mission)", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_J, -1, TARGET_TAB, true, "Target Target's Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_TAB, 5, SHIP_TAB, true, "Afterburner", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, + { KEY_RAPOSTRO, -1, COMPUTER_TAB, true, "Cycle Radar Range", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_C, -1, COMPUTER_TAB, true, "Communications Menu", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { -1, -1, -1, true, "Show Objectives", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_J, -1, COMPUTER_TAB, true, "Enter Subspace (End Mission)", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_J, -1, TARGET_TAB, true, "Target Target's Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_TAB, 5, SHIP_TAB, true, "Afterburner", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, - { KEY_INSERT, -1, COMPUTER_TAB, true, "Increase Weapon Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_DELETE, -1, COMPUTER_TAB, true, "Decrease Weapon Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_HOME, -1, COMPUTER_TAB, true, "Increase Shield Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_END, -1, COMPUTER_TAB, true, "Decrease Shield Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PAGEUP, -1, COMPUTER_TAB, true, "Increase Engine Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_PAGEDOWN, -1, COMPUTER_TAB, true, "Decrease Engine Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_D, -1, COMPUTER_TAB, true, "Equalize Energy Settings", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_INSERT, -1, COMPUTER_TAB, true, "Increase Weapon Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_DELETE, -1, COMPUTER_TAB, true, "Decrease Weapon Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_HOME, -1, COMPUTER_TAB, true, "Increase Shield Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_END, -1, COMPUTER_TAB, true, "Decrease Shield Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PAGEUP, -1, COMPUTER_TAB, true, "Increase Engine Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_PAGEDOWN, -1, COMPUTER_TAB, true, "Decrease Engine Energy", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_D, -1, COMPUTER_TAB, true, "Equalize Energy Settings", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, - { KEY_Q, 7, COMPUTER_TAB, true, "Equalize Shields", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_UP, -1, COMPUTER_TAB, true, "Augment Forward Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_DOWN, -1, COMPUTER_TAB, true, "Augment Rear Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_LEFT, -1, COMPUTER_TAB, true, "Augment Left Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_RIGHT, -1, COMPUTER_TAB, true, "Augment Right Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SCROLLOCK, -1, COMPUTER_TAB, true, "Transfer Energy Laser->Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_SCROLLOCK, -1, COMPUTER_TAB, true, "Transfer Energy Shield->Laser", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_Q, 7, COMPUTER_TAB, true, "Equalize Shields", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_UP, -1, COMPUTER_TAB, true, "Augment Forward Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_DOWN, -1, COMPUTER_TAB, true, "Augment Rear Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_LEFT, -1, COMPUTER_TAB, true, "Augment Left Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_RIGHT, -1, COMPUTER_TAB, true, "Augment Right Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SCROLLOCK, -1, COMPUTER_TAB, true, "Transfer Energy Laser->Shield", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_SCROLLOCK, -1, COMPUTER_TAB, true, "Transfer Energy Shield->Laser", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // { -1, -1, -1, true, "Show Damage Popup Window" }, - { -1, -1, SHIP_TAB, false, "Glide When Pressed", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, + { -1, -1, SHIP_TAB, false, "Glide When Pressed", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, //Backslash -- this was a convenient place for Glide When Pressed, as Show Damage Popup isn't used - { -1, -1, SHIP_TAB, true, "Bank When Pressed", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { -1, -1, -1, true, "Show Nav Map", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_E, -1, COMPUTER_TAB, true, "Add or Remove Escort", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, true, "Clear Escort List", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_E, -1, TARGET_TAB, true, "Target Next Escort Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_R, -1, TARGET_TAB, true, "Target Closest Repair Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { -1, -1, SHIP_TAB, true, "Bank When Pressed", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { -1, -1, -1, true, "Show Nav Map", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_E, -1, COMPUTER_TAB, true, "Add or Remove Escort", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, true, "Clear Escort List", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_E, -1, TARGET_TAB, true, "Target Next Escort Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_R, -1, TARGET_TAB, true, "Target Closest Repair Ship", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, - { KEY_U, -1, TARGET_TAB, true, "Target Next Uninspected Cargo", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_U, -1, TARGET_TAB, true, "Target Previous Uninspected Cargo", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_N, -1, TARGET_TAB, true, "Target Newest Ship in Area", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_K, -1, TARGET_TAB, true, "Target Next Live Turret", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_K, -1, TARGET_TAB, true, "Target Previous Live Turret", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_U, -1, TARGET_TAB, true, "Target Next Uninspected Cargo", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_U, -1, TARGET_TAB, true, "Target Previous Uninspected Cargo", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_N, -1, TARGET_TAB, true, "Target Newest Ship in Area", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_K, -1, TARGET_TAB, true, "Target Next Live Turret", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_K, -1, TARGET_TAB, true, "Target Previous Live Turret", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, - { KEY_B, -1, TARGET_TAB, true, "Target Next Hostile Bomb or Bomber", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_B, -1, TARGET_TAB, true, "Target Previous Hostile Bomb or Bomber", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_B, -1, TARGET_TAB, true, "Target Next Hostile Bomb or Bomber", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_B, -1, TARGET_TAB, true, "Target Previous Hostile Bomb or Bomber", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // multiplayer messaging keys - { KEY_1, -1, COMPUTER_TAB, true, "(Multiplayer) Message All", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_2, -1, COMPUTER_TAB, true, "(Multiplayer) Message Friendly", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_3, -1, COMPUTER_TAB, true, "(Multiplayer) Message Hostile", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_4, -1, COMPUTER_TAB, true, "(Multiplayer) Message Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_X, -1, COMPUTER_TAB, true, "(Multiplayer) Observer Zoom to Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_PERIOD, -1, COMPUTER_TAB, true, "Increase Time Compression", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_COMMA, -1, COMPUTER_TAB, true, "Decrease Time Compression", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_L, -1, COMPUTER_TAB, true, "Toggle High HUD Contrast", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_N, -1, COMPUTER_TAB, true, "(Multiplayer) Toggle Network Info", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_END, -1, COMPUTER_TAB, true, "(Multiplayer) Self Destruct", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_1, -1, COMPUTER_TAB, true, "(Multiplayer) Message All", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_2, -1, COMPUTER_TAB, true, "(Multiplayer) Message Friendly", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_3, -1, COMPUTER_TAB, true, "(Multiplayer) Message Hostile", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_4, -1, COMPUTER_TAB, true, "(Multiplayer) Message Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_X, -1, COMPUTER_TAB, true, "(Multiplayer) Observer Zoom to Target", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_PERIOD, -1, COMPUTER_TAB, true, "Increase Time Compression", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_COMMA, -1, COMPUTER_TAB, true, "Decrease Time Compression", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_L, -1, COMPUTER_TAB, true, "Toggle High HUD Contrast", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_N, -1, COMPUTER_TAB, true, "(Multiplayer) Toggle Network Info", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_END, -1, COMPUTER_TAB, true, "(Multiplayer) Self Destruct", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // Misc - { KEY_SHIFTED | KEY_O, -1, COMPUTER_TAB, true, "Toggle HUD", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_3, -1, SHIP_TAB, true, "Right Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_1, -1, SHIP_TAB, true, "Left Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_PADPLUS, -1, SHIP_TAB, true, "Up Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_SHIFTED | KEY_PADENTER, -1, SHIP_TAB, true, "Down Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_SHIFTED | KEY_Q, -1, COMPUTER_TAB, true, "Toggle HUD Wireframe Target View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { -1, -1, COMPUTER_TAB, false, "Top-Down View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { -1, -1, COMPUTER_TAB, false, "Target Padlock View", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, // (Swifty) Toggle for VM_TRACK + { KEY_SHIFTED | KEY_O, -1, COMPUTER_TAB, true, "Toggle HUD", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_3, -1, SHIP_TAB, true, "Right Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_1, -1, SHIP_TAB, true, "Left Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_PADPLUS, -1, SHIP_TAB, true, "Up Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_SHIFTED | KEY_PADENTER, -1, SHIP_TAB, true, "Down Thrust", CC_TYPE_CONTINUOUS, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_SHIFTED | KEY_Q, -1, COMPUTER_TAB, true, "Toggle HUD Wireframe Target View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { -1, -1, COMPUTER_TAB, false, "Top-Down View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { -1, -1, COMPUTER_TAB, false, "Target Padlock View", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, // (Swifty) Toggle for VM_TRACK // Auto Navigation Systen - { KEY_ALTED | KEY_A, -1, COMPUTER_TAB, false, "Toggle Auto Pilot", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_N, -1, COMPUTER_TAB, false, "Cycle Nav Points", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_ALTED | KEY_G, -1, SHIP_TAB, false, "Toggle Gliding", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, - { KEY_O, -1, WEAPON_TAB, false, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER, -1, -1, 0, false, false }, + { KEY_ALTED | KEY_A, -1, COMPUTER_TAB, false, "Toggle Auto Pilot", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_N, -1, COMPUTER_TAB, false, "Cycle Nav Points", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_ALTED | KEY_G, -1, SHIP_TAB, false, "Toggle Gliding", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, + { KEY_O, -1, WEAPON_TAB, false, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER, -1, -1, 0, false, false, NULL }, { -1, -1, -1, false, "", CC_TYPE_TRIGGER, -1, -1, 0, false, false } }; @@ -404,6 +404,7 @@ char **Scan_code_text = Scan_code_text_english; char **Joy_button_text = Joy_button_text_english; SCP_vector Control_config_presets; +SCP_vector Control_config_preset_names; void set_modifier_status() { @@ -591,6 +592,9 @@ void control_config_common_init() for (int i=0; iinstance].ship_info_index].collision_lod > -1) { + mc.lod = Ship_info[Ships[ship_obj->instance].ship_info_index].collision_lod; + } + // check each submodel in turn for (smv = submodel_vector.begin(); smv != submodel_vector.end(); ++smv) { // turn on submodel for collision test diff --git a/code/gamesnd/eventmusic.cpp b/code/gamesnd/eventmusic.cpp index 9e966c4c37..085b21a02e 100644 --- a/code/gamesnd/eventmusic.cpp +++ b/code/gamesnd/eventmusic.cpp @@ -28,15 +28,14 @@ #pragma optimize("", off) -#define DEFAULT_MASTER_EVENT_MUSIC_VOLUME 0.5f - #define HULL_VALUE_TO_PLAY_INTENSE_BATTLE_MUSIC 0.75f //////////////////////////// // Globals //////////////////////////// int Event_Music_battle_started = 0; -float Master_event_music_volume = DEFAULT_MASTER_EVENT_MUSIC_VOLUME; // range is 0->1 +float Default_music_volume = 0.5f; // range is 0->1 +float Master_event_music_volume = Default_music_volume; typedef struct tagSNDPATTERN { int default_next_pattern; // Needed so the next_pattern member can be reset diff --git a/code/gamesnd/eventmusic.h b/code/gamesnd/eventmusic.h index 8a11186bc3..c3a36e2bb9 100644 --- a/code/gamesnd/eventmusic.h +++ b/code/gamesnd/eventmusic.h @@ -41,7 +41,8 @@ extern int Event_Music_battle_started; // flag that will tell us if we've started a battle in the current mission extern int Event_music_enabled; -extern float Master_event_music_volume; // range is 0->1 +extern float Default_music_volume; // range is 0->1 +extern float Master_event_music_volume; // range is 0->1 ///////////////////////////////////////////////////////////////////////////// diff --git a/code/graphics/2d.cpp b/code/graphics/2d.cpp index c0e56add82..a62b89edbf 100644 --- a/code/graphics/2d.cpp +++ b/code/graphics/2d.cpp @@ -33,6 +33,7 @@ #include "io/keycontrol.h" // m!m #include "debugconsole/console.h" #include "debugconsole/console.h" +#include "popup/popup.h" #if defined(SCP_UNIX) && !defined(__APPLE__) @@ -83,7 +84,7 @@ int gr_alpha_test = 0; // Default clipping distances const float Default_min_draw_distance = 1.0f; -const float Default_max_draw_distance = 1e10; +const float Default_max_draw_distance = 10000000.0f; float Min_draw_distance = Default_min_draw_distance; float Max_draw_distance = Default_max_draw_distance; @@ -1771,7 +1772,7 @@ void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float a void gr_flip() { // m!m avoid running CHA_ONFRAME when the "Quit mission" popup is shown. See mantis 2446 for reference - if (!quit_mission_popup_shown) + if (!quit_mission_popup_shown && !popup_active()) { //WMC - Evaluate global hook if not override. Script_system.RunBytecode(Script_globalhook); diff --git a/code/graphics/generic.cpp b/code/graphics/generic.cpp index c51fb059d7..defdc30f08 100644 --- a/code/graphics/generic.cpp +++ b/code/graphics/generic.cpp @@ -220,13 +220,19 @@ int generic_anim_stream(generic_anim *ga) ga->previous_frame = -1; } else { + bool in_subdir; bpp = 32; if(ga->use_hud_color) bpp = 8; - bm_load_and_parse_eff(ga->filename, CF_TYPE_ANY, &ga->num_frames, &anim_fps, &ga->keyframe, 0); + bm_load_and_parse_eff(ga->filename, CF_TYPE_ANY, &ga->num_frames, &anim_fps, &ga->keyframe, 0, &in_subdir); char *p = strrchr( ga->filename, '.' ); if ( p ) *p = 0; + + if (in_subdir) { + set_temp_subdir_pathtype(ga->filename); + } + char frame_name[32]; snprintf(frame_name, 32, "%s_0000", ga->filename); ga->bitmap_id = bm_load(frame_name); diff --git a/code/hud/hud.cpp b/code/hud/hud.cpp index 2ea0aa267b..a61abfe154 100644 --- a/code/hud/hud.cpp +++ b/code/hud/hud.cpp @@ -1090,7 +1090,7 @@ bool HudGauge::canRender() } if (gauge_config == HUD_ETS_GAUGE) { - if (Ships[Player_obj->instance].flags2 & SF2_NO_ETS) { + if (Ships[Player_obj->instance].flags2 & SF2_NO_ETS && !Show_disabled_ets_gauges) { return false; } } diff --git a/code/hud/hudlock.cpp b/code/hud/hudlock.cpp index f8d3b611c3..78f3a2fb30 100644 --- a/code/hud/hudlock.cpp +++ b/code/hud/hudlock.cpp @@ -420,6 +420,10 @@ int hud_abort_lock() return 1; } + if ( Player_ship->flags2 & SF2_NO_SECONDARY_LOCKON ) { + return 1; + } + // if we're on the same team and the team doesn't attack itself, then don't lock! if ((Player_ship->team == target_team) && (!iff_x_attacks_y(Player_ship->team, target_team))) { diff --git a/code/hud/hudparse.cpp b/code/hud/hudparse.cpp index 84c99b8292..4158515459 100644 --- a/code/hud/hudparse.cpp +++ b/code/hud/hudparse.cpp @@ -46,6 +46,7 @@ int Hud_reticle_style = HUD_RETICLE_STYLE_FS2; bool Hud_retail = true; bool Scale_retail_gauges = true; +bool Show_disabled_ets_gauges = false; int Hud_font = -1; @@ -261,6 +262,10 @@ void parse_hud_gauges_tbl(const char *filename) Hud_reticle_style = temp; } + if (optional_string("$Show disabled ETS gauges:")) { + stuff_boolean(&Show_disabled_ets_gauges); + } + int base_res[2]; int ship_idx = -1; int ship_font = -1; @@ -2006,6 +2011,7 @@ void load_gauge_center_reticle(int base_w, int base_h, int hud_font, bool scale_ int scaleX = 15; int scaleY = 10; int size = 5; + int autoaim_frame = -1; if(Hud_reticle_style == HUD_RETICLE_STYLE_FS1) { if(gr_screen.res == GR_640) { @@ -2055,8 +2061,12 @@ void load_gauge_center_reticle(int base_w, int base_h, int hud_font, bool scale_ if (optional_string("Firepoint Y coordinate multiplier:")) stuff_int(&scaleY); + if(optional_string("Autoaim Frame:")) + stuff_int(&autoaim_frame); + hud_gauge->initBitmaps(fname); hud_gauge->initFirepointDisplay(firepoints, scaleX, scaleY, size); + hud_gauge->setAutoaimFrame(autoaim_frame); if(ship_idx->at(0) >= 0) { for (SCP_vector::iterator ship_index = ship_idx->begin(); ship_index != ship_idx->end(); ++ship_index) { diff --git a/code/hud/hudparse.h b/code/hud/hudparse.h index 0a95e8fb0f..3c0822faf3 100644 --- a/code/hud/hudparse.h +++ b/code/hud/hudparse.h @@ -20,6 +20,7 @@ extern float Hud_speed_multiplier; #define HUD_RETICLE_STYLE_FS2 1 extern int Hud_reticle_style; +extern bool Show_disabled_ets_gauges; //Functions int hud_get_gauge_index(char* name); diff --git a/code/hud/hudreticle.cpp b/code/hud/hudreticle.cpp index dc7d6213c5..aa82750478 100644 --- a/code/hud/hudreticle.cpp +++ b/code/hud/hudreticle.cpp @@ -20,6 +20,7 @@ #include "weapon/emp.h" #include "localization/localize.h" #include "network/multi.h" +#include "weapon/weapon.h" #define NUM_RETICLE_ANIS 11 // keep up to date when modifying the number of reticle ani files @@ -228,6 +229,7 @@ static int Threat_lock_frame; // frame offset of current lock flashing warnin HudGaugeReticle::HudGaugeReticle(): HudGauge(HUD_OBJECT_CENTER_RETICLE, HUD_CENTER_RETICLE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_TOPDOWN | VM_OTHER_SHIP), 255, 255, 255) { + has_autoaim_lock = false; } void HudGaugeReticle::initBitmaps(char *fname) @@ -247,9 +249,39 @@ void HudGaugeReticle::initFirepointDisplay(bool firepoint, int scaleX, int scale void HudGaugeReticle::render(float frametime) { + ship_info *sip = &Ship_info[Player_ship->ship_info_index]; + + if (autoaim_frame_offset > 0 || sip->autoaim_lock_snd > -1 || sip->autoaim_lost_snd > -1) { + ship *shipp = &Ships[Objects[Player->objnum].instance]; + ship_weapon *swp = &shipp->weapons; + ai_info *aip = &Ai_info[shipp->ai_index]; + + if (aip->target_objnum != -1) { + bool autoaiming = false; + + autoaiming = in_autoaim_fov(shipp, swp->current_primary_bank, &Objects[aip->target_objnum]); + + if (autoaiming) { + if (!has_autoaim_lock && sip->autoaim_lock_snd > -1) { + snd_play( &Snds[sip->autoaim_lock_snd]); + } + has_autoaim_lock = true; + } + else { + if (has_autoaim_lock && sip->autoaim_lost_snd > -1) { + snd_play( &Snds[sip->autoaim_lost_snd]); + } + has_autoaim_lock = false; + } + } + } + setGaugeColor(HUD_C_BRIGHT); - renderBitmap(crosshair.first_frame, position[0], position[1]); + if (has_autoaim_lock) + renderBitmap(crosshair.first_frame + autoaim_frame_offset, position[0], position[1]); + else + renderBitmap(crosshair.first_frame, position[0], position[1]); if (firepoint_display) { fp.clear(); @@ -277,7 +309,7 @@ void HudGaugeReticle::render(float frametime) void HudGaugeReticle::getFirepointStatus() { //First, get the player ship - ship_info* pship; + ship_info* sip; ship* shipp; polymodel* pm; @@ -285,30 +317,45 @@ void HudGaugeReticle::getFirepointStatus() { if (Objects[Player->objnum].type == OBJ_SHIP) { shipp = &Ships[Objects[Player->objnum].instance]; - pship = &Ship_info[shipp->ship_info_index]; + sip = &Ship_info[shipp->ship_info_index]; //Get the player eyepoint - pm = model_get(pship->model_num); + pm = model_get(sip->model_num); if (pm->n_view_positions == 0) { mprintf(("Model %s does not have a defined eyepoint. Firepoint display could not be generated\n", pm->filename)); } else { - if (pm->n_guns > 0) { - + if (pm->n_guns > 0) { eye eyepoint = pm->view_positions[shipp->current_viewpoint]; vec2d ep = { eyepoint.pnt.xyz.x, eyepoint.pnt.xyz.y }; for (int i = 0; i < pm->n_guns; i++) { - int isactive = 0; + int bankactive = 0; + ship_weapon *swp = &shipp->weapons; if (!timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[i])) - isactive = 1; + bankactive = 1; else if (timestamp_elapsed(shipp->weapons.primary_animation_done_time[i])) - isactive = 1; + bankactive = 1; else if (i == shipp->weapons.current_primary_bank || shipp->flags & SF_PRIMARY_LINKED) - isactive = 2; + bankactive = 2; + + int num_slots = pm->gun_banks[i].num_slots; + for (int j = 0; j < num_slots; j++) { + int fpactive = bankactive; + + if (sip->flags2 & SIF2_DYN_PRIMARY_LINKING) { + // If this firepoint is not among the next shot(s) to be fired, dim it one step + if ( !( (j >= (shipp->last_fired_point[i]+1) % num_slots) && (j <= (shipp->last_fired_point[i]+swp->primary_bank_slot_count[i]) % num_slots) ) ) { + fpactive--; + } + } else if (Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & WIF2_CYCLE) { + // If this firepoint is not the next one to be fired, dim it one step + if (j != (shipp->last_fired_point[i]+1) % num_slots) { + fpactive--; + } + } - for (int j = 0; j < pm->gun_banks[i].num_slots; j++) { vec3d fpfromeye; matrix eye_orient, player_transpose; @@ -317,7 +364,7 @@ void HudGaugeReticle::getFirepointStatus() { vm_matrix_x_matrix(&eye_orient, &player_transpose, &Eye_matrix); vm_vec_rotate(&fpfromeye, &pm->gun_banks[i].pnt[j], &eye_orient); - firepoint tmp = { { fpfromeye.xyz.x - ep.x, ep.y - fpfromeye.xyz.y }, isactive }; + firepoint tmp = { { fpfromeye.xyz.x - ep.x, ep.y - fpfromeye.xyz.y }, fpactive }; fp.push_back(tmp); } } @@ -326,6 +373,13 @@ void HudGaugeReticle::getFirepointStatus() { } } +void HudGaugeReticle::setAutoaimFrame(int framenum) { + if (framenum < 0 || framenum > crosshair.num_frames - 1) + autoaim_frame_offset = 0; + else + autoaim_frame_offset = framenum; +} + void HudGaugeReticle::pageIn() { bm_page_in_aabitmap( crosshair.first_frame, crosshair.num_frames); diff --git a/code/hud/hudreticle.h b/code/hud/hudreticle.h index 3663013d3d..93012126a3 100644 --- a/code/hud/hudreticle.h +++ b/code/hud/hudreticle.h @@ -43,6 +43,8 @@ protected: int firepoint_size; int firepoint_scale_x; int firepoint_scale_y; + int autoaim_frame_offset; + bool has_autoaim_lock; public: HudGaugeReticle(); void render(float frametime); @@ -50,6 +52,7 @@ public: void pageIn(); void initFirepointDisplay(bool firepoint, int scaleX, int scaleY, int size); void getFirepointStatus(); + void setAutoaimFrame(int framenum); }; class HudGaugeThrottle: public HudGauge diff --git a/code/hud/hudtarget.cpp b/code/hud/hudtarget.cpp index 858cde3772..8a52e287f4 100644 --- a/code/hud/hudtarget.cpp +++ b/code/hud/hudtarget.cpp @@ -1095,7 +1095,7 @@ void hud_target_subobject_common(int next_flag) target_shipp = &Ships[Objects[Player_ai->target_objnum].instance]; if (!Player_ai->targeted_subsys) { - start = GET_FIRST(&target_shipp->subsys_list); + start = END_OF_LIST(&target_shipp->subsys_list); } else { start = Player_ai->targeted_subsys; } diff --git a/code/lab/lab.cpp b/code/lab/lab.cpp index 9fae744924..e7b7109456 100644 --- a/code/lab/lab.cpp +++ b/code/lab/lab.cpp @@ -41,6 +41,7 @@ #define LAB_FLAG_FULLY_LOAD (1<<5) // use create_ship() to test the ships #define LAB_FLAG_SHOW_WEAPONS (1<<6) // determines if external weapons models are displayed #define LAB_FLAG_INITIAL_ROTATION (1<<7) // initial rotation setting +#define LAB_FLAG_DESTROYED_SUBSYSTEMS (1<<8) // render model as if all subsystems are destroyed // modes #define LAB_MODE_NONE 0 // not showing anything @@ -684,7 +685,7 @@ void labviewer_add_model_thrusters(ship_info *sip) mst.distortion_bitmap = thruster_distortion_bitmap; mst.use_ab = Lab_thrust_afterburn; - mst.glow_noise = thruster_glow_noise; + mst.glow_noise = thruster_glow_noise * sip->thruster_glow_noise_mult; mst.rotvel = &vmd_zero_vector; if (Lab_mode == LAB_MODE_SHIP) { @@ -904,6 +905,13 @@ void labviewer_render_model(float frametime) } } } + + if (sip != NULL) { + if (Lab_viewer_flags & LAB_FLAG_DESTROYED_SUBSYSTEMS) { + model_show_damaged(Lab_model_num, 1); + } + } + opengl_shader_set_animated_effect(ANIMATED_SHADER_LOADOUTSELECT_FS1); opengl_shader_set_animated_timer(MIN((timer_get_milliseconds()-anim_timer_start)/1500.0f,2.0f)); @@ -1855,6 +1863,7 @@ void labviewer_make_render_options_window(Button *caller) ADD_RENDER_FLAG("Animated Shader", Lab_model_flags, MR_ANIMATED_SHADER); ADD_RENDER_FLAG("Show Ship Weapons", Lab_viewer_flags, LAB_FLAG_SHOW_WEAPONS); ADD_RENDER_FLAG("Initial Rotation", Lab_viewer_flags, LAB_FLAG_INITIAL_ROTATION); + ADD_RENDER_FLAG("Show Destroyed Subsystems", Lab_viewer_flags, LAB_FLAG_DESTROYED_SUBSYSTEMS); // start tree diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 6c19e758f8..94a828a33c 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -1838,6 +1838,7 @@ int parse_create_object_sub(p_object *p_objp) shipp->ship_max_shield_strength = p_objp->ship_max_shield_strength; shipp->ship_max_hull_strength = p_objp->ship_max_hull_strength; + shipp->max_shield_recharge = p_objp->max_shield_recharge; // Goober5000 - ugh, this is really stupid having to do this here; if the // ship creation code was better organized this wouldn't be necessary @@ -2250,7 +2251,7 @@ int parse_create_object_sub(p_object *p_objp) Objects[objnum].hull_strength = p_objp->initial_hull * shipp->ship_max_hull_strength / 100.0f; for (iLoop = 0; iLoopinitial_shields * get_max_shield_quad(&Objects[objnum]) / 100.0f); + Objects[objnum].shield_quadrant[iLoop] = (float) (shipp->max_shield_recharge * p_objp->initial_shields * get_max_shield_quad(&Objects[objnum]) / 100.0f); } // initial velocities now do not apply to ships which warp in after mission starts @@ -3110,6 +3111,7 @@ int parse_object(mission *pm, int flag, p_object *p_objp) } Assert(p_objp->ship_max_hull_strength > 0.0f); // Goober5000: div-0 check (not shield because we might not have one) + p_objp->max_shield_recharge = Ship_info[p_objp->ship_class].max_shield_recharge; // if the kamikaze flag is set, we should have the next flag @@ -4603,8 +4605,9 @@ void resolve_path_masks(int anchor, int *path_mask) Assert(!(anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)); parent_pobjp = mission_parse_get_parse_object(Parse_names[anchor]); - // load model for checking paths - modelnum = model_load(Ship_info[parent_pobjp->ship_class].pof_file, 0, NULL); + // Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway + ship_info *sip = &Ship_info[parent_pobjp->ship_class]; + modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]); // resolve names to indexes *path_mask = 0; @@ -4617,9 +4620,6 @@ void resolve_path_masks(int anchor, int *path_mask) *path_mask |= (1 << bay_path); } - // unload model - model_unload(modelnum); - // cache the result prp->cached_mask = *path_mask; } @@ -7555,6 +7555,7 @@ void mission_bring_in_support_ship( object *requester_objp ) // set support ship hitpoints pobj->ship_max_hull_strength = Ship_info[i].max_hull_strength; pobj->ship_max_shield_strength = Ship_info[i].max_shield_strength; + pobj->max_shield_recharge = Ship_info[i].max_shield_recharge; pobj->team = requester_shipp->team; diff --git a/code/mission/missionparse.h b/code/mission/missionparse.h index 83501a5d64..5f1252efaa 100644 --- a/code/mission/missionparse.h +++ b/code/mission/missionparse.h @@ -424,6 +424,8 @@ public: float ship_max_hull_strength; // Needed to deal with special hitpoints float ship_max_shield_strength; + float max_shield_recharge; + // Goober5000 SCP_vector replacement_textures; diff --git a/code/missionui/missionscreencommon.cpp b/code/missionui/missionscreencommon.cpp index 5edeb2bce3..0a419135b9 100644 --- a/code/missionui/missionscreencommon.cpp +++ b/code/missionui/missionscreencommon.cpp @@ -1513,6 +1513,11 @@ void draw_model_icon(int model_id, int flags, float closeup_zoom, int x, int y, //Assume it's a weapon rot_angles.h = -(PI_2); } + else if(sip->model_icon_angles.p != 0.0f || sip->model_icon_angles.b != 0.0f || sip->model_icon_angles.h != 0.0f) + { + // If non-zero model_icon_angles exists, always use that + rot_angles = sip->model_icon_angles; + } else if(sip->flags & SIF_SMALL_SHIP) { rot_angles.p = -(PI_2); diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index e5c03fcc99..fb3fd566de 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -193,15 +193,15 @@ void parse_mod_table(const char *filename) optional_string("#SOUND SETTINGS"); if (optional_string("$Default Sound Volume:")) { - stuff_float(&Master_sound_volume); + stuff_float(&Default_sound_volume); } if (optional_string("$Default Music Volume:")) { - stuff_float(&Master_event_music_volume); + stuff_float(&Default_music_volume); } if (optional_string("$Default Voice Volume:")) { - stuff_float(&Master_voice_volume); + stuff_float(&Default_voice_volume); } optional_string("#FRED SETTINGS"); diff --git a/code/model/model.h b/code/model/model.h index da32414cce..98f5ce7b19 100644 --- a/code/model/model.h +++ b/code/model/model.h @@ -1022,7 +1022,8 @@ typedef struct mc_info { vec3d *p0; // The starting point of the ray (sphere) to check vec3d *p1; // The ending point of the ray (sphere) to check int flags; // Flags that the model_collide code looks at. See MC_??? defines - float radius; // If MC_CHECK_THICK is set, checks a sphere moving with the radius. + float radius; // If MC_CHECK_THICK is set, checks a sphere moving with the radius. + int lod; // Which detail level of the submodel to check instead // Return values int num_hits; // How many collisions were found diff --git a/code/model/modelcollide.cpp b/code/model/modelcollide.cpp index 387bed5049..da44dddab5 100644 --- a/code/model/modelcollide.cpp +++ b/code/model/modelcollide.cpp @@ -1130,7 +1130,22 @@ void mc_check_subobj( int mn ) if ( Cmdline_old_collision_sys ) { model_collide_sub(sm->bsp_data); } else { - model_collide_bsp(model_get_bsp_collision_tree(sm->collision_tree_index), 0); + if (Mc->lod > 0 && sm->num_details > 0) { + bsp_info *lod_sm = sm; + + for (int i = Mc->lod - 1; i >= 0; i--) { + if (sm->details[i] != -1) { + lod_sm = &Mc_pm->submodel[sm->details[i]]; + + //mprintf(("Checking %s collision for %s using %s instead\n", Mc_pm->filename, sm->name, lod_sm->name)); + break; + } + } + + model_collide_bsp(model_get_bsp_collision_tree(lod_sm->collision_tree_index), 0); + } else { + model_collide_bsp(model_get_bsp_collision_tree(sm->collision_tree_index), 0); + } } } } diff --git a/code/model/modelinterp.cpp b/code/model/modelinterp.cpp index 7eb78b9d58..4a484ae81d 100644 --- a/code/model/modelinterp.cpp +++ b/code/model/modelinterp.cpp @@ -83,7 +83,6 @@ static float Interp_box_scale = 1.0f; // this is used to scale both detail boxes static vec3d Interp_render_box_min = ZERO_VECTOR; static vec3d Interp_render_box_max = ZERO_VECTOR; static float Interp_render_sphere_radius = 0.0f; -static vec3d Interp_render_sphere_offset = ZERO_VECTOR; // ------------------------------------------------------------------- // lighting save stuff @@ -369,7 +368,6 @@ void interp_clear_instance() Interp_render_box_min = vmd_zero_vector; Interp_render_box_max = vmd_zero_vector; Interp_render_sphere_radius = 0.0f; - Interp_render_sphere_offset = vmd_zero_vector; } /** @@ -2728,6 +2726,7 @@ void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, uint save_gr_zbuffering_mode; int zbuf_mode; ship *shipp = NULL; + ship_info *sip = NULL; object *objp = NULL; bool set_autocen = false; bool draw_thrusters = false; @@ -2742,6 +2741,7 @@ void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, if (objp->type == OBJ_SHIP) { shipp = &Ships[objp->instance]; + sip = &Ship_info[shipp->ship_info_index]; if (shipp->flags2 & SF2_GLOWMAPS_DISABLED) flags |= MR_NO_GLOWMAPS; @@ -2905,33 +2905,37 @@ void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, } #endif - // scale the render box settings based on the "Model Detail" slider - switch (Detail.detail_distance) - { - // 1st dot is 20% - case 0: - Interp_box_scale = 0.2f; - break; + if (shipp != NULL && (sip->flags2 & SIF2_LOCK_DETAIL_BOXES)) { + Interp_box_scale = 1.0f; + } else { + // scale the render box settings based on the "Model Detail" slider + switch (Detail.detail_distance) + { + // 1st dot is 20% + case 0: + Interp_box_scale = 0.2f; + break; - // 2nd dot is 50% - case 1: - Interp_box_scale = 0.5f; - break; + // 2nd dot is 50% + case 1: + Interp_box_scale = 0.5f; + break; - // 3rd dot is 80% - case 2: - Interp_box_scale = 0.8f; - break; + // 3rd dot is 80% + case 2: + Interp_box_scale = 0.8f; + break; - // 4th dot is 100% (this is the default setting for "High" and "Very High" settings) - case 3: - Interp_box_scale = 1.0f; - break; + // 4th dot is 100% (this is the default setting for "High" and "Very High" settings) + case 3: + Interp_box_scale = 1.0f; + break; - // 5th dot (max) is 120% - case 4: - Interp_box_scale = 1.2f; - break; + // 5th dot (max) is 120% + case 4: + Interp_box_scale = 1.2f; + break; + } } vec3d auto_back = ZERO_VECTOR; @@ -4468,7 +4472,13 @@ void model_render_children_buffers(polymodel *pm, int mn, int detail_level) vm_vec_copy_scale(&Interp_render_box_min, &model->render_box_min, Interp_box_scale); vm_vec_copy_scale(&Interp_render_box_max, &model->render_box_max, Interp_box_scale); - if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &model->offset)) ) + vec3d offset; + if (model->use_render_box_offset) + offset = model->render_box_offset; + else + model_find_submodel_offset(&offset, pm->id, mn); + + if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &offset)) ) return; } if ( !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) { @@ -4476,8 +4486,10 @@ void model_render_children_buffers(polymodel *pm, int mn, int detail_level) // TODO: doesn't consider submodel rotations yet -zookeeper vec3d offset; - model_find_submodel_offset(&offset, pm->id, mn); - vm_vec_add2(&offset, &model->render_sphere_offset); + if (model->use_render_sphere_offset) + offset = model->render_sphere_offset; + else + model_find_submodel_offset(&offset, pm->id, mn); if ( (-model->use_render_sphere + in_sphere(&offset, Interp_render_sphere_radius)) ) return; @@ -4591,7 +4603,13 @@ void model_render_buffers(polymodel *pm, int mn, bool is_child) vm_vec_copy_scale(&Interp_render_box_min, &model->render_box_min, Interp_box_scale); vm_vec_copy_scale(&Interp_render_box_max, &model->render_box_max, Interp_box_scale); - if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &model->offset)) ) + vec3d offset; + if (model->use_render_box_offset) + offset = model->render_box_offset; + else + model_find_submodel_offset(&offset, pm->id, mn); + + if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &offset)) ) return; } if ( !is_child && !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) { @@ -4599,8 +4617,10 @@ void model_render_buffers(polymodel *pm, int mn, bool is_child) // TODO: doesn't consider submodel rotations yet -zookeeper vec3d offset; - model_find_submodel_offset(&offset, pm->id, mn); - vm_vec_add2(&offset, &model->render_sphere_offset); + if (model->use_render_sphere_offset) + offset = model->render_sphere_offset; + else + model_find_submodel_offset(&offset, pm->id, mn); if ( (-model->use_render_sphere + in_sphere(&offset, Interp_render_sphere_radius)) ) return; diff --git a/code/model/modelread.cpp b/code/model/modelread.cpp index 4213873e3b..aa65277d9f 100644 --- a/code/model/modelread.cpp +++ b/code/model/modelread.cpp @@ -1354,6 +1354,18 @@ int read_model_file(polymodel * pm, char *filename, int n_subsystems, model_subs while (*p == ' ') p++; pm->submodel[n].use_render_box = atoi(p); + if ( (p = strstr(props, "$box_offset:")) != NULL ) { + p += 12; + while (*p == ' ') p++; + pm->submodel[n].render_box_offset.xyz.x = (float)strtod(p, (char **)NULL); + while (*p != ',') p++; + pm->submodel[n].render_box_offset.xyz.y = (float)strtod(++p, (char **)NULL); + while (*p != ',') p++; + pm->submodel[n].render_box_offset.xyz.z = (float)strtod(++p, (char **)NULL); + + pm->submodel[n].use_render_box_offset = true; + } + if ( (p = strstr(props, "$box_min:")) != NULL ) { p += 9; while (*p == ' ') p++; @@ -1400,6 +1412,8 @@ int read_model_file(polymodel * pm, char *filename, int n_subsystems, model_subs pm->submodel[n].render_sphere_offset.xyz.y = (float)strtod(++p, (char **)NULL); while (*p != ',') p++; pm->submodel[n].render_sphere_offset.xyz.z = (float)strtod(++p, (char **)NULL); + + pm->submodel[n].use_render_sphere_offset = true; } else { pm->submodel[n].render_sphere_offset = vmd_zero_vector; } @@ -2466,7 +2480,7 @@ int model_load(char *filename, int n_subsystems, model_subsystem *subsystems, in pm = new polymodel; Polygon_models[num] = pm; - + pm->n_paths = 0; pm->paths = NULL; @@ -2639,6 +2653,12 @@ int model_load(char *filename, int n_subsystems, model_subsystem *subsystems, in } dl2 = tolower(sm2->name[first_diff]) - 'a'; + // Handle LODs named "detail0/1/2/etc" too (as opposed to "detaila/b/c/etc") + if (sm1->parent == -1 && sm2->parent == -1 && !sm1->is_damaged && !sm2->is_damaged && !sm1->is_live_debris && !sm2->is_live_debris) { + dl2 = dl2 - dl1; + dl1 = 0; + } + if ( (dl1<0) || (dl2<0) || (dl1>=MAX_MODEL_DETAIL_LEVELS) || (dl2>=MAX_MODEL_DETAIL_LEVELS) ) continue; // invalid detail levels if ( dl1 == 0 ) { @@ -2663,10 +2683,11 @@ int model_load(char *filename, int n_subsystems, model_subsystem *subsystems, in if ( !Cmdline_old_collision_sys ) { for ( i = 0; i < pm->n_models; ++i ) { - pm->submodel[i].collision_tree_index = model_create_bsp_collision_tree(); - bsp_collision_tree *tree = model_get_bsp_collision_tree(pm->submodel[i].collision_tree_index); - - model_collide_parse_bsp(tree, pm->submodel[i].bsp_data, pm->version); + if ( !(pm->submodel[i].nocollide_this_only || pm->submodel[i].no_collisions) ) { + pm->submodel[i].collision_tree_index = model_create_bsp_collision_tree(); + bsp_collision_tree *tree = model_get_bsp_collision_tree(pm->submodel[i].collision_tree_index); + model_collide_parse_bsp(tree, pm->submodel[i].bsp_data, pm->version); + } } } diff --git a/code/object/collideshipship.cpp b/code/object/collideshipship.cpp index f5f800796e..8f02561867 100644 --- a/code/object/collideshipship.cpp +++ b/code/object/collideshipship.cpp @@ -269,6 +269,10 @@ int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info, vec3d * // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE mc.flags = copy_flags | MC_SUBMODEL_INSTANCE; + if (heavy_sip->collision_lod > -1) { + mc.lod = heavy_sip->collision_lod; + } + // check each submodel in turn for (smv = submodel_vector.begin(); smv != submodel_vector.end(); ++smv) { // turn on submodel for collision test diff --git a/code/object/collideshipweapon.cpp b/code/object/collideshipweapon.cpp index dcf5b956b4..82534bf3db 100644 --- a/code/object/collideshipweapon.cpp +++ b/code/object/collideshipweapon.cpp @@ -168,6 +168,7 @@ int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float ti mc.pos = &ship_objp->pos; mc.p0 = &weapon_objp->last_pos; mc.p1 = &weapon_end_pos; + mc.lod = sip->collision_lod; memcpy(&mc_shield, &mc, sizeof(mc_info)); memcpy(&mc_hull, &mc, sizeof(mc_info)); @@ -200,15 +201,16 @@ int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float ti // The weapon is not allowed to impact the shield before it reaches this point vec3d shield_ignored_until = weapon_objp->last_pos; + float min_weapon_span = sip->auto_shield_spread_min_span; float weapon_flown_for = vm_vec_dist(&wp->start_pos, &weapon_objp->last_pos); // If weapon hasn't yet flown a distance greater than the maximum ignore // range, then some part of the currently checked range needs to be // ignored - if (weapon_flown_for < sip->auto_shield_spread) { + if (weapon_flown_for < min_weapon_span) { vm_vec_sub(&shield_ignored_until, &weapon_end_pos, &wp->start_pos); vm_vec_normalize(&shield_ignored_until); - vm_vec_scale(&shield_ignored_until, sip->auto_shield_spread); + vm_vec_scale(&shield_ignored_until, min_weapon_span); vm_vec_add2(&shield_ignored_until, &wp->start_pos); } @@ -243,7 +245,7 @@ int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float ti // If no collision with the model found in the ignore range, only // then do we check for sphereline collisions with the model during the // non-ignored range - if (!shield_collision && weapon_flown_for + this_range > sip->auto_shield_spread) { + if (!shield_collision && weapon_flown_for + this_range > min_weapon_span) { mc_shield.p0 = &shield_ignored_until; mc_shield.p1 = &weapon_end_pos; @@ -251,16 +253,14 @@ int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float ti mc_shield.radius = sip->auto_shield_spread; if (sip->auto_shield_spread_from_lod > -1) { - pm = model_get(sip->model_num); - mc_shield.submodel_num = pm->detail[sip->auto_shield_spread_from_lod]; - - mc_shield.flags = MC_CHECK_MODEL | MC_SUBMODEL_INSTANCE | MC_CHECK_SPHERELINE; - } else { - mc_shield.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE; + mc_shield.lod = sip->auto_shield_spread_from_lod; } + mc_shield.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE; + shield_collision = model_collide(&mc_shield); + mc_shield.lod = sip->collision_lod; mc_shield.submodel_num = -1; // Because we manipulated p0 and p1 above, hit_dist will be @@ -407,6 +407,12 @@ int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float ti bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, weapon_objp); if(!ship_override && !weapon_override) { + if (shield_collision && quadrant_num >= 0) { + if ((sip->shield_impact_explosion_anim > -1) && (wip->shield_impact_explosion_radius > 0)) { + // do in weapons.cpp for most parts just because that is how rest of the functions are handled + shield_impact_explosion(&mc.hit_point, ship_objp, wip->shield_impact_explosion_radius, sip->shield_impact_explosion_anim); + } + } ship_weapon_do_hit_stuff(ship_objp, weapon_objp, &mc.hit_point_world, &mc.hit_point, quadrant_num, mc.hit_submodel, mc.hit_normal); } diff --git a/code/object/objectshield.cpp b/code/object/objectshield.cpp index 1d9b7a52b7..f382437a8b 100644 --- a/code/object/objectshield.cpp +++ b/code/object/objectshield.cpp @@ -49,10 +49,18 @@ void shield_add_strength(object *objp, float delta) if (delta == 0.0f) return; - + float shield_str = shield_get_strength(objp); + float shield_recharge_limit = Ships[objp->instance].ship_max_shield_strength * Ships[objp->instance].max_shield_recharge; + + if (shield_str >= shield_recharge_limit) + return; + if (!(Ai_info[Ships[objp->instance].ai_index].ai_profile_flags & AIPF_SMART_SHIELD_MANAGEMENT) || delta <= 0.0f) //SUSHI: We don't want smart shield management for negative delta { + if ((delta > 0.0f) && ((shield_str + delta) > shield_recharge_limit)) + delta = shield_recharge_limit - shield_str; + for (int i = 0; i < objp->n_quadrants; i++) shield_add_quad(objp, i, delta / objp->n_quadrants); } @@ -81,7 +89,11 @@ void shield_add_strength(object *objp, float delta) // all quads are at full strength if (weakest >= section_max) break; - + + // set the limit for the shield recharge + if ((delta > 0.0f) && ((shield_str + delta) > shield_recharge_limit)) + delta = shield_recharge_limit - shield_str; + // throw all possible shield power at this quadrant // if there's any left over then apply it to the next weakest on the next pass float xfer_amount; diff --git a/code/object/objectsnd.cpp b/code/object/objectsnd.cpp index e21e354e60..5e3c74876e 100644 --- a/code/object/objectsnd.cpp +++ b/code/object/objectsnd.cpp @@ -521,17 +521,24 @@ void obj_snd_do_frame() rot_vol_mult = 1.0f; alive_vol_mult = 1.0f; if ( objp->type == OBJ_SHIP ) { - if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) { + ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index]; + if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) { 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 ( 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 + 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 = MAX(sip->min_engine_vol, percent_max); } } if (osp->ss != NULL) diff --git a/code/parse/lua.cpp b/code/parse/lua.cpp index 52dc2035b6..f373568453 100644 --- a/code/parse/lua.cpp +++ b/code/parse/lua.cpp @@ -2632,7 +2632,7 @@ ADE_VIRTVAR(RotationalVelocityDamping, l_Physics, "number", "Rotational damping, return ade_set_args(L, "f", pih->pi->rotdamp); } -ADE_VIRTVAR(RotationalVelocityDesired, l_Physics, "vector", "Desired rotational velocity", "vector", "Desired rotational velocity, or null vector if handle is invalid") +ADE_VIRTVAR(RotationalVelocityDesired, l_Physics, "lvector", "Desired rotational velocity", "number", "Desired rotational velocity, or 0 if handle is invalid") { physics_info_h *pih; vec3d *v3=NULL; @@ -15614,6 +15614,29 @@ ADE_FUNC(getStack, l_Testing, NULL, "Generates an ADE stackdump", "string", "Cur return ade_set_args(L, "s", buf); } +ADE_FUNC(setCurrentPlayerMulti, l_Testing, "boolean", "Sets whether current player is a multiplayer pilot or not.", "boolean", "True if successful, false if not.") +{ + bool multi; + + if(!ade_get_args(L, "b", &multi)) + return ADE_RETURN_NIL; + + if(Player == NULL) + return ade_set_error(L, "b", false); + + if (multi) { + Game_mode &= ~GM_NORMAL; + Game_mode |= GM_MULTIPLAYER; + Player->flags |= PLAYER_FLAGS_IS_MULTI; + } else { + Game_mode &= ~GM_MULTIPLAYER; + Game_mode |= GM_NORMAL; + Player->flags &= ~PLAYER_FLAGS_IS_MULTI; + } + + return ADE_RETURN_TRUE; +} + ADE_FUNC(isCurrentPlayerMulti, l_Testing, NULL, "Returns whether current player is a multiplayer pilot or not.", "boolean", "Whether current player is a multiplayer pilot or not") { if(Player == NULL) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 906a2c6e51..7895a7e901 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -16541,6 +16541,7 @@ void parse_copy_damage(p_object *target_pobjp, ship *source_shipp) // ...and shields target_pobjp->ship_max_shield_strength = source_shipp->ship_max_shield_strength; target_pobjp->initial_shields = fl2i(get_shield_pct(source_objp) * 100.0f); + target_pobjp->max_shield_recharge = source_shipp->max_shield_recharge; // search through all subsystems on source ship and map them onto target ship @@ -31465,7 +31466,8 @@ sexp_help_struct Sexp_help[] = { "friendly-stealth-invisible - If set, the ship can't be targeted even by ships on the same team\r\n" "hide-ship-name - If set, the ship name can't be seen when the ship is targeted\r\n" "hidden-from-sensors - If set, the ship can't be targeted and appears on radar as a blinking dot\r\n" - "no-dynamic - Will stop allowing the AI to persue dynamic goals (eg: chasing ships it was not ordered to)\r\n"}, + "no-dynamic - Will stop allowing the AI to persue dynamic goals (eg: chasing ships it was not ordered to)\r\n" + "no-secondary-lock-on - Will disable target acquisition for secondaries of all types (does not affect turrets)\r\n"}, { OP_SHIP_VISIBLE, "ship-visible\r\n" "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n" diff --git a/code/parse/sexp.h b/code/parse/sexp.h index 48fec5a434..6df167f09f 100644 --- a/code/parse/sexp.h +++ b/code/parse/sexp.h @@ -1035,7 +1035,7 @@ typedef struct sexp_node { typedef struct sexp_variable { int type; - char text[TOKEN_LENGTH]; + char text[256]; char variable_name[TOKEN_LENGTH]; } sexp_variable; diff --git a/code/playerman/managepilot.cpp b/code/playerman/managepilot.cpp index 10edd05005..ab89368f83 100644 --- a/code/playerman/managepilot.cpp +++ b/code/playerman/managepilot.cpp @@ -101,6 +101,10 @@ void init_new_pilot(player *p, int reset) Mouse_sensitivity = 4; Joy_sensitivity = 9; Dead_zone_size = 10; + + Master_sound_volume = Default_sound_volume; + Master_event_music_volume = Default_music_volume; + Master_voice_volume = Default_voice_volume; } // unassigned squadron diff --git a/code/playerman/playercontrol.cpp b/code/playerman/playercontrol.cpp index 60b65a8701..f5700eb39a 100644 --- a/code/playerman/playercontrol.cpp +++ b/code/playerman/playercontrol.cpp @@ -637,22 +637,22 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info * if (Axis_map_to[JOY_HEADING_AXIS] >= 0) { // check the heading on the x axis if ( check_control(BANK_WHEN_PRESSED) ) { - delta = f2fl( axis[JOY_HEADING_AXIS] ); + delta = f2fl( axis[Axis_map_to[JOY_HEADING_AXIS]] ); if ( (delta > 0.05f) || (delta < -0.05f) ) { ci->bank -= delta; } } else { - ci->heading += f2fl( axis[JOY_HEADING_AXIS] ); + ci->heading += f2fl( axis[Axis_map_to[JOY_HEADING_AXIS]] ); } } // check the pitch on the y axis if (Axis_map_to[JOY_PITCH_AXIS] >= 0) { - ci->pitch -= f2fl( axis[JOY_PITCH_AXIS] ); + ci->pitch -= f2fl( axis[Axis_map_to[JOY_PITCH_AXIS]] ); } if (Axis_map_to[JOY_BANK_AXIS] >= 0) { - ci->bank -= f2fl( axis[JOY_BANK_AXIS] ) * 1.5f; + ci->bank -= f2fl( axis[Axis_map_to[JOY_BANK_AXIS]] ) * 1.5f; } // axis 2 is for throttle diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index ecdc4a0ef0..972f7d2c9b 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -312,6 +312,7 @@ flag_def_list Ship_flags[] = { { "no lighting", SIF2_NO_LIGHTING, 1 }, { "auto spread shields", SIF2_AUTO_SPREAD_SHIELDS, 1 }, { "model point shields", SIF2_MODEL_POINT_SHIELDS, 1 }, + { "lock detail boxes", SIF2_LOCK_DETAIL_BOXES, 1 }, // to keep things clean, obsolete options go last { "ballistic primaries", -1, 255 } @@ -385,6 +386,7 @@ ship_flag_name Ship_flag_names[] = { {SF2_DONT_COLLIDE_INVIS, "don't-collide-invisible", 2, }, {SF2_NO_ETS, "no-ets", 2, }, {SF2_TOGGLE_SUBSYSTEM_SCANNING, "toggle-subsystem-scanning", 2, }, + {SF2_NO_SECONDARY_LOCKON, "no-secondary-lock-on", 2, }, }; const int num_ai_tgt_weapon_flags = sizeof(ai_tgt_weapon_flags) / sizeof(flag_def_list); @@ -690,6 +692,7 @@ void init_ship_entry(ship_info *sip) sip->pof_file_hud[0] = '\0'; sip->num_detail_levels = 1; sip->detail_distance[0] = 0; + sip->collision_lod = -1; sip->cockpit_model_num = -1; sip->model_num = -1; sip->model_num_hud = -1; @@ -865,9 +868,12 @@ void init_ship_entry(ship_info *sip) sip->max_hull_strength = 100.0f; sip->max_shield_strength = 0.0f; + sip->max_shield_recharge = 1.0f; + sip->auto_shield_spread = 0.0f; sip->auto_shield_spread_bypass = false; sip->auto_shield_spread_from_lod = -1; + sip->auto_shield_spread_min_span = -1.0f; for (i = 0; i < 4; i++) { @@ -948,6 +954,7 @@ void init_ship_entry(ship_info *sip) sip->thruster02_glow_len_factor = 1.0f; sip->thruster_dist_rad_factor = 2.0f; sip->thruster_dist_len_factor = 2.0f; + sip->thruster_glow_noise_mult = 1.0f; sip->draw_distortion = true; @@ -967,11 +974,14 @@ void init_ship_entry(ship_info *sip) sip->newtonian_damp_override = false; sip->autoaim_fov = 0.0f; + sip->autoaim_lock_snd = -1; + sip->autoaim_lost_snd = -1; sip->topdown_offset_def = false; vm_vec_zero(&sip->topdown_offset); sip->engine_snd = -1; + sip->min_engine_vol = -1.0f; sip->glide_start_snd = -1; sip->glide_end_snd = -1; @@ -1009,6 +1019,7 @@ void init_ship_entry(ship_info *sip) sip->damage_lightning_type = SLT_DEFAULT; + sip->shield_impact_explosion_anim = -1; sip->hud_gauges.clear(); sip->hud_enabled = false; sip->hud_retail = false; @@ -1664,6 +1675,13 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) sip->num_detail_levels = stuff_int_list(sip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE); } + if(optional_string("$Collision LOD:")) { + stuff_int(&sip->collision_lod); + + // Cap to sane values, just in case + sip->collision_lod = MAX(0, MIN(sip->collision_lod, sip->num_detail_levels)); + } + // check for optional pixel colors while(optional_string("$ND:")){ ubyte nr, ng, nb; @@ -2000,6 +2018,9 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) if(optional_string("+Minimum Distance:")) stuff_float(&sip->minimum_convergence_distance); + + parse_sound("+Autoaim Lock Snd:", &sip->autoaim_lock_snd, sip->name); + parse_sound("+Autoaim Lost Snd:", &sip->autoaim_lost_snd, sip->name); } if(optional_string("$Convergence:")) @@ -2306,6 +2327,11 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) if(optional_string("+Auto Spread:")) { stuff_float(&sip->auto_shield_spread); } + if(optional_string("+Minimum Weapon Span:")) { + stuff_float(&sip->auto_shield_spread_min_span); + } else { + sip->auto_shield_spread_min_span = sip->auto_shield_spread; + } if(optional_string("+Allow Bypass:")) { stuff_boolean(&sip->auto_shield_spread_bypass); } @@ -2355,6 +2381,19 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) stuff_ubyte(&sip->shield_color[2]); } + if(optional_string("$Shield Impact Explosion:")) { + char fname[MAX_NAME_LEN]; + stuff_string(fname, F_NAME, NAME_LENGTH); + + if ( VALID_FNAME(fname) ) + sip->shield_impact_explosion_anim = Weapon_explosions.Load(fname); + } + + if(optional_string("$Max Shield Recharge:")){ + stuff_float(&sip->max_shield_recharge); + CLAMP(sip->max_shield_recharge, 0.0f, 1.0f); + } + // The next five fields are used for the ETS if (optional_string("$Power Output:")) stuff_float(&sip->power_output); @@ -2660,6 +2699,9 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) //Parse the engine sound parse_sound("$EngineSnd:", &sip->engine_snd, sip->name); + if(optional_string("$Minimum Engine Volume:")) + stuff_float(&sip->min_engine_vol); + //Parse optional sound to be used for beginning of a glide parse_sound("$GlideStartSnd:", &sip->glide_start_snd, sip->name); @@ -2716,6 +2758,32 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) stuff_string(sip->icon_filename, F_NAME, MAX_FILENAME_LEN); } + if ( optional_string("$Model Icon Direction:") ) { + char str[NAME_LENGTH]; + stuff_string(str, F_NAME, NAME_LENGTH); + + angles model_icon_angles = {0.0f,0.0f,0.0f}; + + if (!stricmp(str, "top")) { + model_icon_angles.p = -PI_2; + } else if (!stricmp(str, "bottom")) { + model_icon_angles.p = -PI_2; + model_icon_angles.b = 2 * PI_2; + } else if (!stricmp(str, "front")) { + model_icon_angles.h = 2 * PI_2; + } else if (!stricmp(str, "back")) { + model_icon_angles.h = 4 * PI_2; + } else if (!stricmp(str, "left")) { + model_icon_angles.h = -PI_2; + } else if (!stricmp(str, "right")) { + model_icon_angles.h = PI_2; + } else { + Warning(LOCATION, "Unrecognized value \"%s\" passed to $Model Icon Direction, ignoring...", str); + } + + sip->model_icon_angles = model_icon_angles; + } + // read in filename for animation that is used in ship selection if ( optional_string("$Ship_anim:") ) { stuff_string(sip->anim_filename, F_NAME, MAX_FILENAME_LEN); @@ -2865,6 +2933,10 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) stuff_boolean(&sip->draw_distortion); } + if ( optional_string("$Thruster Glow Noise Mult:") ) { + stuff_float(&sip->thruster_glow_noise_mult); + } + while ( optional_string("$Thruster Particles:") ) { bool afterburner = false; thruster_particles tpart; @@ -3139,6 +3211,15 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace) stuff_vec3d(&metadata.departure_rvec); } + if (optional_string("+arrive speed multiplier:")) + { + stuff_float(&metadata.arrive_speed_mult); + } + if (optional_string("+depart speed multiplier:")) + { + stuff_float(&metadata.depart_speed_mult); + } + //Add the new path_metadata to sip->pathMetadata keyed by path name SCP_string pathName(path_name); sip->pathMetadata[pathName] = metadata; @@ -5019,12 +5100,14 @@ void ship_set(int ship_index, int objnum, int ship_type) } objp->hull_strength = shipp->ship_max_hull_strength; + shipp->max_shield_recharge = sip->max_shield_recharge; + if (Fred_running) { shipp->ship_max_shield_strength = 100.0f; objp->shield_quadrant[0] = 100.0f; } else { shipp->ship_max_shield_strength = sip->max_shield_strength; - shield_set_strength(objp, shipp->ship_max_shield_strength); + shield_set_strength(objp, shipp->ship_max_shield_strength * shipp->max_shield_recharge); } if (sip->flags2 & SIF2_MODEL_POINT_SHIELDS) { @@ -6105,7 +6188,7 @@ void ship_render(object * obj) mst.distortion_bitmap = shipp->thruster_distortion_bitmap; mst.use_ab = (obj->phys_info.flags & PF_AFTERBURNER_ON) || (obj->phys_info.flags & PF_BOOSTER_ON); - mst.glow_noise = shipp->thruster_glow_noise; + mst.glow_noise = shipp->thruster_glow_noise * sip->thruster_glow_noise_mult; mst.rotvel = &Objects[shipp->objnum].phys_info.rotvel; mst.glow_rad_factor = sip->thruster01_glow_rad_factor; @@ -9184,6 +9267,8 @@ void change_ship_type(int n, int ship_type, int by_sexp) objp->hull_strength = hull_pct * sp->ship_max_hull_strength; } + sp->max_shield_recharge = sip->max_shield_recharge; + // set the correct shield strength if (Fred_running) { if (sp->ship_max_shield_strength) @@ -9827,6 +9912,70 @@ int tracers[MAX_SHIPS][4][4]; float ship_get_subsystem_strength( ship *shipp, int type ); +/** + * Checks whether a ship would use autoaim if it were to fire its primaries at + * the given object, taking lead into account. + * + * @param shipp the ship to check + * @param bank_to_fire the assumed primary bank + * @param obj the object to check; must be the ship's current target + */ + +bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj) +{ + // Most of the code of this function has been duplicated from + // ship_fire_primary(), because it is not easy to encapsulate cleanly. + + ship_info *sip; + ai_info *aip; + ship_weapon *swp; + + bool has_autoaim, has_converging_autoaim; + float autoaim_fov = 0; + float dist_to_target = 0; + + vec3d plr_to_target_vec; + vec3d player_forward_vec = Objects[shipp->objnum].orient.vec.fvec; + + vec3d target_position; + + sip = &Ship_info[shipp->ship_info_index]; + aip = &Ai_info[shipp->ai_index]; + + swp = &shipp->weapons; + int weapon = swp->primary_bank_weapons[bank_to_fire]; + weapon_info* winfo_p = &Weapon_info[weapon]; + + has_converging_autoaim = ((sip->aiming_flags & AIM_FLAG_AUTOAIM_CONVERGENCE || (The_mission.ai_profile->player_autoaim_fov[Game_skill_level] > 0.0f && !( Game_mode & GM_MULTIPLAYER ))) && aip->target_objnum != -1); + has_autoaim = ((has_converging_autoaim || (sip->aiming_flags & AIM_FLAG_AUTOAIM)) && aip->target_objnum != -1); + + if (!has_autoaim) + return FALSE; + + autoaim_fov = MAX(shipp->autoaim_fov, The_mission.ai_profile->player_autoaim_fov[Game_skill_level]); + + if (aip->targeted_subsys != NULL) + { + get_subsystem_world_pos(&Objects[aip->target_objnum], aip->targeted_subsys, &target_position); + } + else + { + target_position = obj->pos; + } + + dist_to_target = vm_vec_dist_quick(&Objects[shipp->objnum].pos, &target_position); + + hud_calculate_lead_pos(&plr_to_target_vec, &target_position, obj, winfo_p, dist_to_target); + vm_vec_sub2(&plr_to_target_vec, &Objects[shipp->objnum].pos); + + float angle_to_target = vm_vec_delta_ang(&player_forward_vec, &plr_to_target_vec, NULL); + + if (angle_to_target <= autoaim_fov) + return true; + else + return false; +} + // fires a primary weapon for the given object. It also handles multiplayer cases. // in multiplayer, the starting network signature, and number of banks fired are sent // to all the clients in the game. All the info is passed to send_primary at the end of @@ -12590,7 +12739,7 @@ float ship_calculate_rearm_duration( object *objp ) //find out time to repair shields if(sip->sup_shield_repair_rate > 0.0f) - shield_rep_time = (sp->ship_max_shield_strength - shield_get_strength(objp)) / (sp->ship_max_shield_strength * sip->sup_shield_repair_rate); + shield_rep_time = ((sp->ship_max_shield_strength * sp->max_shield_recharge) - shield_get_strength(objp)) / (sp->max_shield_recharge * sp->ship_max_shield_strength * sip->sup_shield_repair_rate); max_hull_repair = sp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f); if ((The_mission.flags & MISSION_FLAG_SUPPORT_REPAIRS_HULL) && (max_hull_repair > objp->hull_strength) && (sip->sup_hull_repair_rate > 0.0f)) @@ -12726,7 +12875,7 @@ int ship_do_rearm_frame( object *objp, float frametime ) if ( !(objp->flags & OF_NO_SHIELDS) ) { shield_str = shield_get_strength(objp); - if ( shield_str < shipp->ship_max_shield_strength ) { + if ( shield_str < (shipp->ship_max_shield_strength * shipp->max_shield_recharge) ) { if ( objp == Player_obj ) { player_maybe_start_repair_sound(); } @@ -17721,6 +17870,8 @@ int ship_get_subobj_model_num(ship_info* sip, char* subobj_name) void init_path_metadata(path_metadata& metadata) { vm_vec_zero(&metadata.departure_rvec); + metadata.arrive_speed_mult = FLT_MIN; + metadata.depart_speed_mult = FLT_MIN; } int ship_get_sound(object *objp, GameSoundsIndex id) diff --git a/code/ship/ship.h b/code/ship/ship.h index 9430008ed2..27ed0df849 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -424,7 +424,7 @@ typedef struct ship_flag_name { int flag_list; // is this flag in the 1st or 2nd ship flags list? } ship_flag_name; -#define MAX_SHIP_FLAG_NAMES 17 +#define MAX_SHIP_FLAG_NAMES 18 extern ship_flag_name Ship_flag_names[]; // states for the flags variable within the ship structure @@ -505,6 +505,7 @@ extern ship_flag_name Ship_flag_names[]; #define SF2_WEAPONS_LOCKED (1<<25) // Karajorma - Prevents the player from changing the weapons on the ship on the loadout screen #define SF2_SHIP_SELECTIVE_LINKING (1<<26) // RSAXVC - Allow pilot to pick firing configuration #define SF2_SCRAMBLE_MESSAGES (1<<27) // Goober5000 - all messages sent from this ship appear scrambled +#define SF2_NO_SECONDARY_LOCKON (1<<28) // zookeeper - secondary lock-on disabled // If any of these bits in the ship->flags are set, ignore this ship when targeting extern int TARGET_SHIP_IGNORE_FLAGS; @@ -592,6 +593,8 @@ public: float ship_max_shield_strength; float ship_max_hull_strength; + float max_shield_recharge; + int ship_guardian_threshold; // Goober5000 - now also determines whether ship is guardian'd @@ -928,6 +931,7 @@ extern int ship_find_exited_ship_by_signature( int signature); #define SIF2_AUTO_SPREAD_SHIELDS (1 << 16) // zookeeper - auto spread shields #define SIF2_DRAW_WEAPON_MODELS (1 << 17) // the ship draws weapon models of any sort (used to be a boolean) #define SIF2_MODEL_POINT_SHIELDS (1 << 18) // zookeeper - uses model-defined shield points instead of quadrants +#define SIF2_LOCK_DETAIL_BOXES (1 << 19) // zookeeper - don't scale detail boxes or spheres based on detail settings #define SIF_DEFAULT_VALUE 0 #define SIF2_DEFAULT_VALUE 0 @@ -1144,6 +1148,8 @@ typedef struct ship_collision_physics { typedef struct path_metadata { vec3d departure_rvec; + float arrive_speed_mult; + float depart_speed_mult; } path_metadata; // The real FreeSpace ship_info struct. @@ -1175,6 +1181,7 @@ public: char pof_file_hud[MAX_FILENAME_LEN]; // POF file to load for the HUD target box int num_detail_levels; // number of detail levels for this ship int detail_distance[MAX_SHIP_DETAIL_LEVELS]; // distance to change detail levels at + int collision_lod; // check for collisions using a LOD int cockpit_model_num; // cockpit model int model_num; // ship model int model_num_hud; // model to use when rendering to the HUD (eg, mini supercap) @@ -1295,12 +1302,15 @@ public: float max_hull_strength; // Max hull strength of this class of ship. float max_shield_strength; - float auto_shield_spread; - bool auto_shield_spread_bypass; - int auto_shield_spread_from_lod; + float auto_shield_spread; // Thickness of the shield + bool auto_shield_spread_bypass; // Whether weapons fired up close can bypass shields + int auto_shield_spread_from_lod; // Which LOD to project the shield from + float auto_shield_spread_min_span; // Minimum distance weapons must travel until allowed to collide with the shield int shield_point_augment_ctrls[4]; // Re-mapping of shield augmentation controls for model point shields + float max_shield_recharge; + float hull_repair_rate; //How much of the hull is repaired every second float subsys_repair_rate; //How fast @@ -1320,6 +1330,7 @@ public: ubyte shield_icon_index; // index to locate ship-specific animation frames for the shield on HUD char icon_filename[MAX_FILENAME_LEN]; // filename for icon that is displayed in ship selection + angles model_icon_angles; // angle from which the model icon should be rendered (if not 0,0,0) char anim_filename[MAX_FILENAME_LEN]; // filename for animation that plays in ship selection char overhead_filename[MAX_FILENAME_LEN]; // filename for animation that plays weapons loadout int selection_effect; @@ -1372,6 +1383,7 @@ public: float thruster02_glow_len_factor; float thruster_dist_rad_factor; float thruster_dist_len_factor; + float thruster_glow_noise_mult; bool draw_distortion; @@ -1398,6 +1410,7 @@ public: vec3d topdown_offset; int engine_snd; // handle to engine sound for ship (-1 if no engine sound) + float min_engine_vol; // minimum volume modifier for engine sound when ship is stationary int glide_start_snd; // handle to sound to play at the beginning of a glide maneuver (default is 0 for regular throttle down sound) int glide_end_snd; // handle to sound to play at the end of a glide maneuver (default is 0 for regular throttle up sound) @@ -1417,10 +1430,12 @@ public: float minimum_convergence_distance; float convergence_distance; vec3d convergence_offset; + int autoaim_lock_snd, autoaim_lost_snd; float emp_resistance_mod; float piercing_damage_draw_limit; + int shield_impact_explosion_anim; int damage_lightning_type; @@ -1591,6 +1606,7 @@ extern void ship_cleanup(int shipnum, int cleanup_mode); extern void ship_destroy_instantly(object *ship_obj, int shipnum); extern void ship_actually_depart(int shipnum, int method = SHIP_DEPARTED_WARP); +extern bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj); extern int ship_fire_primary_debug(object *objp); // Fire the debug laser. extern int ship_stop_fire_primary(object * obj); extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0); diff --git a/code/sound/sound.cpp b/code/sound/sound.cpp index 9f58289650..7dc1b66ff0 100644 --- a/code/sound/sound.cpp +++ b/code/sound/sound.cpp @@ -50,10 +50,12 @@ typedef struct sound { SCP_vector Sounds; -int Sound_enabled = FALSE; // global flag to turn sound on/off -int Snd_sram; // mem (in bytes) used up by storing sounds in system memory -float Master_sound_volume = 1.0f; // range is 0 -> 1, used for non-music sound fx -float Master_voice_volume = 0.7f; // range is 0 -> 1, used for all voice playback +int Sound_enabled = FALSE; // global flag to turn sound on/off +int Snd_sram; // mem (in bytes) used up by storing sounds in system memory +float Default_sound_volume = 1.0f; // range is 0 -> 1, used for non-music sound fx +float Default_voice_volume = 0.7f; // range is 0 -> 1, used for all voice playback +float Master_sound_volume = Default_sound_volume; +float Master_voice_volume = Default_voice_volume; unsigned int SND_ENV_DEFAULT = 0; diff --git a/code/sound/sound.h b/code/sound/sound.h index 48983c7bd4..9acd8f9939 100644 --- a/code/sound/sound.h +++ b/code/sound/sound.h @@ -62,6 +62,8 @@ typedef struct sound_env } sound_env; extern int Sound_enabled; +extern float Default_sound_volume; // 0 -> 1.0 +extern float Default_voice_volume; // 0 -> 1.0 extern float Master_sound_volume; // 0 -> 1.0 extern float Master_voice_volume; // 0 -> 1.0 extern int Snd_sram; // System memory consumed by sound data diff --git a/code/weapon/weapon.h b/code/weapon/weapon.h index a344f80471..fa5863992b 100644 --- a/code/weapon/weapon.h +++ b/code/weapon/weapon.h @@ -141,6 +141,7 @@ extern int Num_weapon_subtypes; #define WF_DESTROYED_BY_WEAPON (1<<6) // destroyed by damage from other weapon #define WF_SPAWNED (1<<7) //Spawned from a spawning type weapon #define WF_HOMING_UPDATE_NEEDED (1<<8) // this is a newly spawned homing weapon which needs to update client machines +#define WF_NO_HOMING (1<<9) // this weapon should ignore any homing behavior it'd usually have // flags for setting burst fire #define WBF_FAST_FIRING (1<<0) // burst is to use only the firewait to determine firing delays @@ -416,6 +417,7 @@ typedef struct weapon_info { int impact_weapon_expl_index; // Index into Weapon_expl_info of which ANI should play when this thing impacts something float impact_explosion_radius; // How big the explosion should be + float shield_impact_explosion_radius; // How big the shield hit explosion should be int dinky_impact_weapon_expl_index; float dinky_impact_explosion_radius; @@ -670,4 +672,6 @@ void weapon_pause_sounds(); // Unpauses all running weapon sounds void weapon_unpause_sounds(); +void shield_impact_explosion(vec3d *hitpos, object *objp, float radius, int idx); + #endif diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index 3fe8386b38..0ccac3d34a 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -938,6 +938,8 @@ void init_weapon_entry(int weap_info_index) wip->impact_explosion_radius = 1.0f; wip->impact_weapon_expl_index = -1; + wip->shield_impact_explosion_radius = 1.0f; + wip->dinky_impact_explosion_radius = 1.0f; wip->dinky_impact_weapon_expl_index = -1; @@ -1063,6 +1065,7 @@ void init_weapon_entry(int weap_info_index) wip->burst_delay = 1.0f; // 1 second, just incase its not defined wip->burst_shots = 0; + wip->burst_flags = 0; generic_anim_init( &wip->thruster_flame ); @@ -1909,6 +1912,12 @@ int parse_weapon(int subtype, bool replace) if ( optional_string("$Impact Explosion Radius:") ) stuff_float(&wip->impact_explosion_radius); + if ( optional_string("$Shield Impact Explosion Radius:") ) { + stuff_float(&wip->shield_impact_explosion_radius); + } else if (first_time) { + wip->shield_impact_explosion_radius = wip->impact_explosion_radius; + } + if ( optional_string("$Dinky Impact Explosion:") ) { stuff_string(fname, F_NAME, NAME_LENGTH); @@ -4770,7 +4779,7 @@ void weapon_process_post(object * obj, float frame_time) } // a single player or multiplayer server function -- it affects actual weapon movement. - if (wip->wi_flags & WIF_HOMING) { + if (wip->wi_flags & WIF_HOMING && !(wp->weapon_flags & WF_NO_HOMING)) { weapon_home(obj, num, frame_time); // If this is a swarm type missile, @@ -4942,6 +4951,10 @@ void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_o wp = &Weapons[Objects[weapon_objnum].instance]; wip = &Weapon_info[wp->weapon_info_index]; + if (wp->weapon_flags & WF_NO_HOMING) { + return; + } + if (parent_objnum >= 0) { parent_objp = &Objects[parent_objnum]; Assert(parent_objp->type == OBJ_SHIP); @@ -4949,6 +4962,16 @@ void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_o parent_objp = NULL; } + if (parent_objp != NULL && (Ships[parent_objp->instance].flags2 & SF2_NO_SECONDARY_LOCKON)) { + wp->weapon_flags |= WF_NO_HOMING; + wp->homing_object = NULL; + wp->homing_subsys = NULL; + wp->target_num = -1; + wp->target_sig = -1; + + return; + } + if ( parent_objp == NULL || Ships[parent_objp->instance].ai_index >= 0 ) { int target_team = -1; if ( target_objnum >= 0 ) { @@ -7034,3 +7057,8 @@ void weapon_unpause_sounds() // Pause all beam sounds beam_unpause_sounds(); } + +void shield_impact_explosion(vec3d *hitpos, object *objp, float radius, int idx) { + int expl_ani_handle = Weapon_explosions.GetAnim(idx, hitpos, radius); + particle_create( hitpos, &vmd_zero_vector, 0.0f, radius, PARTICLE_BITMAP_PERSISTENT, expl_ani_handle, -1.0f, objp ); +}