Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions code/menuui/readyroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1575,10 +1575,22 @@ void campaign_room_scroll_info_down()

void campaign_reset(const SCP_string& campaign_file)
{
// note: we do not toss all-time stats from player's performance in campaign up till now
mission_campaign_savefile_delete(campaign_file.c_str());
Assert(Player != nullptr);

// if the caller is resetting a campaign other than the active one (only reachable via the Lua resetCampaign API),
// we can't go through mission_campaign_load(): that would swap the global Campaign struct over to the target
// campaign and overwrite Player->stats with the wrong running totals. Just delete the CSG; the next time the
// player activates this campaign, mission_campaign_load() will recreate a fresh one.
if (stricmp(campaign_file.c_str(), Campaign.filename) != 0) {
mission_campaign_savefile_delete(campaign_file.c_str());
return;
}

const int load_status = mission_campaign_load(campaign_file.c_str(), nullptr, nullptr, 1);
// note: just as in retail, we do not toss all-time stats from player's performance in campaign up till now;
// reset_accumulated_stats=false tells mission_campaign_load() (and through it, csg_reset_data())
// to leave Player->stats alone when it recreates the CSG, so accumulated score/rank/kills/medals carry over
mission_campaign_savefile_delete(campaign_file.c_str());
const int load_status = mission_campaign_load(campaign_file.c_str(), nullptr, nullptr, true, false);

// see if we successfully loaded this campaign
if (load_status == 0) {
Expand Down
8 changes: 6 additions & 2 deletions code/mission/missioncampaign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ void mission_campaign_get_sw_info()
* this file. If you change the format of the campaign file, you should be sure these related
* functions work properly and update them if it breaks them.
*/
int mission_campaign_load(const char* filename, const char* full_path, player* pl, int load_savefile)
int mission_campaign_load(const char* filename, const char* full_path, player* pl, bool load_savefile, bool reset_accumulated_stats)
{
int i;
char name[NAME_LENGTH], type[NAME_LENGTH], temp[NAME_LENGTH];
Expand Down Expand Up @@ -709,7 +709,11 @@ int mission_campaign_load(const char* filename, const char* full_path, player* p
}
// start with fresh new campaign data
else {
Pilot.clear_savefile(false); // don't reset ships and weapons because they are currently the campaign's starting ones
// don't reset ships and weapons because they have just been set to the campaign's starting ones;
// don't reset score/rank because it tracks all-time score/rank and we want to stay consistent;
// reset_accumulated_stats is false when the caller is resetting an existing campaign and wants to
// keep the player's accumulated totals
Pilot.clear_savefile(false, reset_accumulated_stats, false);
Campaign.next_mission = 0;
Pilot.save_savefile();
}
Expand Down
2 changes: 1 addition & 1 deletion code/mission/missioncampaign.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void player_loadout_init();
void mission_campaign_init( void );

// load up and initialize a new campaign
int mission_campaign_load(const char* filename, const char* full_path = nullptr, player* pl = nullptr, int load_savefile = 1);
int mission_campaign_load(const char* filename, const char* full_path = nullptr, player* pl = nullptr, bool load_savefile = true, bool reset_accumulated_stats = true);

bool campaign_is_ignored(const char *filename);

Expand Down
16 changes: 10 additions & 6 deletions code/pilotfile/csg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1594,7 +1594,7 @@ void pilotfile::csg_write_container(const sexp_container &container)
}
}

void pilotfile::csg_reset_data(bool reset_ships_and_weapons)
void pilotfile::csg_reset_data(bool reset_ships_and_weapons, bool reset_accumulated_stats, bool reset_score_and_rank)
{
int idx;
cmission *missionp;
Expand All @@ -1605,8 +1605,12 @@ void pilotfile::csg_reset_data(bool reset_ships_and_weapons)

m_data_invalid = false;

// init stats
p->stats.init();
// init stats (unless the caller wants to keep the player's accumulated totals,
// e.g. for a campaign reset)
if (reset_accumulated_stats) {
// even when we do clear stats, we might not clear score or rank
p->stats.init(reset_score_and_rank);
}

// zero out allowed ships/weapons
if (reset_ships_and_weapons) {
Expand Down Expand Up @@ -1723,7 +1727,7 @@ bool pilotfile::load_savefile(player *_p, const char *campaign)

mprintf(("CSG => Loading '%s' with version %d...\n", filename.c_str(), (int)csg_ver));

csg_reset_data(true);
csg_reset_data(true, true, true);

// the point of all this: read in the CSG contents
while ( !cfeof(cfp) ) {
Expand Down Expand Up @@ -1948,7 +1952,7 @@ bool pilotfile::save_savefile()
return true;
}

void pilotfile::clear_savefile(bool reset_ships_and_weapons)
void pilotfile::clear_savefile(bool reset_ships_and_weapons, bool reset_accumulated_stats, bool reset_score_and_rank)
{
if (Game_mode & GM_MULTIPLAYER) {
return;
Expand All @@ -1958,7 +1962,7 @@ void pilotfile::clear_savefile(bool reset_ships_and_weapons)
Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
p = &Players[Player_num];

csg_reset_data(reset_ships_and_weapons);
csg_reset_data(reset_ships_and_weapons, reset_accumulated_stats, reset_score_and_rank);
}

/*
Expand Down
4 changes: 2 additions & 2 deletions code/pilotfile/pilotfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class pilotfile {
bool save_player(player *_p = nullptr);
bool save_savefile();

void clear_savefile(bool reset_ships_and_weapons);
void clear_savefile(bool reset_ships_and_weapons, bool reset_accumulated_stats, bool reset_score_and_rank);

// updating stats, multi and/or all-time
void update_stats(scoring_struct *stats, bool training = false);
Expand Down Expand Up @@ -215,7 +215,7 @@ class pilotfile {
// --------------------------------------------------------------------
// CSG specific
// --------------------------------------------------------------------
void csg_reset_data(bool reset_ships_and_weapons);
void csg_reset_data(bool reset_ships_and_weapons, bool reset_accumulated_stats, bool reset_score_and_rank);
void csg_close();

void csg_read_flags();
Expand Down
2 changes: 1 addition & 1 deletion code/playerman/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class player
int objnum; // object number for this player
button_info bi; // structure that holds bit vectors for button presses
control_info ci; // control info structure for this player
scoring_struct stats; // scoring and stats info for the player (points to multi_stats or single_stats)
scoring_struct stats; // scoring and stats info for the player that are currently accumulating; loaded from multi_stats or all_time_stats

int friendly_hits; // Number of times hit a friendly ship this mission.
float friendly_damage; // Total friendly damage done in mission. Diminishes over time.
Expand Down
5 changes: 4 additions & 1 deletion code/scripting/api/objs/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,17 @@ player_h& player_h::operator=(player_h&& other) noexcept
ADE_OBJ(l_Player, player_h, "player", "Player handle");


ADE_VIRTVAR(Stats, l_Player, "scoring_stats stats", "The scoring stats of this player (read-only)", "scoring_stats", "The player stats or invalid handle") {
ADE_VIRTVAR(Stats, l_Player, "scoring_stats stats", "The scoring stats of this player that are currently accumulating, e.g. from a campaign or multiplayer session; read-only", "scoring_stats", "The player stats or invalid handle") {
player_h* plr;
if (!ade_get_args(L, "o", l_Player.GetPtr(&plr)))
return ade_set_error(L, "o", l_ScoringStats.Set(scoring_stats_h()));

if (!plr->isValid())
return ade_set_error(L, "o", l_ScoringStats.Set(scoring_stats_h()));

if (ADE_SETTING_VAR)
LuaError(L, "This property is read only.");

return ade_set_args(L, "o", l_ScoringStats.Set(scoring_stats_h(plr->get()->stats, plr->get())));
}

Expand Down
11 changes: 7 additions & 4 deletions code/stats/scoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,13 +408,16 @@ void traitor_init()
}

// initialize a nice blank scoring element
void scoring_struct::init()
void scoring_struct::init(bool reset_score_and_rank)
{
flags = 0;
score = 0;
rank = 0;

medal_counts.assign((int)Medals.size(), 0);
if (reset_score_and_rank) {
score = 0;
rank = 0;
}

medal_counts.assign(Medals.size(), 0);

memset(kills, 0, sizeof(kills));
assists = 0;
Expand Down
2 changes: 1 addition & 1 deletion code/stats/scoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class scoring_struct
scoring_struct(const scoring_struct &s) { assign(s); }
scoring_struct& operator=(const scoring_struct &s) { assign(s); return *this; }

void init();
void init(bool reset_score_and_rank = true);
void assign(const scoring_struct &s);

bool operator==(const scoring_struct& rhs) const;
Expand Down
2 changes: 1 addition & 1 deletion fred2/campaigneditordlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ void campaign_editor::load_campaign(const char *filename, const char *full_path)
else
m_current_campaign_path = _T("");

auto result = mission_campaign_load(filename, full_path, nullptr, 0);
auto result = mission_campaign_load(filename, full_path, nullptr, false);
if (result != 0) {
if (result == CAMPAIGN_ERROR_CORRUPT)
MessageBox("Requested campaign file is corrupt.", "Could not load campaign file");
Expand Down
2 changes: 1 addition & 1 deletion qtfred/src/mission/dialogs/CampaignEditorDialogModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ void CampaignEditorDialogModel::loadCampaignFromFile(const SCP_string& filename)

// Attempt to load the selected file into the global Campaign struct.
// We pass the full path via the filename argument now.
if (mission_campaign_load(filename.c_str(), filename.c_str(), nullptr, 0) != 0) {
if (mission_campaign_load(filename.c_str(), filename.c_str(), nullptr, false) != 0) {
// Load failed. Reset the model to a clean "new campaign" state.
initializeData(nullptr);
clearCampaignGlobal(); // Ensure cleanup after failed load
Expand Down
Loading