Files
fs2open.github.com/code/playerman/managepilot.cpp
T
1fa6e00f39 Controls 4 and 5 - Multi-joy and Preset Files (#2958)
* Updates the AxisInverted hooks to use the new CC_bind.  Currently assumes only Joy0, so this may need further tweaking or replacement

* * Add ability to claim up to 4 joysticks for player use, using the launcher (via registry or fs2_open.ini) or the new menu system
* Hack into existing control polling system to get the new primary/secondary binding schema to work.  This should be reworked into a reverse-lookup system.
* Finally break the mouse loose from overlapping with Joystick binidngs
* Fix bug where joy button wouldn't register on search function
* Fix bug with mousewheel not always decaying as it should.
* Fix rebase conflict with selecting item during search mode

* Fix bug where mouse buttons were incorrectly labeled.

* Move some enums around

* Fix bug with joy axis labels being displayed as buttons.
* Fix possible bug where Other conflict axis wouldn't clear
* Updated default joystick axis text. A new strings.tbl will need to be supplied for localization support

* Deprecate nAxisInverted ADE functions and replace with AxisInverted(int, int, bool)

* Merge Axis mappings into Control_config
* More utility functions for CC_bind and CCB
* Adds another ADE function for inverting the axis action, instead of individual bindings
* Hacks to get plr file to compile.  Currently slices out many of the new features like secondary axis and the mouse axis, and the additional joystick bindings (joy1, joy2, joy3)

* Fix bug with uncontrollable spin (slopily)
* Fix some warnings
* Add flags for Hats and Balls for future implementation

* Fix bug where inverting an axis would trigger an assert

* Fix bug where joy wasn't detected as present

* Most axis actions should be relative, instead of absolute
* Hopefully optimize joy_get_unscaled_reading
* Properly break out of double-for search function for search and bind modes in config menu
* Refactor control_get_axes_readings to provide more consistent behavior
* Use JOY_REL_THROTTLE instead of Action::REL_THROTTLE when indexing into Control_config

* Bank axis has a default binding, Throttle does not.

* Fix bug where conflicts weren't being detected.
Fix bug where default presets weren't being detectd

* Elegantly break out of double fors with goto.
Fix bug with bind mode not detecting axis

* Save new binding scheme to playerfile
* Add cid_assign() to standardize cid assignment with shorts
* Add write and read handlers for int8_t

* Remove saving controls to csg.  This will be replaced by presets.

* Fix bug with reading pilotfile

* Use "Joy-n" instead of "joystick n" to match default identifiers in config menu

* Fix bug with CC_TYPE_TRIGGER controls always marked as used, if not bound with a key.
Fix bug with Joy-0 btn 1 triggering multiple controls that are not bound to it.

* Improve readibility of the joy info output to log
* Message when a joystick connects and using legacy system.
* Fix bug where system joystick 0 was mapped in pJoystick that should've been nullptr

* Fix bug where *not* having a joystick will cause bind mode in the config menu to immediately cancel.

* Add reverse lookup functions for actions.  mActionToVal[] does string -> int, mValToAction() does int -> string

* Add the foundation for writing the presets files.
* Adds a bunch of reverse lookup functions to get the stringified version of various enums, etc.
* presets.h and presets.cpp are in something of a barebones at this point

* Add new subdir for preset files 'player/presets'
* add utility for finding the distance of a bitfield/bitmask.  Used to translate mouse button masks into mouse button id's
* Write PresetFileHandler as standalone, instead of a subclass.  Still mostly a copy of JSONFileHandler
* Make functions to map and reverse-map the bindings for use with the preset files and ultimately the ControlConfigDefaults.tbl as well

* Check presets from file to see if they are duplicate with an existing preset.

* Check for uniqueness of presets fron controlconfigdefaults.tbl before adding them, and warn the user if there are duplicates.

* Rename the test functions into proper load/save.
* save function has some standard checks
* load function is called on init
* make finding duplicates its own function outside of the preset label rendering function
* Initial draft of saving presets in-game

* Fix bug where all loaded presets were thought to be duplicate of defaults.

* Fix bug where preset cycling would get stuck on the first preset other than default

* Fix bug where saving a mouse button saves the wrong textified string.

* Prevent preset names from being too long.
* Crash still occurs if the name is too long, as if set outside of FSO
* Workaround text running off the popups in 640x480.  Doesn't look as nice, sadly

* CSG's now track preset names instead of containing bindings
* remake control_config_reset_defaults() into control_config_use_preset()
* provide a function to get the currently selected preset.

* Do PLR backward compatibility, hopefully.

* PLR now tracks presets instead of saving the bindings.

* On Loading pre PLR3 or CSG6 data, create a new preset.

* Strip off file extension before saving preset files. Jason.json.json just looks dumb

* Add "-joy_info" commandline option.  Will output SDL joystick info of all detected joysticks to stdout

* Fix non-standard behavior with registry_read_string().
* Returning NULL instead of the default value, or an empty string, will trigger an access violation with SCP_string and other string copy functions.

* Modify io::joystick::init() to take an optional param, no_register, to use with -joy_info

* Address CI complaints

* Address CI issues 2.

* Supress preset duplicate warning if in unit tests.

* Address scripting unittest failure due to incorrect argument type. (Ints don't technically exist in LUA)

* Use Mouse enums instead of Joy enums or magic numbers

* More signed/unsigned madness

* Address CI issues 3

* Address gcc-5 issues. (No auto in lambdas)
* Fix bug with bit_distance()

* Address gcc-5 issues 2

* Address clang issues

* Address clang-tidy issues

* Address clang-tidy issues in undosys.  May need to be cherry picked to Controls2.

* Address clang-tidy issues

* Improve logic of preset name prompt.

* Fix bug with selected presets not being saved

* No, a cstr is not a std::string.
* Can't use i there because its already defined.

* Fix bug where CSG's wouldn't save selected preset
* Fix bug where presets wouldn't be loaded correctly from player files
* Fix bug where loaded preset files would not update the preset cycle position correctly

* Some QoL changes for the preset name prompt

* Address Appveyor issues.

* Add forward compatibility for player file.  This is required because currently any "missing" data in the pilotfile will trigger an Error, and crash FSO.

* Fix bug where mouse X axis was unusable.
Apparently stating that MOUSE_Z_AXIS IS MOUSE_Z_AXIS doesn't get the point across.

* Address CI issues.

* Address CI issues.
* sel default assigned to -1
* static_cast enum to an int

* Assimilate -joy_info int -get_flags, replacing the existing method that launchers use to get joystick information.

* Add '\n' to PST log messages.

* Change logic of checking joystick .ini keys:  Should the multi-joy key not be found, try legacy joy key.

* Add some more missing '\n' to mprintf messages.

* Notify the player when converting to the new multi-joy system. (Using ReleaseWarning for now...)
* Warn the player if a preset defined in their .PLR could not be found.

* Actually disable control items without a valid tab, instead of relying on the fact that they're disabled  by default.

* Add CC_preset::is_duplicate_of() method to make checking for preset uniqueness less of a brain teaser.
* Doxy the first_override param of control_config_common_read_section

* Adds the os::dialogs::Information() method, which displays an information-only dialog in similar format to Warning and Error dialogs.  Does not throw Warning nor Error exceptions
* load_player now uses os::dialogs::Information() instead of ::ReleaseWarning, thus not halting unit tests.

* Breaks retail localization support for Joystick and Mice labels - This is because the new multi-joy axis end up very confusing "Joy-0 Joystick/Mouse X Axis"
* Renames Joy_axis_text to Axis_text, intended to be used both by joysticks and the mouse.
* Moves mouse axis and button labels into an array to ease future XSTR support
* Adds asserts to ensure indices are within the array sizes of label arrays
* Adds XSTR indices for Axis labels, mouse buttons, and mouse wheels.

* Seperates conflict check comparisons from equality comparisons.
*  Conflict checks can ignore certain combinations of CC_bind::flag's; i.e. an Axis bound to a different control but is inverted or relative will still cause a conflict with another control that has the same axis
*  Equality comparisons is exact equality, allowing presets to check if binds have the same binding and flag configurations.

* More UI string changes and localization
* Tweaks positioning of top button labels in CC menu
* Adds the following XSTR's:
1659 "Preset: %s"
1660 "Preset: custom"
1661 "Next Preset"

* Fix bug where mouse axis turned into mouse button when inverting control by its control name.
* Fix bug with the new conflict checking subroutine not working with non-axis controls

* Address CI issues.

* Don't trigger modal popups while in unit tests.

* Address clang tidy issues
* use nullptr
* be consistent with conditional style
* use booleans to stuff debug states, methods with just Assertions in it will trigger a false positive on clang tidy
* use string::empty() instead of checking for "".

* Remove dead code of -joy_info.  Functionality has been merged with -get_flags

* Address clang-tidy [readability-simplify-boolean-expr]
--z64 says "I don't like it!"

* fix axis check

* Implement revised axis action virtvars for scripting. Also protect against a control mismatch in the CSG file due to a script-inverted control

* Encapsulate CC_bind properly
* Add mouse and joystick cid's to AxisInverted description

* Update translate_key() and its user to correctly translate multijoy bindings for training messages.

* Prompt user trying to escape from config menu when they have changes.
* Remove dead function.

* Fix crash during training missions due to key translation that would overflow internal buffer.
* Went back with cstring as return value instead of SCP string, because caller would need a significant refactor
* Update doxy of the textify functions

* Unify textify_scancode() and textify_scancode_universal() to ensure common code path.

* Add "Multijoy" entry to json caps. (For Knossos)

* Make CI happy.
* Remove unused lambda captures.

* Instead of overwrite during plr2 => plr3 conversion, try to use the existing preset file.

* Fix conditional for checking if file exists within presets::save_preset_file()

* Revert Majority of PR#3739
* PLR and CSG versions are incremented past versions set by 3739
* Forward compatibility feature of #3739 (Only save sexp containers into the CSG if they are used) is removed

* Remove forward compatibilty of PLR file when going in between pre- and post-Controls5 FSO versions
* Add missing startSection() and endSection() for csg_write_controls()

* Fix clang-tidy error #1

* Fix clang-tidy error #2

* fix clandy tidy error #1 part 2/2

* Fix clang tidy error 3

Co-authored-by: Goober5000 <[email protected]>
Co-authored-by: Tyler Blount <[email protected]>
Co-authored-by: wookieejedi <[email protected]>
Co-authored-by: BMagnu <[email protected]>
2022-01-04 12:01:32 -06:00

809 lines
26 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "cfile/cfile.h"
#include "cutscene/cutscenes.h"
#include "freespace.h"
#include "gamesnd/eventmusic.h"
#include "graphics/font.h"
#include "hud/hudconfig.h"
#include "hud/hudsquadmsg.h"
#include "io/joy.h"
#include "io/mouse.h"
#include "menuui/playermenu.h"
#include "menuui/techmenu.h"
#include "mission/missionbriefcommon.h"
#include "mission/missioncampaign.h"
#include "mission/missionload.h"
#include "missionui/missionscreencommon.h"
#include "missionui/missionshipchoice.h"
#include "missionui/redalert.h"
#include "mod_table/mod_table.h"
#include "network/multi.h"
#include "osapi/osregistry.h"
#include "pilotfile/pilotfile.h"
#include "playerman/managepilot.h"
#include "playerman/player.h"
#include "popup/popup.h"
#include "ship/ship.h"
#include "sound/audiostr.h"
#include "sound/sound.h"
#include "weapon/weapon.h"
// pilot pic image list stuff ( call pilot_load_pic_list() to make these valid )
char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
char *Pilot_image_names[MAX_PILOT_IMAGES];
int Num_pilot_images = 0;
// squad logo list stuff (call pilot_load_squad_pic_list() to make these valid )
char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
char *Pilot_squad_image_names[MAX_PILOT_IMAGES];
int Num_pilot_squad_images = 0;
// internal function to delete a player file. Called after a pilot is obsoleted, and when a pilot is deleted
// used in barracks and player_select
//returns 0 on failure, 1 on success
bool delete_pilot_file(const char *pilot_name)
{
int delreturn;
char filename[MAX_FILENAME_LEN];
char basename[MAX_FILENAME_LEN];
// get the player file.
_splitpath(pilot_name, NULL, NULL, basename, NULL);
strcpy_s( filename, basename );
strcat_s( filename, NOX(".plr") );
cf_delete(filename, CF_TYPE_PLAYERS, CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
strcpy_s( filename, basename );
strcat_s( filename, NOX(".json") );
delreturn =
cf_delete(filename, CF_TYPE_PLAYERS, CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
// we must try and delete the campaign save files for a pilot as well.
if (delreturn) {
mission_campaign_delete_all_savefiles(basename);
return true;
} else {
return false;
}
}
// this works on barracks and player_select interface screens
void init_new_pilot(player *p, int reset)
{
int cur_speed;
if (reset) {
hud_set_default_hud_config(p); // use a default hud config
control_config_use_preset(Control_config_presets[0]); // get a default keyboard config
player_set_pilot_defaults(p); // set up any player struct defaults
// set the default detail level based on tabling rather than the above method
cur_speed = Default_detail_level;
#if NUM_DEFAULT_DETAIL_LEVELS != 4
#error Code in ManagePilot assumes NUM_DEFAULT_DETAIL_LEVELS = 4
#endif
detail_level_set(cur_speed);
Game_skill_level = game_get_default_skill_level();
mprintf(( "Setting detail level to %d because of new pilot\n", cur_speed ));
Use_mouse_to_fly = true;
Mouse_sensitivity = 4;
if (!Using_in_game_options) {
// This is handled by the in game options in this case
Joy_sensitivity = 9;
Joy_dead_zone_size = 10;
}
snd_set_effects_volume(Default_sound_volume);
event_music_set_volume(Default_music_volume);
snd_set_voice_volume(Default_voice_volume);
p->variables.clear();
p->containers.clear();
}
// unassigned squadron
strcpy_s(p->s_squad_name, XSTR("Unassigned", 1255));
strcpy_s(p->s_squad_filename, "");
strcpy_s(p->m_squad_name, XSTR("Unassigned", 1255));
strcpy_s(p->m_squad_filename, "");
// set him to be a single player pilot by default (the actual creation routines will change this if necessary)
p->flags &= ~PLAYER_FLAGS_IS_MULTI;
// effectively sets the length return by strlen() to 0
Campaign.filename[0] = 0;
// pick a random pilot image for this guy
if (reset){
pilot_set_random_pic(p);
p->insignia_texture = -1;
pilot_set_random_squad_pic(p);
}
p->stats.init();
Pilot.reset_stats();
p->stats.score = 0;
p->stats.rank = RANK_ENSIGN;
p->tips = 1;
// set pilot language to the current language
lcl_get_language_name(p->language);
Multi_options_g.protocol = NET_TCP;
// initialize default multiplayer options
multi_options_set_netgame_defaults(&p->m_server_options);
multi_options_set_local_defaults(&p->m_local_options);
Player_loadout.filename[0] = 0;
// reset the cutscenes which can be viewed
if ( reset ){
cutscene_init();
}
pilot_set_start_campaign(p);
}
int local_num_campaigns = 0;
int campaign_file_list_filter(const char *filename)
{
char name[NAME_LENGTH];
char *desc = NULL;
int type, max_players;
if ( mission_campaign_get_info( filename, name, &type, &max_players, &desc) ) {
if ( type == CAMPAIGN_TYPE_SINGLE) {
Campaign_names[local_num_campaigns] = vm_strdup(name);
local_num_campaigns++;
return 1;
}
}
if (desc != NULL)
vm_free(desc);
return 0;
}
//Generates a list of available campaigns. Sets active campaign to "freespace2.fc2", or if that is unavailable, the first campaign found
void pilot_set_start_campaign(player* p)
{
char wild_card[10];
int i, j, incr = 0;
char *t = NULL;
int rc = 0;
char *campaign_file_list[MAX_CAMPAIGNS];
memset(wild_card, 0, sizeof(wild_card));
strcpy_s(wild_card, NOX("*"));
strcat_s(wild_card, FS_CAMPAIGN_FILE_EXT);
// set filter for cf_get_file_list() if there isn't one set already (the simroom has a special one)
if (Get_file_list_filter == NULL)
Get_file_list_filter = campaign_file_list_filter;
// now get the list of all campaign names
// NOTE: we don't do sorting here, but we assume CF_SORT_NAME, and do it manually below
rc = cf_get_file_list(MAX_CAMPAIGNS, campaign_file_list, CF_TYPE_MISSIONS, wild_card, CF_SORT_NONE);
for (i = 0; i < rc; i++)
{
if (!stricmp(campaign_file_list[i], Default_campaign_file_name))
{
strcpy_s(p->current_campaign, campaign_file_list[i]);
return;
}
}
// now sort everything
incr = local_num_campaigns / 2;
while (incr > 0) {
for (i = incr; i < local_num_campaigns; i++) {
j = i - incr;
while (j >= 0) {
char *name1 = Campaign_names[j];
char *name2 = Campaign_names[j + incr];
// if we hit this then a coder probably did something dumb (like not needing to sort)
if ( (name1 == NULL) || (name2 == NULL) ) {
Int3();
break;
}
if ( !strnicmp(name1, "the ", 4) )
name1 += 4;
if ( !strnicmp(name2, "the ", 4) )
name2 += 4;
if (stricmp(name1, name2) > 0) {
// first, do filenames
t = campaign_file_list[j];
campaign_file_list[j] = campaign_file_list[j + incr];
campaign_file_list[j + incr] = t;
j -= incr;
} else {
break;
}
}
}
incr /= 2;
}
if (rc > 0)
strcpy_s(p->current_campaign, campaign_file_list[0]);
else
strcpy_s(p->current_campaign, "<none>");
}
void pilot_set_short_callsign(player *p, int max_width)
{
strcpy_s(p->short_callsign, p->callsign);
font::set_font(font::FONT1);
font::force_fit_string(p->short_callsign, CALLSIGN_LEN - 1, max_width);
gr_get_string_size( &(p->short_callsign_width), NULL, p->short_callsign );
}
// pick a random image for the passed player
void pilot_set_random_pic(player *p)
{
// if there are no available pilot pics, set the image filename to null
if (Num_pilot_images <= 0) {
strcpy_s(p->image_filename, "");
} else {
// pick a random name from the list
int random_index = Random::next(Num_pilot_images);
Assert((random_index >= 0) && (random_index < Num_pilot_images));
strcpy_s(p->image_filename, Pilot_images_arr[random_index]);
}
}
/*
* pick a random squad image for the passed player
* sets single & multi squad pic to the same image
*
* @param p pointer to player
*/
void pilot_set_random_squad_pic(player *p)
{
// if there are no available pilot pics, set the image filename to null
if (Num_pilot_squad_images <= 0) {
player_set_squad_bitmap(p, "", true);
player_set_squad_bitmap(p, "", false);
} else {
// pick a random name from the list
int random_index = Random::next(Num_pilot_squad_images);
Assert((random_index >= 0) && (random_index < Num_pilot_squad_images));
player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index], true);
player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index], false);
}
}
// format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate
void pilot_format_callsign_personal(char *in_callsign,char *out_callsign)
{
// don't do anything if we've got invalid strings
if((in_callsign == NULL) || (out_callsign == NULL)){
return;
}
// copy the original string
strcpy(out_callsign,in_callsign);
// tack on the appropriate postfix
if(in_callsign[strlen(in_callsign) - 1] == 's'){
strcat(out_callsign,XSTR( "\'", 45));
} else {
strcat(out_callsign,XSTR( "\'s", 46));
}
}
// throw up a popup asking the user to verify the overwrite of an existing pilot name
// 1 == ok to overwrite, 0 == not ok
int pilot_verify_overwrite()
{
return popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR( "&No", 47), XSTR( "&Yes", 48), XSTR( "Warning\nA duplicate pilot exists\nOverwrite?", 49));
}
// load up the list of pilot image filenames (do this at game startup as well as barracks startup)
void pilot_load_pic_list()
{
Num_pilot_images = 0;
// load pilot images from the player images directory
Num_pilot_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_images_arr, Pilot_image_names, CF_TYPE_PLAYER_IMAGES, NOX("*.pcx"));
// sort all filenames
cf_sort_filenames(Num_pilot_images, Pilot_image_names, CF_SORT_NAME);
}
// load up the list of pilot squad filenames
void pilot_load_squad_pic_list()
{
Num_pilot_squad_images = 0;
// load pilot images from the player images directory
Num_pilot_squad_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_squad_images_arr, Pilot_squad_image_names, CF_TYPE_SQUAD_IMAGES, NOX("*.pcx"));
// sort all filenames
cf_sort_filenames(Num_pilot_squad_images, Pilot_squad_image_names, CF_SORT_NAME);
}
// will attempt to load an insignia bitmap and set it as active for the player
void player_set_squad_bitmap(player *p, const char *fname, bool ismulti)
{
// sanity check
if(p == NULL){
return;
}
char *squad_pic_p;
if (ismulti) {
squad_pic_p = p->m_squad_filename;
} else {
squad_pic_p = p->s_squad_filename;
}
// if he has another bitmap already - unload it
if (p->insignia_texture > 0) {
bm_release(p->insignia_texture);
}
p->insignia_texture = -1;
// try and set the new one
if (fname != squad_pic_p) {
strncpy(squad_pic_p, fname, MAX_FILENAME_LEN);
}
if (squad_pic_p[0] != '\0') {
p->insignia_texture = bm_load_duplicate(fname);
// lock is as a transparent texture
if (p->insignia_texture != -1) {
bm_lock(p->insignia_texture, 16, BMP_TEX_XPARENT);
bm_unlock(p->insignia_texture);
}
}
}
// set squadron
void player_set_squad(player *p, char *squad_name)
{
// sanity check
if(p == NULL){
return;
}
if (Game_mode & GM_MULTIPLAYER) {
strcpy_s(p->m_squad_name, squad_name);
} else {
strcpy_s(p->s_squad_name, squad_name);
}
}
void player::reset()
{
memset(callsign, 0, sizeof(callsign));
memset(short_callsign, 0, sizeof(short_callsign));
short_callsign_width = 0;
memset(image_filename, 0, sizeof(image_filename));
memset(s_squad_filename, 0, sizeof(s_squad_filename));
memset(s_squad_name, 0, sizeof(s_squad_name));
memset(m_squad_filename, 0, sizeof(m_squad_filename));
memset(m_squad_name, 0, sizeof(m_squad_name));
memset(current_campaign, 0, sizeof(current_campaign));
readyroom_listing_mode = 0;
flags = 0;
save_flags = 0;
memset(keyed_targets, 0, sizeof(keyed_targets));
current_hotkey_set = -1;
lead_target_pos = vmd_zero_vector;
lead_target_cheat = 0;
lead_indicator_active = 0;
lock_indicator_x = 0;
lock_indicator_y = 0;
lock_indicator_start_x = 0;
lock_indicator_start_y = 0;
lock_indicator_visible = 0;
lock_time_to_target = 0.0f;
lock_dist_to_target = 0.0f;
last_ship_flown_si_index = -1;
objnum = -1;
memset(&bi, 0, sizeof(bi));
memset(&ci, 0, sizeof(ci));
stats.init();
// only reset Pilotfile stats if we're resetting Player
// remember: multi has many Players...
if (Player == this) {
Pilot.reset_stats();
}
friendly_hits = 0;
friendly_damage = 0.0f;
friendly_last_hit_time = 0;
last_warning_message_time = 0;
control_mode = 0;
saved_viewer_mode = 0;
check_warn_timestamp = -1;
distance_warning_count = 0;
distance_warning_time = -1;
allow_warn_timestamp = -1;
warn_count = 0;
damage_this_burst = 0.0f;
repair_sound_loop = sound_handle::invalid();
cargo_scan_loop = sound_handle::invalid();
praise_count = 0;
allow_praise_timestamp = -1;
praise_delay_timestamp = -1;
ask_help_count = 0;
allow_ask_help_timestamp = -1;
scream_count = 0;
allow_scream_timestamp = -1;
low_ammo_complaint_count = 0;
allow_ammo_timestamp = -1;
praise_self_count = 0;
praise_self_timestamp = -1;
subsys_in_view = -1;
request_repair_timestamp = -1;
cargo_inspect_time = -1;
target_is_dying = -1;
current_target_sx = 0;
current_target_sy = 0;
target_in_lock_cone = 0;
locking_subsys = NULL;
locking_subsys_parent = -1;
locking_on_center = 0;
killer_objtype = -1;
killer_species = 0;
killer_weapon_index = -1;
memset(killer_parent_name, 0, sizeof(killer_parent_name));
check_for_all_alone_msg = -1;
update_dumbfire_time = -1;
update_lock_time = -1;
threat_flags = 0;
auto_advance = 1;
multi_options_set_local_defaults(&m_local_options);
multi_options_set_netgame_defaults(&m_server_options);
insignia_texture = -1;
tips = 1;
shield_penalty_stamp = -1;
failures_this_session = 0;
show_skip_popup = 0;
variables.clear();
containers.clear();
death_message = "";
memset(&lua_ci, 0, sizeof(lua_ci));
memset(&lua_bi, 0, sizeof(lua_bi));
memset(&lua_bi_full, 0, sizeof(lua_bi_full));
player_was_multi = 0;
memset(language, 0, sizeof(language));
}
void player::assign(const player *other)
{
int i;
strcpy_s(callsign, other->callsign);
strcpy_s(short_callsign, other->short_callsign);
short_callsign_width = other->short_callsign_width;
strcpy_s(image_filename, other->image_filename);
strcpy_s(s_squad_filename, other->s_squad_filename);
strcpy_s(s_squad_name, other->s_squad_name);
strcpy_s(m_squad_filename, other->m_squad_filename);
strcpy_s(m_squad_name, other->m_squad_name);
strcpy_s(current_campaign, other->current_campaign);
readyroom_listing_mode = other->readyroom_listing_mode;
flags = other->flags;
save_flags = other->save_flags;
memcpy(keyed_targets, other->keyed_targets, sizeof(keyed_targets));
// make sure we correctly set the pointers
for (i = 0; i < MAX_KEYED_TARGETS; i++)
{
if (other->keyed_targets[i].next == NULL)
keyed_targets[i].next = NULL;
else
{
size_t index = (other->keyed_targets[i].next - &other->keyed_targets[0]);
keyed_targets[i].next = &keyed_targets[index];
}
if (other->keyed_targets[i].prev == NULL)
keyed_targets[i].prev = NULL;
else
{
size_t index = (other->keyed_targets[i].prev - &other->keyed_targets[0]);
keyed_targets[i].prev = &keyed_targets[index];
}
}
current_hotkey_set = other->current_hotkey_set;
lead_target_pos = other->lead_target_pos;
lead_target_cheat = other->lead_target_cheat;
lead_indicator_active = other->lead_indicator_active;
lock_indicator_x = other->lock_indicator_x;
lock_indicator_y = other->lock_indicator_y;
lock_indicator_start_x = other->lock_indicator_start_x;
lock_indicator_start_y = other->lock_indicator_start_y;
lock_indicator_visible = other->lock_indicator_visible;
lock_time_to_target = other->lock_time_to_target;
lock_dist_to_target = other->lock_dist_to_target;
last_ship_flown_si_index = other->last_ship_flown_si_index;
// this one might be dicey
objnum = other->objnum;
memcpy(&bi, &other->bi, sizeof(button_info));
memcpy(&ci, &other->ci, sizeof(control_info));
stats.assign(other->stats);
friendly_hits = other->friendly_hits;
friendly_damage = other->friendly_damage;
friendly_last_hit_time = other->friendly_last_hit_time;
last_warning_message_time = other->last_warning_message_time;
control_mode = other->control_mode;
saved_viewer_mode = other->saved_viewer_mode;
check_warn_timestamp = other->check_warn_timestamp;
distance_warning_count = other->distance_warning_count;
distance_warning_time = other->distance_warning_time;
allow_warn_timestamp = other->allow_warn_timestamp;
warn_count = other->warn_count;
damage_this_burst = other->damage_this_burst;
repair_sound_loop = other->repair_sound_loop;
cargo_scan_loop = other->cargo_scan_loop;
praise_count = other->praise_count;
allow_praise_timestamp = other->allow_praise_timestamp;
praise_delay_timestamp = other->praise_delay_timestamp;
ask_help_count = other->ask_help_count;
allow_ask_help_timestamp = other->allow_ask_help_timestamp;
scream_count = other->scream_count;
allow_scream_timestamp = other->allow_scream_timestamp;
low_ammo_complaint_count = other->low_ammo_complaint_count;
allow_ammo_timestamp = other->allow_ammo_timestamp;
praise_self_count = other->praise_self_count;
praise_self_timestamp = other->praise_self_timestamp;
subsys_in_view = other->subsys_in_view;
request_repair_timestamp = other->request_repair_timestamp;
cargo_inspect_time = other->cargo_inspect_time;
target_is_dying = other->target_is_dying;
current_target_sx = other->current_target_sx;
current_target_sy = other->current_target_sy;
target_in_lock_cone = other->target_in_lock_cone;
locking_subsys = other->locking_subsys;
locking_subsys_parent = other->locking_subsys_parent;
locking_on_center = other->locking_on_center;
killer_objtype = other->killer_objtype;
killer_species = other->killer_species;
killer_weapon_index = other->killer_weapon_index;
strcpy_s(killer_parent_name, other->killer_parent_name);
check_for_all_alone_msg = other->check_for_all_alone_msg;
update_dumbfire_time = other->update_dumbfire_time;
update_lock_time = other->update_lock_time;
threat_flags = other->threat_flags;
auto_advance = other->auto_advance;
memcpy(&m_local_options, &other->m_local_options, sizeof(multi_local_options));
memcpy(&m_server_options, &other->m_server_options, sizeof(multi_server_options));
insignia_texture = other->insignia_texture;
tips = other->tips;
shield_penalty_stamp = other->shield_penalty_stamp;
failures_this_session = other->failures_this_session;
show_skip_popup = other->show_skip_popup;
variables.clear();
variables.reserve(other->variables.size());
for (SCP_vector<sexp_variable>::const_iterator ii = other->variables.begin(); ii != other->variables.end(); ++ii)
{
sexp_variable temp;
memcpy(&temp, &(*ii), sizeof(sexp_variable));
variables.push_back(temp);
}
containers = other->containers;
death_message = other->death_message;
memcpy(&lua_ci, &other->lua_ci, sizeof(control_info));
memcpy(&lua_bi, &other->lua_bi, sizeof(button_info));
memcpy(&lua_bi_full, &other->lua_bi_full, sizeof(button_info));
player_was_multi = other->player_was_multi;
strcpy_s(language, other->language);
Assertion(*this == *other, "Equality comparison failed after pilot assignment!");
}
template<typename T, size_t N>
bool array_compare(const T (&left)[N], const T (&right)[N]) {
auto left_el = std::begin(left);
auto right_el = std::begin(right);
auto left_end = std::end(left);
auto right_end = std::end(right);
for (; left_el != left_end && right_el != right_end; ++left_el, ++right_el) {
if (!(*left_el == *right_el)) {
return false;
}
}
return true;
}
bool operator==(const htarget_list& left, const htarget_list& right) {
return left.how_added == right.how_added && obj_compare(left.objp, right.objp);
}
bool operator==(const button_info& left, const button_info& right) {
return array_compare(left.status, right.status);
}
bool operator==(const control_info& left, const control_info& right) {
return left.pitch == right.pitch && left.vertical == right.vertical && left.heading == right.heading
&& left.sideways == right.sideways && left.bank == right.bank && left.forward == right.forward
&& left.forward_cruise_percent == right.forward_cruise_percent
&& left.fire_primary_count == right.fire_primary_count
&& left.fire_secondary_count == right.fire_secondary_count
&& left.fire_countermeasure_count == right.fire_countermeasure_count
&& left.fire_debug_count == right.fire_debug_count && left.afterburner_start == right.afterburner_start
&& left.afterburner_stop == right.afterburner_stop;
}
bool operator==(const multi_local_options& left, const multi_local_options& right) {
return left.flags == right.flags && left.obj_update_level == right.obj_update_level;
}
bool operator==(const multi_server_options& left, const multi_server_options& right) {
return left.squad_set == right.squad_set && left.endgame_set == right.endgame_set && left.flags == right.flags
&& left.respawn == right.respawn && left.max_observers == right.max_observers
&& left.skill_level == right.skill_level && left.voice_qos == right.voice_qos
&& left.voice_token_wait == right.voice_token_wait && left.voice_record_time == right.voice_record_time
&& left.mission_time_limit == right.mission_time_limit && left.kill_limit == right.kill_limit;
}
bool operator==(const sexp_variable& left, const sexp_variable& right) {
return left.type == right.type && !strcmp(left.text, right.text)
&& !strcmp(left.variable_name, right.variable_name);
}
bool operator==(const player& lhs, const player& rhs) {
return !strcmp(lhs.callsign, rhs.callsign) && !strcmp(lhs.short_callsign, rhs.short_callsign)
&& lhs.short_callsign_width == rhs.short_callsign_width && !strcmp(lhs.image_filename, rhs.image_filename)
&& !strcmp(lhs.s_squad_filename, rhs.s_squad_filename) && !strcmp(lhs.s_squad_name, rhs.s_squad_name)
&& !strcmp(lhs.m_squad_filename, rhs.m_squad_filename) && !strcmp(lhs.m_squad_name, rhs.m_squad_name)
&& !strcmp(lhs.current_campaign, rhs.current_campaign) && lhs.readyroom_listing_mode == rhs.readyroom_listing_mode
&& lhs.flags == rhs.flags && lhs.save_flags == rhs.save_flags && array_compare(lhs.keyed_targets, rhs.keyed_targets)
&& lhs.current_hotkey_set == rhs.current_hotkey_set && lhs.lead_target_pos == rhs.lead_target_pos
&& lhs.lead_target_cheat == rhs.lead_target_cheat && lhs.lead_indicator_active == rhs.lead_indicator_active
&& lhs.lock_indicator_x == rhs.lock_indicator_x && lhs.lock_indicator_y == rhs.lock_indicator_y
&& lhs.lock_indicator_start_x == rhs.lock_indicator_start_x
&& lhs.lock_indicator_start_y == rhs.lock_indicator_start_y
&& lhs.lock_indicator_visible == rhs.lock_indicator_visible
&& lhs.lock_time_to_target == rhs.lock_time_to_target && lhs.lock_dist_to_target == rhs.lock_dist_to_target
&& lhs.last_ship_flown_si_index == rhs.last_ship_flown_si_index && lhs.objnum == rhs.objnum && lhs.bi == rhs.bi
&& lhs.ci == rhs.ci && lhs.stats == rhs.stats && lhs.friendly_hits == rhs.friendly_hits
&& lhs.friendly_damage == rhs.friendly_damage && lhs.friendly_last_hit_time == rhs.friendly_last_hit_time
&& lhs.last_warning_message_time == rhs.last_warning_message_time && lhs.control_mode == rhs.control_mode
&& lhs.saved_viewer_mode == rhs.saved_viewer_mode && lhs.check_warn_timestamp == rhs.check_warn_timestamp
&& lhs.distance_warning_count == rhs.distance_warning_count
&& lhs.distance_warning_time == rhs.distance_warning_time
&& lhs.allow_warn_timestamp == rhs.allow_warn_timestamp && lhs.warn_count == rhs.warn_count
&& lhs.damage_this_burst == rhs.damage_this_burst && lhs.repair_sound_loop == rhs.repair_sound_loop
&& lhs.cargo_scan_loop == rhs.cargo_scan_loop && lhs.praise_count == rhs.praise_count
&& lhs.allow_praise_timestamp == rhs.allow_praise_timestamp
&& lhs.praise_delay_timestamp == rhs.praise_delay_timestamp && lhs.ask_help_count == rhs.ask_help_count
&& lhs.allow_ask_help_timestamp == rhs.allow_ask_help_timestamp && lhs.scream_count == rhs.scream_count
&& lhs.allow_scream_timestamp == rhs.allow_scream_timestamp
&& lhs.low_ammo_complaint_count == rhs.low_ammo_complaint_count
&& lhs.allow_ammo_timestamp == rhs.allow_ammo_timestamp && lhs.praise_self_count == rhs.praise_self_count
&& lhs.praise_self_timestamp == rhs.praise_self_timestamp && lhs.subsys_in_view == rhs.subsys_in_view
&& lhs.request_repair_timestamp == rhs.request_repair_timestamp
&& lhs.cargo_inspect_time == rhs.cargo_inspect_time && lhs.target_is_dying == rhs.target_is_dying
&& lhs.current_target_sx == rhs.current_target_sx && lhs.current_target_sy == rhs.current_target_sy
&& lhs.target_in_lock_cone == rhs.target_in_lock_cone && lhs.locking_subsys == rhs.locking_subsys
&& lhs.locking_subsys_parent == rhs.locking_subsys_parent && lhs.locking_on_center == rhs.locking_on_center
&& lhs.killer_objtype == rhs.killer_objtype && lhs.killer_species == rhs.killer_species
&& lhs.killer_weapon_index == rhs.killer_weapon_index && !strcmp(lhs.killer_parent_name, rhs.killer_parent_name)
&& lhs.check_for_all_alone_msg == rhs.check_for_all_alone_msg
&& lhs.update_dumbfire_time == rhs.update_dumbfire_time && lhs.update_lock_time == rhs.update_lock_time
&& lhs.threat_flags == rhs.threat_flags && lhs.auto_advance == rhs.auto_advance
&& lhs.m_local_options == rhs.m_local_options && lhs.m_server_options == rhs.m_server_options
&& lhs.insignia_texture == rhs.insignia_texture && lhs.tips == rhs.tips
&& lhs.shield_penalty_stamp == rhs.shield_penalty_stamp
&& lhs.failures_this_session == rhs.failures_this_session && lhs.show_skip_popup == rhs.show_skip_popup
&& lhs.variables == rhs.variables && lhs.containers == rhs.containers && lhs.death_message == rhs.death_message
&& lhs.lua_ci == rhs.lua_ci && lhs.lua_bi == rhs.lua_bi && lhs.lua_bi_full == rhs.lua_bi_full
&& lhs.player_was_multi == rhs.player_was_multi && !strcmp(lhs.language, rhs.language);
}
bool operator!=(const player& lhs, const player& rhs) {
return !(rhs == lhs);
}