diff --git a/doc/action.md b/doc/action.md index cef5c473c..97609d603 100644 --- a/doc/action.md +++ b/doc/action.md @@ -268,6 +268,15 @@ The forfeit system provides two ways to end a match early: captain-initiated for **Abandonment Forfeit**: When enabled, if all teams have zero players during a match in progress (at least one round played or any team has a score), the abandon timer begins counting down. If a player reconnects and joins a team before the timer expires, it resets. Warnings are printed at 30 seconds, 10 seconds, and a final 5-second countdown. If the timer expires, the match ends with no score awarded. +#### Score Carryover + +In multi-map matches (e.g., best-of-two), team scores are normally reset when the map changes. Enabling `mm_carryover` preserves team scores from the first map into the second map, so the final scoreboard reflects the cumulative result across both maps. + +- Server settings: + - `mm_carryover [0/1]` - Carry over team scores from map 1 to map 2 (default: 0). Requires matchmode to be enabled. Supports `use_3teams`. + +After the second map concludes, the carryover is automatically cleared. Stat logging subtracts the carried-over scores so that per-map statistics remain accurate. + #### Timeout Settings Timeouts allow teams to pause the match for a strategic break. The following cvars control timeout behavior: diff --git a/src/action/a_match.c b/src/action/a_match.c index ab334910c..1ba9adb27 100644 --- a/src/action/a_match.c +++ b/src/action/a_match.c @@ -92,6 +92,17 @@ void SendScores(void) // Stats: Reset roundNum game.roundNum = 0; // Stats end + + // Clear carryover scores after map 2 is done. + // Keep carryover_active=true so ExitLevel() won't re-save scores. + // SpawnEntities() on map 3 will reset carryover_active when it finds no scores. + if (game.carryover_active) + { + int i; + for(i = TEAM1; i < TEAM_TOP; i++) + game.carryover_scores[i] = 0; + gi.dprintf("Matchmode carryover: cleared after map 2\n"); + } } void Cmd_Sub_f(edict_t * ent) diff --git a/src/action/g_local.h b/src/action/g_local.h index 4cf5f224e..409384ad5 100644 --- a/src/action/g_local.h +++ b/src/action/g_local.h @@ -879,6 +879,10 @@ typedef struct // LRCON configuration lrcon_config_t lrcon_config; + + // Matchmode carryover scores (persist across map changes) + int carryover_scores[TEAM_TOP]; + qboolean carryover_active; // true if we're on map 2 with carried-over scores } game_locals_t; @@ -1451,6 +1455,7 @@ extern cvar_t *bots; // If bots are enabled and in the server // 2026 extern cvar_t *use_buggy_ent_hitbox; +extern cvar_t *mm_carryover; // Carry over team scores across maps in matchmode #ifdef AQTION_EXTENSION extern int (*engine_Client_GetVersion)(edict_t *ent); diff --git a/src/action/g_main.c b/src/action/g_main.c index 41546d2cc..3077f82cc 100644 --- a/src/action/g_main.c +++ b/src/action/g_main.c @@ -605,6 +605,7 @@ cvar_t *bots; // If bots are enabled and in the server // 2026 cvar_t *use_buggy_ent_hitbox; // Enables classic dead entity hitbox +cvar_t *mm_carryover; // Carry over team scores across maps in matchmode #ifdef AQTION_EXTENSION cvar_t *use_newirvision; @@ -1254,6 +1255,17 @@ void ExitLevel (void) // clear some things before going to next level if (teamplay->value) { + // Save scores for carryover if enabled in matchmode + if (mm_carryover->value && matchmode->value && !game.carryover_active) + { + for(i=TEAM1; iname, "0"); } + // Restore carried-over scores from previous map in matchmode + if (mm_carryover->value && matchmode->value && + (game.carryover_scores[TEAM1] || game.carryover_scores[TEAM2] || game.carryover_scores[TEAM3])) + { + for(i = TEAM1; i < TEAM_TOP; i++) + { + teams[i].score = game.carryover_scores[i]; + if (teams[i].teamscore) { + char val[16]; + Q_snprintf(val, sizeof(val), "%d", game.carryover_scores[i]); + gi.cvar_forceset(teams[i].teamscore->name, val); + } + } + game.carryover_active = true; + gi.dprintf("Matchmode carryover: restored scores t1=%d t2=%d t3=%d\n", + game.carryover_scores[TEAM1], + game.carryover_scores[TEAM2], + game.carryover_scores[TEAM3]); + } + else + { + game.carryover_active = false; + } + day_cycle_at = 0; team_round_going = team_game_going = team_round_countdown = 0; lights_camera_action = holding_on_tie_check = 0; diff --git a/src/action/tng_stats.c b/src/action/tng_stats.c index d14dd766a..4a0f23029 100644 --- a/src/action/tng_stats.c +++ b/src/action/tng_stats.c @@ -1324,6 +1324,14 @@ void LogMatch(void) int t3 = teams[TEAM3].score; eventtime = (int)time(NULL); + // Subtract carried-over scores so stats reflect only this map's results + if (game.carryover_active) + { + t1 -= game.carryover_scores[TEAM1]; + t2 -= game.carryover_scores[TEAM2]; + t3 -= game.carryover_scores[TEAM3]; + } + // Check if there's an AI bot in the game, if so, do nothing if (game.ai_ent_found) { return; diff --git a/subprojects/packagefiles/.DS_Store b/subprojects/packagefiles/.DS_Store deleted file mode 100644 index 755eb1e48..000000000 Binary files a/subprojects/packagefiles/.DS_Store and /dev/null differ