From a0d52ddacd1938a8099f0ad75d4b6f29e929dd80 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 22 Feb 2024 00:06:49 -0600 Subject: [PATCH 01/14] Add per-gametype time stats --- src/g_game.c | 9 +-------- src/g_gamedata.cpp | 10 ++++++++++ src/g_gamedata.h | 16 +++++++++++++++- src/m_cond.c | 16 ++++++++++++++++ src/m_cond.h | 2 ++ src/p_tick.c | 9 +++++++++ 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1c4d23a80..96d57b7f5 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4516,14 +4516,7 @@ static void G_DoCompleted(void) { if (gametype != GT_TUTORIAL) { - UINT8 roundtype = GDGT_CUSTOM; - - if (gametype == GT_RACE) - roundtype = GDGT_RACE; - else if (gametype == GT_BATTLE) - roundtype = (battleprisons ? GDGT_PRISONS : GDGT_BATTLE); - else if (gametype == GT_SPECIAL || gametype == GT_VERSUS) - roundtype = GDGT_SPECIAL; + UINT8 roundtype = M_GameDataGameType(gametype, battleprisons); gamedata->roundsplayed[roundtype]++; } diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index fc1bdd277..14c8a15f5 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -46,6 +46,11 @@ void srb2::save_ng_gamedata() GamedataJson ng {}; ng.playtime.total = gamedata->totalplaytime; + ng.playtime.race = gamedata->modeplaytime[GDGT_RACE]; + ng.playtime.battle = gamedata->modeplaytime[GDGT_BATTLE]; + ng.playtime.prisons = gamedata->modeplaytime[GDGT_PRISONS]; + ng.playtime.special = gamedata->modeplaytime[GDGT_SPECIAL]; + ng.playtime.custom = gamedata->modeplaytime[GDGT_CUSTOM]; ng.rings.total = gamedata->totalrings; ng.playtime.tumble = gamedata->totaltumbletime; ng.rounds.race = gamedata->roundsplayed[GDGT_RACE]; @@ -412,6 +417,11 @@ void srb2::load_ng_gamedata() gamedata->evercrashed = dirty; gamedata->totalplaytime = js.playtime.total; + gamedata->modeplaytime[GDGT_RACE] = js.playtime.race; + gamedata->modeplaytime[GDGT_BATTLE] = js.playtime.battle; + gamedata->modeplaytime[GDGT_PRISONS] = js.playtime.prisons; + gamedata->modeplaytime[GDGT_SPECIAL] = js.playtime.special; + gamedata->modeplaytime[GDGT_CUSTOM] = js.playtime.custom; gamedata->totalrings = js.rings.total; gamedata->totaltumbletime = js.playtime.tumble; gamedata->roundsplayed[GDGT_RACE] = js.rounds.race; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index c5ba7bdcd..f5a2ea780 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -27,9 +27,23 @@ namespace srb2 struct GamedataPlaytimeJson final { uint32_t total; + uint32_t race; + uint32_t battle; + uint32_t prisons; + uint32_t special; + uint32_t custom; uint32_t tumble; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataPlaytimeJson, total, tumble) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + GamedataPlaytimeJson, + total, + race, + battle, + prisons, + special, + custom, + tumble + ) }; struct GamedataRingsJson final diff --git a/src/m_cond.c b/src/m_cond.c index 3deba3a63..49d6177a6 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -652,6 +652,8 @@ void M_ClearStats(void) { UINT8 i; gamedata->totalplaytime = 0; + for (i = 0; i < GDGT_MAX; ++i) + gamedata->modeplaytime[i] = 0; gamedata->totalrings = 0; gamedata->totaltumbletime = 0; for (i = 0; i < GDGT_MAX; ++i) @@ -3823,3 +3825,17 @@ boolean M_UseAlternateTitleScreen(void) extern consvar_t cv_alttitle; return cv_alttitle.value && M_SecretUnlocked(SECRET_ALTTITLE, true); } + +INT32 M_GameDataGameType(INT32 lgametype, boolean lbattleprisons) +{ + INT32 playtimemode = GDGT_CUSTOM; + if (lgametype == GT_RACE) + playtimemode = GDGT_RACE; + else if (lgametype == GT_BATTLE) + playtimemode = lbattleprisons ? GDGT_PRISONS : GDGT_BATTLE; + else if (lgametype == GT_SPECIAL || lgametype == GT_VERSUS) + playtimemode = GDGT_SPECIAL; + + return playtimemode; +} + diff --git a/src/m_cond.h b/src/m_cond.h index 42e7184a9..c8933f9a9 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -369,6 +369,7 @@ struct gamedata_t // PLAY TIME UINT32 totalplaytime; + UINT32 modeplaytime[GDGT_MAX]; UINT32 roundsplayed[GDGT_MAX]; UINT32 totalrings; UINT32 totaltumbletime; @@ -495,6 +496,7 @@ UINT16 M_EmblemMapNum(emblem_t *emblem); #define M_Achieved(a) ((a) >= MAXCONDITIONSETS || gamedata->achieved[a]) boolean M_UseAlternateTitleScreen(void); +INT32 M_GameDataGameType(INT32 gametype, boolean battleprisons); #ifdef __cplusplus } // extern "C" diff --git a/src/p_tick.c b/src/p_tick.c index ae753dea8..162941c64 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -944,6 +944,15 @@ void P_Ticker(boolean run) // Keep track of how long they've been playing! gamedata->totalplaytime++; + if (gametype != GT_TUTORIAL) + { + INT32 mode = M_GameDataGameType(gametype, battleprisons); + if (mode >= 0 && mode < GDGT_MAX) + { + gamedata->modeplaytime[mode]++; + } + } + // TODO would this be laggy with more conditions in play... if ( (leveltime > introtime From 19341b71a34745e4521ab735a630463874f5ff02 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 Feb 2024 16:58:53 -0600 Subject: [PATCH 02/14] Add total menu time stat --- src/g_gamedata.cpp | 2 ++ src/g_gamedata.h | 2 ++ src/k_menufunc.c | 6 ++++++ src/m_cond.c | 1 + src/m_cond.h | 1 + 5 files changed, 12 insertions(+) diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index 14c8a15f5..6cd19aaff 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -51,6 +51,7 @@ void srb2::save_ng_gamedata() ng.playtime.prisons = gamedata->modeplaytime[GDGT_PRISONS]; ng.playtime.special = gamedata->modeplaytime[GDGT_SPECIAL]; ng.playtime.custom = gamedata->modeplaytime[GDGT_CUSTOM]; + ng.playtime.menus = gamedata->totalmenutime; ng.rings.total = gamedata->totalrings; ng.playtime.tumble = gamedata->totaltumbletime; ng.rounds.race = gamedata->roundsplayed[GDGT_RACE]; @@ -422,6 +423,7 @@ void srb2::load_ng_gamedata() gamedata->modeplaytime[GDGT_PRISONS] = js.playtime.prisons; gamedata->modeplaytime[GDGT_SPECIAL] = js.playtime.special; gamedata->modeplaytime[GDGT_CUSTOM] = js.playtime.custom; + gamedata->totalmenutime = js.playtime.menus; gamedata->totalrings = js.rings.total; gamedata->totaltumbletime = js.playtime.tumble; gamedata->roundsplayed[GDGT_RACE] = js.rounds.race; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index f5a2ea780..f00874acd 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -32,6 +32,7 @@ struct GamedataPlaytimeJson final uint32_t prisons; uint32_t special; uint32_t custom; + uint32_t menus; uint32_t tumble; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( @@ -42,6 +43,7 @@ struct GamedataPlaytimeJson final prisons, special, custom, + menus, tumble ) }; diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 618840024..b8155c46e 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1429,6 +1429,12 @@ void M_Ticker(void) skullAnimCounter = 8; } + if (!Playing()) + { + // Anything in M_Ticker that isn't actively playing is considered "in menus" for time tracking + gamedata->totalmenutime++; + } + if (!Playing() && !M_GameTrulyStarted()) { M_GonerBGTick(); diff --git a/src/m_cond.c b/src/m_cond.c index 49d6177a6..e113d0b9f 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -654,6 +654,7 @@ void M_ClearStats(void) gamedata->totalplaytime = 0; for (i = 0; i < GDGT_MAX; ++i) gamedata->modeplaytime[i] = 0; + gamedata->totalmenutime = 0; gamedata->totalrings = 0; gamedata->totaltumbletime = 0; for (i = 0; i < GDGT_MAX; ++i) diff --git a/src/m_cond.h b/src/m_cond.h index c8933f9a9..c90877383 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -370,6 +370,7 @@ struct gamedata_t // PLAY TIME UINT32 totalplaytime; UINT32 modeplaytime[GDGT_MAX]; + UINT32 totalmenutime; UINT32 roundsplayed[GDGT_MAX]; UINT32 totalrings; UINT32 totaltumbletime; From c22d6d75db0f58c4f91b13e8ab3de6721b0529fc Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 Feb 2024 18:09:26 -0600 Subject: [PATCH 03/14] Add several skin record stats, profile rounds --- src/doomstat.h | 4 ++++ src/g_gamedata.cpp | 23 +++++++++++++++++++-- src/g_gamedata.h | 31 +++++++++++++++++++++++++++- src/k_kart.c | 8 ++++++++ src/k_profiles.cpp | 3 +++ src/k_profiles.h | 4 +++- src/p_tick.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ src/p_user.c | 21 +++++++++++++++---- 8 files changed, 137 insertions(+), 8 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index 42420d911..20af72ed1 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -122,6 +122,10 @@ extern preciptype_t curWeather; struct skinrecord_t { UINT32 wins; + UINT32 rounds; + UINT32 timeplayed; + UINT32 modetimeplayed[5]; // no GDGT_MAX, m_cond.h is not included in here... it might need to be? + UINT32 tumbletime; }; struct unloaded_skin_t diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index 6cd19aaff..d95b3796e 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -109,8 +109,18 @@ void srb2::save_ng_gamedata() for (int i = 0; i < numskins; i++) { srb2::GamedataSkinJson skin {}; - std::string name = std::string(skins[i].name); - skin.records.wins = skins[i].records.wins; + skin_t& memskin = skins[i]; + + std::string name = std::string(memskin.name); + skin.records.wins = memskin.records.wins; + skin.records.rounds = memskin.records.rounds; + skin.records.time.total = memskin.records.timeplayed; + skin.records.time.race = memskin.records.modetimeplayed[GDGT_RACE]; + skin.records.time.battle = memskin.records.modetimeplayed[GDGT_BATTLE]; + skin.records.time.prisons = memskin.records.modetimeplayed[GDGT_PRISONS]; + skin.records.time.special = memskin.records.modetimeplayed[GDGT_SPECIAL]; + skin.records.time.custom = memskin.records.modetimeplayed[GDGT_CUSTOM]; + skin.records.time.tumble = memskin.records.tumbletime; ng.skins[name] = std::move(skin); } for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next) @@ -516,7 +526,16 @@ void srb2::load_ng_gamedata() { INT32 skin = R_SkinAvailableEx(skinpair.first.c_str(), false); skinrecord_t dummyrecord {}; + dummyrecord.wins = skinpair.second.records.wins; + dummyrecord.rounds = skinpair.second.records.rounds; + dummyrecord.timeplayed = skinpair.second.records.time.total; + dummyrecord.modetimeplayed[GDGT_RACE] = skinpair.second.records.time.race; + dummyrecord.modetimeplayed[GDGT_BATTLE] = skinpair.second.records.time.battle; + dummyrecord.modetimeplayed[GDGT_PRISONS] = skinpair.second.records.time.prisons; + dummyrecord.modetimeplayed[GDGT_SPECIAL] = skinpair.second.records.time.special; + dummyrecord.modetimeplayed[GDGT_CUSTOM] = skinpair.second.records.time.custom; + dummyrecord.tumbletime = skinpair.second.records.time.tumble; if (skin != -1) { diff --git a/src/g_gamedata.h b/src/g_gamedata.h index f00874acd..0b34c477a 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -120,11 +120,40 @@ struct GamedataChallengeGridJson final NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeGridJson, width, grid) }; +struct GamedataSkinRecordsPlaytimeJson final +{ + uint32_t total; + uint32_t race; + uint32_t battle; + uint32_t prisons; + uint32_t special; + uint32_t custom; + uint32_t tumble; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + GamedataSkinRecordsPlaytimeJson, + total, + race, + battle, + prisons, + special, + custom, + tumble + ) +}; + struct GamedataSkinRecordsJson final { uint32_t wins; + uint32_t rounds; + GamedataSkinRecordsPlaytimeJson time; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSkinRecordsJson, wins) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + GamedataSkinRecordsJson, + wins, + rounds, + time + ) }; struct GamedataSkinJson final diff --git a/src/k_kart.c b/src/k_kart.c index 215265bca..f4122ee6f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8196,11 +8196,19 @@ void K_KartPlayerHUDUpdate(player_t *player) else player->karthud[khud_finish] = 0; + // Tumble time stat update if (demo.playback == false && P_IsMachineLocalPlayer(player) == true) { if (player->tumbleBounces != 0 && gamedata->totaltumbletime != UINT32_MAX) { gamedata->totaltumbletime++; + + if (player->skin >= 0 && player->skin < numskins) + { + skin_t *playerskin; + playerskin = &skins[player->skin]; + playerskin->records.tumbletime++; + } } } } diff --git a/src/k_profiles.cpp b/src/k_profiles.cpp index 1206c90c8..2d29e8c82 100644 --- a/src/k_profiles.cpp +++ b/src/k_profiles.cpp @@ -88,6 +88,7 @@ profile_t* PR_MakeProfile( memcpy(newprofile->controls, controlarray, sizeof(newprofile->controls)); newprofile->wins = 0; + newprofile->rounds = 0; return newprofile; } @@ -293,6 +294,7 @@ void PR_SaveProfiles(void) jsonprof.followercolorname = std::string(skincolors[cprof->followercolor].name); } jsonprof.records.wins = cprof->wins; + jsonprof.records.rounds = cprof->rounds; jsonprof.preferences.kickstartaccel = cprof->kickstartaccel; jsonprof.preferences.autoroulette = cprof->autoroulette; jsonprof.preferences.litesteer = cprof->litesteer; @@ -458,6 +460,7 @@ void PR_LoadProfiles(void) } newprof->wins = jsprof.records.wins; + newprof->rounds = jsprof.records.rounds; newprof->kickstartaccel = jsprof.preferences.kickstartaccel; newprof->autoroulette = jsprof.preferences.autoroulette; newprof->litesteer = jsprof.preferences.litesteer; diff --git a/src/k_profiles.h b/src/k_profiles.h index 1bbb48d6e..0d1c922bd 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -36,8 +36,9 @@ namespace srb2 struct ProfileRecordsJson { uint32_t wins; + uint32_t rounds; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfileRecordsJson, wins) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfileRecordsJson, wins, rounds) }; struct ProfilePreferencesJson @@ -147,6 +148,7 @@ struct profile_t UINT16 followercolor; // Follower color UINT32 wins; // I win I win I win + UINT32 rounds; // I played I played I played // Player-specific consvars. // @TODO: List all of those diff --git a/src/p_tick.c b/src/p_tick.c index 162941c64..a42161ce5 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -944,13 +944,64 @@ void P_Ticker(boolean run) // Keep track of how long they've been playing! gamedata->totalplaytime++; + // Per-skin total playtime for all machine-local players + for (i = 0; i < MAXPLAYERS; i++) + { + skin_t *playerskin; + + if (!P_IsMachineLocalPlayer(&players[i])) + { + continue; + } + + if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))) + { + continue; + } + + if (players[i].skin >= numskins) + { + continue; + } + + playerskin = &skins[players[i].skin]; + + playerskin->records.timeplayed++; + } + if (gametype != GT_TUTORIAL) { INT32 mode = M_GameDataGameType(gametype, battleprisons); + + // Gamedata mode playtime if (mode >= 0 && mode < GDGT_MAX) { gamedata->modeplaytime[mode]++; } + + // Per-skin mode playtime + for (i = 0; i < MAXPLAYERS; i++) + { + skin_t *playerskin; + + if (!P_IsMachineLocalPlayer(&players[i])) + { + continue; + } + + if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))) + { + continue; + } + + if (players[i].skin >= numskins) + { + continue; + } + + playerskin = &skins[players[i].skin]; + playerskin->records.modetimeplayed[mode]++; + } } // TODO would this be laggy with more conditions in play... diff --git a/src/p_user.c b/src/p_user.c index e4bdd8a3b..ecfe54dee 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1315,18 +1315,31 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) G_UpdateRecords(); } - if (!losing) + // Profile and skin record updates + if (P_IsMachineLocalPlayer(player)) { profile_t *pr = PR_GetPlayerProfile(player); + + // Profile records if (pr != NULL) { - pr->wins++; + if (!losing) + { + pr->wins++; + } + pr->rounds++; PR_SaveProfiles(); } - if (P_IsMachineLocalPlayer(player) && player->skin < numskins) + // Skin records (saved to gamedata) + if (player->skin < numskins) { - skins[player->skin].records.wins++; + skin_t *playerskin = &skins[player->skin]; + if (!losing) + { + playerskin->records.wins++; + } + playerskin->records.rounds++; } } From 0cd5a662ce7b02bb99d4cd23fbec3d107280f709 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 Feb 2024 19:21:23 -0600 Subject: [PATCH 04/14] Add statistic for time looking at statistics --- src/g_gamedata.cpp | 2 ++ src/g_gamedata.h | 2 ++ src/m_cond.c | 1 + src/m_cond.h | 1 + src/menus/extras-challenges.c | 8 +++++++- 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index d95b3796e..b8f54656d 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -52,6 +52,7 @@ void srb2::save_ng_gamedata() ng.playtime.special = gamedata->modeplaytime[GDGT_SPECIAL]; ng.playtime.custom = gamedata->modeplaytime[GDGT_CUSTOM]; ng.playtime.menus = gamedata->totalmenutime; + ng.playtime.statistics = gamedata->totaltimestaringatstatistics; ng.rings.total = gamedata->totalrings; ng.playtime.tumble = gamedata->totaltumbletime; ng.rounds.race = gamedata->roundsplayed[GDGT_RACE]; @@ -434,6 +435,7 @@ void srb2::load_ng_gamedata() gamedata->modeplaytime[GDGT_SPECIAL] = js.playtime.special; gamedata->modeplaytime[GDGT_CUSTOM] = js.playtime.custom; gamedata->totalmenutime = js.playtime.menus; + gamedata->totaltimestaringatstatistics = js.playtime.statistics; gamedata->totalrings = js.rings.total; gamedata->totaltumbletime = js.playtime.tumble; gamedata->roundsplayed[GDGT_RACE] = js.rounds.race; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index 0b34c477a..1c7260c81 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -33,6 +33,7 @@ struct GamedataPlaytimeJson final uint32_t special; uint32_t custom; uint32_t menus; + uint32_t statistics; uint32_t tumble; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( @@ -44,6 +45,7 @@ struct GamedataPlaytimeJson final special, custom, menus, + statistics, tumble ) }; diff --git a/src/m_cond.c b/src/m_cond.c index e113d0b9f..019f10ada 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -655,6 +655,7 @@ void M_ClearStats(void) for (i = 0; i < GDGT_MAX; ++i) gamedata->modeplaytime[i] = 0; gamedata->totalmenutime = 0; + gamedata->totaltimestaringatstatistics = 0; gamedata->totalrings = 0; gamedata->totaltumbletime = 0; for (i = 0; i < GDGT_MAX; ++i) diff --git a/src/m_cond.h b/src/m_cond.h index c90877383..25abd437b 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -371,6 +371,7 @@ struct gamedata_t UINT32 totalplaytime; UINT32 modeplaytime[GDGT_MAX]; UINT32 totalmenutime; + UINT32 totaltimestaringatstatistics; UINT32 roundsplayed[GDGT_MAX]; UINT32 totalrings; UINT32 totaltumbletime; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index dfc0bd69e..dc488f953 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -23,6 +23,12 @@ extern consvar_t cv_debugchallenges; #endif +static void M_StatisticsTicker(void) +{ + // the funny + gamedata->totaltimestaringatstatistics++; +} + menuitem_t MISC_ChallengesStatsDummyMenu[] = { {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, @@ -60,7 +66,7 @@ menu_t MISC_StatisticsDef = { 98, 0, M_DrawStatistics, M_DrawExtrasBack, - NULL, + M_StatisticsTicker, NULL, NULL, M_StatisticsInputs, From 3ebe23515486d7e1c6f24d5f41ca977c05653413 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 Feb 2024 19:38:24 -0600 Subject: [PATCH 05/14] Add total netgame time stat --- src/g_gamedata.cpp | 2 ++ src/g_gamedata.h | 2 ++ src/m_cond.c | 1 + src/m_cond.h | 1 + src/p_tick.c | 6 ++++++ 5 files changed, 12 insertions(+) diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index b8f54656d..6779f2917 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -46,6 +46,7 @@ void srb2::save_ng_gamedata() GamedataJson ng {}; ng.playtime.total = gamedata->totalplaytime; + ng.playtime.netgame = gamedata->totalnetgametime; ng.playtime.race = gamedata->modeplaytime[GDGT_RACE]; ng.playtime.battle = gamedata->modeplaytime[GDGT_BATTLE]; ng.playtime.prisons = gamedata->modeplaytime[GDGT_PRISONS]; @@ -429,6 +430,7 @@ void srb2::load_ng_gamedata() gamedata->evercrashed = dirty; gamedata->totalplaytime = js.playtime.total; + gamedata->totalnetgametime = js.playtime.netgame; gamedata->modeplaytime[GDGT_RACE] = js.playtime.race; gamedata->modeplaytime[GDGT_BATTLE] = js.playtime.battle; gamedata->modeplaytime[GDGT_PRISONS] = js.playtime.prisons; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index 1c7260c81..21d9c79be 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -27,6 +27,7 @@ namespace srb2 struct GamedataPlaytimeJson final { uint32_t total; + uint32_t netgame; uint32_t race; uint32_t battle; uint32_t prisons; @@ -39,6 +40,7 @@ struct GamedataPlaytimeJson final NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( GamedataPlaytimeJson, total, + netgame, race, battle, prisons, diff --git a/src/m_cond.c b/src/m_cond.c index 019f10ada..4d6ac752d 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -652,6 +652,7 @@ void M_ClearStats(void) { UINT8 i; gamedata->totalplaytime = 0; + gamedata->totalnetgametime = 0; for (i = 0; i < GDGT_MAX; ++i) gamedata->modeplaytime[i] = 0; gamedata->totalmenutime = 0; diff --git a/src/m_cond.h b/src/m_cond.h index 25abd437b..8d1281ad2 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -369,6 +369,7 @@ struct gamedata_t // PLAY TIME UINT32 totalplaytime; + UINT32 totalnetgametime; UINT32 modeplaytime[GDGT_MAX]; UINT32 totalmenutime; UINT32 totaltimestaringatstatistics; diff --git a/src/p_tick.c b/src/p_tick.c index a42161ce5..76cecc57b 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -944,6 +944,12 @@ void P_Ticker(boolean run) // Keep track of how long they've been playing! gamedata->totalplaytime++; + // Netgame total time + if (netgame) + { + gamedata->totalnetgametime++; + } + // Per-skin total playtime for all machine-local players for (i = 0; i < MAXPLAYERS; i++) { From 23e4df3c905d21252a8d772016bd5efabed86643 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 Feb 2024 21:25:46 -0600 Subject: [PATCH 06/14] Add per-map time stats and TA/SPB time stats --- src/doomstat.h | 7 +++++++ src/g_gamedata.cpp | 31 +++++++++++++++++++++++++++++++ src/g_gamedata.h | 38 +++++++++++++++++++++++++++++++++++++- src/m_cond.c | 2 ++ src/m_cond.h | 2 ++ src/p_tick.c | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/doomstat.h b/src/doomstat.h index 20af72ed1..6a481a1a3 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -165,6 +165,13 @@ struct recorddata_t UINT8 mapvisited; recordtimes_t timeattack; ///< Best times for Time Attack recordtimes_t spbattack; ///< Best times for SPB Attack + UINT32 timeplayed; + UINT32 netgametimeplayed; + UINT32 modetimeplayed[5]; + UINT32 timeattacktimeplayed; + UINT32 spbattacktimeplayed; + UINT32 rounds; + UINT32 wins; }; #define KARTSPEED_AUTO -1 diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index 6779f2917..bc9f5a396 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -47,6 +47,8 @@ void srb2::save_ng_gamedata() ng.playtime.total = gamedata->totalplaytime; ng.playtime.netgame = gamedata->totalnetgametime; + ng.playtime.timeattack = gamedata->timeattackingtotaltime; + ng.playtime.spbattack = gamedata->spbattackingtotaltime; ng.playtime.race = gamedata->modeplaytime[GDGT_RACE]; ng.playtime.battle = gamedata->modeplaytime[GDGT_BATTLE]; ng.playtime.prisons = gamedata->modeplaytime[GDGT_PRISONS]; @@ -145,6 +147,15 @@ void srb2::save_ng_gamedata() map.stats.timeattack.bestlap = mapheaderinfo[i]->records.timeattack.lap; map.stats.spbattack.besttime = mapheaderinfo[i]->records.spbattack.time; map.stats.spbattack.bestlap = mapheaderinfo[i]->records.spbattack.lap; + map.stats.time.total = mapheaderinfo[i]->records.timeplayed; + map.stats.time.netgame = mapheaderinfo[i]->records.netgametimeplayed; + map.stats.time.race = mapheaderinfo[i]->records.modetimeplayed[GDGT_RACE]; + map.stats.time.battle = mapheaderinfo[i]->records.modetimeplayed[GDGT_BATTLE]; + map.stats.time.prisons = mapheaderinfo[i]->records.modetimeplayed[GDGT_PRISONS]; + map.stats.time.special = mapheaderinfo[i]->records.modetimeplayed[GDGT_SPECIAL]; + map.stats.time.custom = mapheaderinfo[i]->records.modetimeplayed[GDGT_CUSTOM]; + map.stats.time.timeattack = mapheaderinfo[i]->records.timeattacktimeplayed; + map.stats.time.spbattack = mapheaderinfo[i]->records.spbattacktimeplayed; ng.maps[lumpname] = std::move(map); } for (auto unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next) @@ -160,6 +171,15 @@ void srb2::save_ng_gamedata() map.stats.timeattack.bestlap = unloadedmap->records.timeattack.lap; map.stats.spbattack.besttime = unloadedmap->records.spbattack.time; map.stats.spbattack.bestlap = unloadedmap->records.spbattack.lap; + map.stats.time.total = unloadedmap->records.timeplayed; + map.stats.time.netgame = unloadedmap->records.netgametimeplayed; + map.stats.time.race = unloadedmap->records.modetimeplayed[GDGT_RACE]; + map.stats.time.battle = unloadedmap->records.modetimeplayed[GDGT_BATTLE]; + map.stats.time.prisons = unloadedmap->records.modetimeplayed[GDGT_PRISONS]; + map.stats.time.special = unloadedmap->records.modetimeplayed[GDGT_SPECIAL]; + map.stats.time.custom = unloadedmap->records.modetimeplayed[GDGT_CUSTOM]; + map.stats.time.timeattack = unloadedmap->records.timeattacktimeplayed; + map.stats.time.spbattack = unloadedmap->records.spbattacktimeplayed; ng.maps[lumpname] = std::move(map); } for (int i = 0; i < gamedata->numspraycans; i++) @@ -431,6 +451,8 @@ void srb2::load_ng_gamedata() gamedata->totalplaytime = js.playtime.total; gamedata->totalnetgametime = js.playtime.netgame; + gamedata->timeattackingtotaltime = js.playtime.timeattack; + gamedata->spbattackingtotaltime = js.playtime.spbattack; gamedata->modeplaytime[GDGT_RACE] = js.playtime.race; gamedata->modeplaytime[GDGT_BATTLE] = js.playtime.battle; gamedata->modeplaytime[GDGT_PRISONS] = js.playtime.prisons; @@ -582,6 +604,15 @@ void srb2::load_ng_gamedata() dummyrecord.timeattack.lap = mappair.second.stats.timeattack.bestlap; dummyrecord.spbattack.time = mappair.second.stats.spbattack.besttime; dummyrecord.spbattack.lap = mappair.second.stats.spbattack.bestlap; + dummyrecord.timeplayed = mappair.second.stats.time.total; + dummyrecord.netgametimeplayed = mappair.second.stats.time.netgame; + dummyrecord.modetimeplayed[GDGT_RACE] = mappair.second.stats.time.race; + dummyrecord.modetimeplayed[GDGT_BATTLE] = mappair.second.stats.time.battle; + dummyrecord.modetimeplayed[GDGT_PRISONS] = mappair.second.stats.time.prisons; + dummyrecord.modetimeplayed[GDGT_SPECIAL] = mappair.second.stats.time.special; + dummyrecord.modetimeplayed[GDGT_CUSTOM] = mappair.second.stats.time.custom; + dummyrecord.timeattacktimeplayed = mappair.second.stats.time.timeattack; + dummyrecord.spbattacktimeplayed = mappair.second.stats.time.spbattack; if (mapnum < nummapheaders && mapheaderinfo[mapnum]) { diff --git a/src/g_gamedata.h b/src/g_gamedata.h index 21d9c79be..3c21ea5c2 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -28,6 +28,8 @@ struct GamedataPlaytimeJson final { uint32_t total; uint32_t netgame; + uint32_t timeattack; + uint32_t spbattack; uint32_t race; uint32_t battle; uint32_t prisons; @@ -41,6 +43,8 @@ struct GamedataPlaytimeJson final GamedataPlaytimeJson, total, netgame, + timeattack, + spbattack, race, battle, prisons, @@ -194,12 +198,44 @@ struct GamedataMapStatsSpbAttackJson final NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsSpbAttackJson, besttime, bestlap) }; +struct GamedataMapStatsPlaytimeJson final +{ + uint32_t total; + uint32_t netgame; + uint32_t race; + uint32_t battle; + uint32_t prisons; + uint32_t special; + uint32_t custom; + uint32_t timeattack; + uint32_t spbattack; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + GamedataMapStatsPlaytimeJson, + total, + netgame, + race, + battle, + prisons, + special, + custom, + timeattack, + spbattack + ) +}; + struct GamedataMapStatsJson final { GamedataMapStatsTimeAttackJson timeattack; GamedataMapStatsSpbAttackJson spbattack; + GamedataMapStatsPlaytimeJson time; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsJson, timeattack, spbattack) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + GamedataMapStatsJson, + timeattack, + spbattack, + time + ) }; struct GamedataMapJson final diff --git a/src/m_cond.c b/src/m_cond.c index 4d6ac752d..d9f8a255b 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -653,6 +653,8 @@ void M_ClearStats(void) UINT8 i; gamedata->totalplaytime = 0; gamedata->totalnetgametime = 0; + gamedata->timeattackingtotaltime = 0; + gamedata->spbattackingtotaltime = 0; for (i = 0; i < GDGT_MAX; ++i) gamedata->modeplaytime[i] = 0; gamedata->totalmenutime = 0; diff --git a/src/m_cond.h b/src/m_cond.h index 8d1281ad2..fc2b64c5a 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -370,6 +370,8 @@ struct gamedata_t // PLAY TIME UINT32 totalplaytime; UINT32 totalnetgametime; + UINT32 timeattackingtotaltime; + UINT32 spbattackingtotaltime; UINT32 modeplaytime[GDGT_MAX]; UINT32 totalmenutime; UINT32 totaltimestaringatstatistics; diff --git a/src/p_tick.c b/src/p_tick.c index 76cecc57b..961375410 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -941,13 +941,28 @@ void P_Ticker(boolean run) if (gamedata && gamestate == GS_LEVEL && !demo.playback) { + mapheader_t *mapheader; + + mapheader = mapheaderinfo[gamemap - 1]; + // Keep track of how long they've been playing! gamedata->totalplaytime++; + // Map playtime + if (mapheader) + { + mapheader->records.timeplayed++; + } + // Netgame total time if (netgame) { gamedata->totalnetgametime++; + + if (mapheader) + { + mapheader->records.netgametimeplayed++; + } } // Per-skin total playtime for all machine-local players @@ -983,6 +998,28 @@ void P_Ticker(boolean run) if (mode >= 0 && mode < GDGT_MAX) { gamedata->modeplaytime[mode]++; + if (mapheader) + { + mapheader->records.modetimeplayed[mode]++; + } + } + + // Attacking mode playtime + if ((modeattacking & ATTACKING_TIME) != 0) + { + gamedata->timeattackingtotaltime++; + if (mapheader) + { + mapheader->records.timeattacktimeplayed++; + } + } + else if ((modeattacking & ATTACKING_SPB) != 0) + { + gamedata->spbattackingtotaltime++; + if (mapheader) + { + mapheader->records.spbattacktimeplayed++; + } } // Per-skin mode playtime From ee41d309f5ae10c9eafce750499d881669959e6a Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 20:54:19 +0100 Subject: [PATCH 07/14] P_Ticker: Fix tracking Attacking modes - SPB: The standard set-up has ATTACKING_TIME in addition to ATTACKING_SPB, so the _TIME check first prevented it from ever being ticked - SPB: Check for encoremode instead of ATTACKING_SPB, to cover Versus SPB as well - All Attacking modes: Check for != ATTACKING_NONE just in case we add other potential modeattacking configurations later --- src/p_tick.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 961375410..8d3187580 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -1005,20 +1005,23 @@ void P_Ticker(boolean run) } // Attacking mode playtime - if ((modeattacking & ATTACKING_TIME) != 0) + if (modeattacking != ATTACKING_NONE) { - gamedata->timeattackingtotaltime++; - if (mapheader) + if (encoremode) // ((modeattacking & ATTACKING_SPB) != 0) { - mapheader->records.timeattacktimeplayed++; + gamedata->spbattackingtotaltime++; + if (mapheader) + { + mapheader->records.spbattacktimeplayed++; + } } - } - else if ((modeattacking & ATTACKING_SPB) != 0) - { - gamedata->spbattackingtotaltime++; - if (mapheader) + else { - mapheader->records.spbattacktimeplayed++; + gamedata->timeattackingtotaltime++; + if (mapheader) + { + mapheader->records.timeattacktimeplayed++; + } } } From d0724c263a292188dee7bfa9704833d7c856e18f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 20:58:04 +0100 Subject: [PATCH 08/14] Bring roundsplayed_t into the highest common denominator header No indirect, hard-to-maintain magic number range in modetimeplayed arrays --- src/doomdef.h | 10 ++++++++++ src/doomstat.h | 4 ++-- src/m_cond.h | 9 --------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index f9f7b65f8..69faba1a0 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -590,6 +590,16 @@ UINT32 quickncasehash (const char *p, size_t n) return x; } +// m_cond, doomstat +typedef enum { + GDGT_RACE, + GDGT_BATTLE, + GDGT_PRISONS, + GDGT_SPECIAL, + GDGT_CUSTOM, + GDGT_MAX +} roundsplayed_t; + #ifndef __cplusplus #ifndef min // Double-Check with WATTCP-32's cdefs.h #define min(x, y) (((x) < (y)) ? (x) : (y)) diff --git a/src/doomstat.h b/src/doomstat.h index 6a481a1a3..20f0531ba 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -124,7 +124,7 @@ struct skinrecord_t UINT32 wins; UINT32 rounds; UINT32 timeplayed; - UINT32 modetimeplayed[5]; // no GDGT_MAX, m_cond.h is not included in here... it might need to be? + UINT32 modetimeplayed[GDGT_MAX]; UINT32 tumbletime; }; @@ -167,7 +167,7 @@ struct recorddata_t recordtimes_t spbattack; ///< Best times for SPB Attack UINT32 timeplayed; UINT32 netgametimeplayed; - UINT32 modetimeplayed[5]; + UINT32 modetimeplayed[GDGT_MAX]; UINT32 timeattacktimeplayed; UINT32 spbattacktimeplayed; UINT32 rounds; diff --git a/src/m_cond.h b/src/m_cond.h index fc2b64c5a..90c219c39 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -311,15 +311,6 @@ typedef enum { GDGONER_DONE, } gdgoner_t; -typedef enum { - GDGT_RACE, - GDGT_BATTLE, - GDGT_PRISONS, - GDGT_SPECIAL, - GDGT_CUSTOM, - GDGT_MAX -} roundsplayed_t; - struct candata_t { UINT16 col; From f1c9f54727b73533005aa9936d75eec08aea1f0c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 21:47:28 +0100 Subject: [PATCH 09/14] Count SPB Attack as a subset of Time Attack, not seperate --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index 8d3187580..d00b08549 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -1015,7 +1015,7 @@ void P_Ticker(boolean run) mapheader->records.spbattacktimeplayed++; } } - else + //else { gamedata->timeattackingtotaltime++; if (mapheader) From 825eda98611be72d63d3d50fcd66070b2f6ffc82 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 22:16:51 +0100 Subject: [PATCH 10/14] Statistics "Characters & Engine Classes" page: Show Wins/Rounds, not just Wins --- src/k_menudraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f3c9ead4e..be7d460f0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -8310,7 +8310,7 @@ static void M_DrawStatsChars(void) i = -1; V_DrawThinString(20, y - 10, highlightflags, "CHARACTER"); - V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 34, y - 10, highlightflags, "WINS"); + V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 34, y - 10, highlightflags, "WINS/ROUNDS"); while ((skin = statisticsmenu.maplist[++i]) < numskins) { @@ -8328,7 +8328,7 @@ static void M_DrawStatsChars(void) V_DrawThinString(24+32+2, y+3, 0, skins[skin].realname); - V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 30, y+3, 0, va("%d", skins[skin].records.wins)); + V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 30, y+3, 0, va("%d/%d", skins[skin].records.wins, skins[skin].records.rounds)); y += STATSSTEP; From 1f6038be659ee861ed1d52dc684e0ab85fc50f63 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 22:19:51 +0100 Subject: [PATCH 11/14] Add "Time Tracked" page to Statistics Ugly but releasable --- src/k_menu.h | 1 + src/k_menudraw.c | 122 ++++++++++++++++++++++++++++------ src/menus/extras-statistics.c | 3 + 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 47a92f58b..d4e214878 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1434,6 +1434,7 @@ typedef enum statisticspage_chars = 0, statisticspage_gp, statisticspage_maps, + statisticspage_time, statisticspage_max } statisticspage_t; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index be7d460f0..68c532f60 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -8474,10 +8474,103 @@ bottomarrow: #undef STATSSTEP +static void M_GetStatsTime(char *beststr, UINT32 totaltime) +{ + beststr[0] = 0; + + boolean showallsubsequent = false; + + UINT32 besttime = G_TicsToHours(totaltime); + if (besttime) + { + showallsubsequent = true; + if (besttime >= 24) + { + strcat(beststr, va("%u day%s, ", besttime/24, (besttime < 48 ? "" : "s"))); + besttime %= 24; + } + + strcat(beststr, va("%u hour%s, ", besttime, (besttime == 1 ? "" : "s"))); + } + besttime = G_TicsToMinutes(totaltime, false); + if (besttime || showallsubsequent) + { + showallsubsequent = true; + strcat(beststr, va("%u minute%s, ", besttime, (besttime == 1 ? "" : "s"))); + } + besttime = G_TicsToSeconds(totaltime); + strcat(beststr, va("%i second%s", besttime, (besttime == 1 ? "" : "s"))); +} + +static void M_DrawStatsTimeTracked(void) +{ + INT32 y = 70; + char beststr[256]; + + #define DISPLAYAMODE(str, besttime) \ + { \ + V_DrawThinString(24, y, 0, str); \ + M_GetStatsTime(beststr, besttime); \ + V_DrawRightAlignedThinString(BASEVIDWIDTH-24, y, 0, beststr); \ + y += 10; \ + } + + DISPLAYAMODE("Race Mode", gamedata->modeplaytime[GDGT_RACE]); + + if (gamedata->roundsplayed[GDGT_PRISONS]) + { + DISPLAYAMODE("Prison Break", gamedata->modeplaytime[GDGT_PRISONS]); + } + + DISPLAYAMODE("Battle Mode", gamedata->modeplaytime[GDGT_BATTLE]); + + if (gamedata->roundsplayed[GDGT_SPECIAL]) + { + DISPLAYAMODE("Special Mode", gamedata->modeplaytime[GDGT_SPECIAL]); + } + + if (gamedata->roundsplayed[GDGT_CUSTOM]) + { + DISPLAYAMODE("All Custom Modes", gamedata->modeplaytime[GDGT_CUSTOM]); + } + + if (M_SecretUnlocked(SECRET_ONLINE, true)) + { + y += 2; + + DISPLAYAMODE("Playing Online", gamedata->totalnetgametime); + } + + if (M_SecretUnlocked(SECRET_TIMEATTACK, true) + || M_SecretUnlocked(SECRET_PRISONBREAK, true) + || M_SecretUnlocked(SECRET_SPECIALATTACK, true)) + { + y += 2; + + DISPLAYAMODE("Time Attack Modes", gamedata->timeattackingtotaltime); + + if (M_SecretUnlocked(SECRET_SPBATTACK, true)) + { + DISPLAYAMODE(" (SPB Attack)", gamedata->spbattackingtotaltime); + } + } + + if (gamedata->totaltumbletime) + { + y += 2; + + DISPLAYAMODE("Tumbling through the air", gamedata->totaltumbletime); + } + + y += 2; + + DISPLAYAMODE("On Menus", gamedata->totalmenutime); + DISPLAYAMODE(" (staring at this screen)", gamedata->totaltimestaringatstatistics); +} + void M_DrawStatistics(void) { char beststr[256]; - tic_t besttime = 0; { const char *pagename = NULL; @@ -8487,7 +8580,6 @@ void M_DrawStatistics(void) switch (statisticsmenu.page) { - case statisticspage_gp: { pagename = gamedata->everseenspecial @@ -8511,6 +8603,13 @@ void M_DrawStatistics(void) break; } + case statisticspage_time: + { + pagename = "TIME TRACKED"; + M_DrawStatsTimeTracked(); + break; + } + default: break; } @@ -8528,26 +8627,9 @@ void M_DrawStatistics(void) 0, "\x1D"); // right arrow } - beststr[0] = 0; V_DrawThinString(20, 30, highlightflags, "Total Play Time:"); - besttime = G_TicsToHours(gamedata->totalplaytime); - if (besttime) - { - if (besttime >= 24) - { - strcat(beststr, va("%u day%s, ", besttime/24, (besttime < 48 ? "" : "s"))); - besttime %= 24; - } - strcat(beststr, va("%u hour%s, ", besttime, (besttime == 1 ? "" : "s"))); - } - besttime = G_TicsToMinutes(gamedata->totalplaytime, false); - if (besttime) - { - strcat(beststr, va("%u minute%s, ", besttime, (besttime == 1 ? "" : "s"))); - } - besttime = G_TicsToSeconds(gamedata->totalplaytime); - strcat(beststr, va("%i second%s", besttime, (besttime == 1 ? "" : "s"))); + M_GetStatsTime(beststr, gamedata->totalplaytime); V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 30, 0, beststr); beststr[0] = 0; diff --git a/src/menus/extras-statistics.c b/src/menus/extras-statistics.c index 005c6e43f..23df0ff26 100644 --- a/src/menus/extras-statistics.c +++ b/src/menus/extras-statistics.c @@ -255,7 +255,10 @@ static void M_StatisticsPageInit(void) } default: + { + statisticsmenu.maplist = NULL; break; + } } } From 96b784defd0584712691a4c17891b14c17048268 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 22:49:47 +0100 Subject: [PATCH 12/14] Convert menus/extras-statistics to cpp --- src/menus/CMakeLists.txt | 2 +- .../{extras-statistics.c => extras-statistics.cpp} | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) rename src/menus/{extras-statistics.c => extras-statistics.cpp} (92%) diff --git a/src/menus/CMakeLists.txt b/src/menus/CMakeLists.txt index be72a4996..80d3c261e 100644 --- a/src/menus/CMakeLists.txt +++ b/src/menus/CMakeLists.txt @@ -3,7 +3,7 @@ target_sources(SRB2SDL2 PRIVATE extras-addons.c extras-challenges.c extras-egg-tv.cpp - extras-statistics.c + extras-statistics.cpp extras-wrong.c main-1.c main-goner.cpp diff --git a/src/menus/extras-statistics.c b/src/menus/extras-statistics.cpp similarity index 92% rename from src/menus/extras-statistics.c rename to src/menus/extras-statistics.cpp index 23df0ff26..254165a09 100644 --- a/src/menus/extras-statistics.c +++ b/src/menus/extras-statistics.cpp @@ -68,7 +68,7 @@ static void M_StatisticsMaps(void) UINT16 i; boolean headerexists; - statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (nummapheaders+1 + numkartcupheaders), PU_STATIC, NULL); + statisticsmenu.maplist = static_cast(Z_Malloc(sizeof(UINT16) * (nummapheaders+1 + numkartcupheaders), PU_STATIC, NULL)); statisticsmenu.nummaps = 0; // Cups @@ -119,7 +119,7 @@ static void M_StatisticsChars(void) { UINT16 i; - statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (1 + numskins), PU_STATIC, NULL); + statisticsmenu.maplist = static_cast(Z_Malloc(sizeof(UINT16) * (1 + numskins), PU_STATIC, NULL)); statisticsmenu.nummaps = 0; UINT32 beststat = 0; @@ -208,7 +208,7 @@ static void M_StatisticsChars(void) static void M_StatisticsGP(void) { - statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (1 + numkartcupheaders), PU_STATIC, NULL); + statisticsmenu.maplist = static_cast(Z_Malloc(sizeof(UINT16) * (1 + numkartcupheaders), PU_STATIC, NULL)); statisticsmenu.nummaps = 0; cupheader_t *cup; @@ -305,15 +305,17 @@ boolean M_StatisticsInputs(INT32 ch) { M_StatisticsPageClear(); - statisticsmenu.page += - statisticspage_max + int newpage = static_cast(statisticsmenu.page) + + static_cast(statisticspage_max) + ( (menucmd[pid].dpad_lr > 0) ? 1 : -1 ); - statisticsmenu.page %= statisticspage_max; + newpage %= static_cast(statisticspage_max); + + statisticsmenu.page = static_cast(newpage); M_StatisticsPageInit(); From cea9a80b2ac161ea58d91615d79986c18f40bde2 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 22:52:59 +0100 Subject: [PATCH 13/14] M_StatisticsChars: Sort for descending rounds completed --- src/menus/extras-statistics.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/menus/extras-statistics.cpp b/src/menus/extras-statistics.cpp index 254165a09..75c6821b1 100644 --- a/src/menus/extras-statistics.cpp +++ b/src/menus/extras-statistics.cpp @@ -166,6 +166,19 @@ static void M_StatisticsChars(void) statisticsmenu.maplist[statisticsmenu.nummaps] = MAXSKINS; + std::sort( + statisticsmenu.maplist, + statisticsmenu.maplist + statisticsmenu.nummaps, + [](UINT16 a, UINT16 b) { + if (skins[a].records.rounds > skins[b].records.rounds) + return true; + if (skins[a].records.rounds != skins[b].records.rounds) + return false; + // Stable for skin ID + return (a < b); + } + ); + statisticsmenu.location = 0; statisticsmenu.maxscroll = statisticsmenu.nummaps - 6; From 4a937936b77dd1fff4b3ce0eb010ef6aea26a302 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 6 Apr 2024 22:54:08 +0100 Subject: [PATCH 14/14] load_ng_gamedata: In DEVELOP, if a skin's wins exceeds a skin's rounds, set rounds to wins Basically just for testing this branch, at this rate --- src/g_gamedata.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index bc9f5a396..e080d5c5e 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -555,6 +555,13 @@ void srb2::load_ng_gamedata() dummyrecord.wins = skinpair.second.records.wins; dummyrecord.rounds = skinpair.second.records.rounds; + +#ifdef DEVELOP + // Only good for testing, not for active play... cheaters never prosper! + if (dummyrecord.rounds < dummyrecord.wins) + dummyrecord.rounds = dummyrecord.wins; +#endif + dummyrecord.timeplayed = skinpair.second.records.time.total; dummyrecord.modetimeplayed[GDGT_RACE] = skinpair.second.records.time.race; dummyrecord.modetimeplayed[GDGT_BATTLE] = skinpair.second.records.time.battle;