diff --git a/src/Sourcefile b/src/Sourcefile index 17a687d5d..dbea241ca 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -129,3 +129,4 @@ k_brightmap.c k_director.c k_follower.c k_mapuser.c +k_stats.c diff --git a/src/doomstat.h b/src/doomstat.h index e310eb549..1006d7ff6 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -557,9 +557,6 @@ struct tolinfo_t extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES]; extern UINT32 lastcustomtol; -extern tic_t totalplaytime; -extern UINT32 matchesplayed; - extern UINT8 stagefailed; // Emeralds stored as bits to throw savegame hackers off. diff --git a/src/g_game.c b/src/g_game.c index 740938394..e349d7844 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -53,6 +53,7 @@ // SRB2kart #include "k_kart.h" +#include "k_stats.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" #include "k_color.h" @@ -196,8 +197,6 @@ UINT32 tokenlist; // List of tokens collected boolean gottoken; // Did you get a token? Used for end of act INT32 tokenbits; // Used for setting token bits -tic_t totalplaytime; -UINT32 matchesplayed; // SRB2Kart boolean gamedataloaded = false; // Temporary holding place for nights data for the current map @@ -2198,7 +2197,7 @@ static inline void G_PlayerFinishLevel(INT32 player) { if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified) { - matchesplayed++; + kartstats.matchesplayed++; if (M_UpdateUnlockablesAndExtraEmblems()) S_StartSound(NULL, sfx_ncitem); G_SaveGameData(); @@ -4044,6 +4043,7 @@ static void G_DoCompleted(void) if (!demo.playback) { nextmap = G_GetNextMap(true); + K_StatRound(); // Remember last map for when you come out of the special stage. if (!spec) @@ -4301,9 +4301,6 @@ void G_LoadGameData(void) G_ClearRecords(); // main and nights records M_ClearSecrets(); // emblems, unlocks, maps visited, etc - totalplaytime = 0; // total play time (separate from all) - matchesplayed = 0; // SRB2Kart: matches played & finished - for (i = 0; i < PWRLV_NUMTYPES; i++) // SRB2Kart: online rank system vspowerlevel[i] = PWRLVRECORD_START; @@ -4334,9 +4331,8 @@ void G_LoadGameData(void) P_SaveBufferFree(&save); I_Error("Game data is not for SRB2Kart v2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder); } - - totalplaytime = READUINT32(save.p); - matchesplayed = READUINT32(save.p); + + K_ReadStats(&save, true); for (i = 0; i < PWRLV_NUMTYPES; i++) { @@ -4486,8 +4482,7 @@ void G_SaveGameData(void) // Version test WRITEUINT32(save.p, GD_VERSIONCHECK); // 4 - WRITEUINT32(save.p, totalplaytime); // 4 - WRITEUINT32(save.p, matchesplayed); // 4 + K_WriteStats(&save, true); for (i = 0; i < PWRLV_NUMTYPES; i++) WRITEUINT16(save.p, vspowerlevel[i]); diff --git a/src/k_collide.c b/src/k_collide.c index fbdf2ed69..dff678846 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -2,6 +2,7 @@ /// \brief SRB2Kart item collision hooks #include "k_collide.h" +#include "k_stats.h" #include "doomstat.h" #include "doomtype.h" #include "p_mobj.h" @@ -800,6 +801,8 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) if (t2->player->flashing > 0) return true; + K_StatPlayerSink(t1->player, P_MobjWasRemoved(t2->target) ? NULL : t2->target->player); + S_StartSound(NULL, sfx_cgot); //let all players hear it. HU_SetCEchoFlags(0); diff --git a/src/k_kart.c b/src/k_kart.c index e5cd00e1f..99b379b35 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -38,6 +38,8 @@ #include "m_cheat.h" // objectplacing #include "p_spec.h" + +#include "k_stats.h" #include "k_waypoint.h" #include "k_bot.h" #include "k_hud.h" @@ -3620,6 +3622,7 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ S_StartSound(player->mo, sfx_slip); } + K_StatPlayerHit(player, source ? source->player : NULL); player->spinouttimer = (3*TICRATE/2)+2; P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } diff --git a/src/k_stats.c b/src/k_stats.c new file mode 100644 index 000000000..4c126f700 --- /dev/null +++ b/src/k_stats.c @@ -0,0 +1,146 @@ +#include "k_stats.h" +#include "doomstat.h" +#include "g_game.h" +#include "byteptr.h" + +kartstats_t kartstats = {0}; + +void K_StatTicker(void) +{ + if (demo.playback) + return; + + kartstats.totalplaytime++; + + if (netgame) + kartstats.onlineplaytime++; + else if (modeattacking) + kartstats.raplaytime++; + + if (gametype == GT_RACE) + kartstats.raceplaytime++; + else if (gametype == GT_BATTLE) + kartstats.battleplaytime++; + + // Should this also track splitscreen players? + if (!players[consoleplayer].spectator) + { + player_t *p = &players[consoleplayer]; + + if (p->kartstuff[k_position] == spbplace) + kartstats.spbtargettime++; + + if (max(p->kartstuff[k_spinouttimer], p->kartstuff[k_wipeoutslow]) > 0) + kartstats.spinouttime++; + } +} + +void K_StatPlayerHit(player_t *victim, player_t *source) +{ + if (demo.playback) + return; + + if (victim == &players[consoleplayer]) + { + if (victim == source) + kartstats.selfhits++; + } + else if (source == &players[consoleplayer]) + kartstats.hits++; +} + +void K_StatPlayerSink(player_t *victim, player_t *source) +{ + if (demo.playback) + return; + + if (victim == &players[consoleplayer]) + kartstats.sinked++; + else if (source == &players[consoleplayer]) + kartstats.sinks++; +} + +void K_StatRound(void) +{ + if (demo.playback) + return; + + int numplayers = 0; + + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && !players[i].spectator) + ++numplayers; + } + + if (numplayers > 1) + { + if (players[consoleplayer].kartstuff[k_position] == 1) + kartstats.totalwins++; + else if (players[consoleplayer].kartstuff[k_position] <= 3) // Should this check if there are more than 3 players in game? + kartstats.totalpodium++; + } +} + +void K_EraseStats(void) +{ + // The only field we want to remember + boolean vanilla = kartstats.vanilla; + + memset(&kartstats, 0, sizeof(kartstats_t)); + + kartstats.vanilla = vanilla; +} + +void K_ReadStats(savebuffer_t *save, boolean vanilla) +{ + memset(&kartstats, 0, sizeof(kartstats_t)); + + kartstats.vanilla = vanilla; + + kartstats.totalplaytime = READUINT32(save->p); + kartstats.matchesplayed = READUINT32(save->p); + + // Vanilla only has those 2 + if (vanilla) + return; + + // So many similar-looking read's... scary... + kartstats.raplaytime = READUINT32(save->p); + kartstats.onlineplaytime = READUINT32(save->p); + kartstats.raceplaytime = READUINT32(save->p); + kartstats.battleplaytime = READUINT32(save->p); + kartstats.spbtargettime = READUINT32(save->p); + kartstats.spinouttime = READUINT32(save->p); + kartstats.totalwins = READUINT32(save->p); + kartstats.totalpodium = READUINT32(save->p); + kartstats.hits = READUINT32(save->p); + kartstats.selfhits = READUINT32(save->p); + kartstats.sinks = READUINT32(save->p); + kartstats.sinked = READUINT32(save->p); + kartstats.respawns = READUINT32(save->p); +} + +void K_WriteStats(savebuffer_t *save, boolean vanilla) +{ + WRITEUINT32(save->p, kartstats.totalplaytime); + WRITEUINT32(save->p, kartstats.matchesplayed); + + // Vanilla only has those 2 + if (vanilla) + return; + + WRITEUINT32(save->p, kartstats.raplaytime); + WRITEUINT32(save->p, kartstats.onlineplaytime); + WRITEUINT32(save->p, kartstats.raceplaytime); + WRITEUINT32(save->p, kartstats.battleplaytime); + WRITEUINT32(save->p, kartstats.spbtargettime); + WRITEUINT32(save->p, kartstats.spinouttime); + WRITEUINT32(save->p, kartstats.totalwins); + WRITEUINT32(save->p, kartstats.totalpodium); + WRITEUINT32(save->p, kartstats.hits); + WRITEUINT32(save->p, kartstats.selfhits); + WRITEUINT32(save->p, kartstats.sinks); + WRITEUINT32(save->p, kartstats.sinked); + WRITEUINT32(save->p, kartstats.respawns); +} diff --git a/src/k_stats.h b/src/k_stats.h new file mode 100644 index 000000000..87a173653 --- /dev/null +++ b/src/k_stats.h @@ -0,0 +1,56 @@ +#ifndef __K_STATS__ +#define __K_STATS__ + +#include "doomtype.h" +#include "d_player.h" +#include "p_saveg.h" + +typedef struct kartstats_s { + // Remember if stats we loaded are vanilla ones or not + boolean vanilla; + + // Vanilla + tic_t totalplaytime; + UINT32 matchesplayed; + + tic_t raplaytime; + tic_t onlineplaytime; + tic_t raceplaytime; + tic_t battleplaytime; + tic_t spbtargettime; + tic_t spinouttime; + UINT32 totalwins; // 1st place + UINT32 totalpodium; // 2nd and 3rd place, *but not 1st* + UINT32 hits; + UINT32 selfhits; + UINT32 sinks; // Times hitting *others* by kitchen sink + UINT32 sinked; // Times *being hit* by kitchen sink + UINT32 respawns; +} kartstats_t; + +extern kartstats_t kartstats; + +// Note: All stat-tracking functions check for demo.playback and early return if its true + +// Update global stats such as total play time, etc +void K_StatTicker(void); + +// Update hit-related stats (PlayerSpin, PlayerSquish, PlayerExplode) +void K_StatPlayerHit(player_t *victim, player_t *source); + +// Separate from stuff above +void K_StatPlayerSink(player_t *victim, player_t *source); + +// Update round-related stats (such as matchesplayed, totalwins, etc) +void K_StatRound(void); + +void K_EraseStats(void); + +// If vanilla is true, only read vanilla-supported fields. Everything else is initialized to 0 +void K_ReadStats(savebuffer_t *save, boolean vanilla); + +// Same as above, except it just doesn't write non-vanilla fields if vanilla is true +void K_WriteStats(savebuffer_t *save, boolean vanilla); + + +#endif diff --git a/src/m_cond.c b/src/m_cond.c index 64b580191..205087653 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -21,6 +21,7 @@ #include "r_skins.h" // numskins #include "r_draw.h" // R_GetColorByName #include "k_pwrlv.h" +#include "k_stats.h" // Map triggers for linedef executors // 32 triggers, one bit each @@ -174,9 +175,9 @@ UINT8 M_CheckCondition(condition_t *cn) switch (cn->type) { case UC_PLAYTIME: // Requires total playing time >= x - return (totalplaytime >= (unsigned)cn->requirement); + return (kartstats.totalplaytime >= (unsigned)cn->requirement); case UC_MATCHESPLAYED: // Requires any level completed >= x times - return (matchesplayed >= (unsigned)cn->requirement); + return (kartstats.matchesplayed >= (unsigned)cn->requirement); case UC_POWERLEVEL: // Requires power level >= x on a certain gametype return (vspowerlevel[cn->extrainfo1] >= (unsigned)cn->requirement); case UC_GAMECLEAR: // Requires game beaten >= x times diff --git a/src/m_menu.c b/src/m_menu.c index 7597dee78..70933cb56 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -64,6 +64,7 @@ #include "k_hud.h" // SRB2kart #include "k_kart.h" // KartItemCVars #include "k_pwrlv.h" +#include "k_stats.h" // SRB2kart #include "d_player.h" // KITEM_ constants #include "k_color.h" #include "k_grandprix.h" @@ -7682,12 +7683,11 @@ static void M_DrawLevelStats(void) V_DrawString(20, 24, highlightflags, "Total Play Time:"); V_DrawCenteredString(BASEVIDWIDTH/2, 32, 0, va("%i hours, %i minutes, %i seconds", - G_TicsToHours(totalplaytime), - G_TicsToMinutes(totalplaytime, false), - G_TicsToSeconds(totalplaytime))); - + G_TicsToHours(kartstats.totalplaytime), + G_TicsToMinutes(kartstats.totalplaytime, false), + G_TicsToSeconds(kartstats.totalplaytime))); V_DrawString(20, 42, highlightflags, "Total Matches:"); - V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, 0, va("%i played", matchesplayed)); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, 0, va("%i played", kartstats.matchesplayed)); V_DrawString(20, 52, highlightflags, "Online Power Level:"); V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE])); @@ -10249,8 +10249,7 @@ static void M_EraseDataResponse(INT32 ch) if (erasecontext == 2) { // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets - totalplaytime = 0; - matchesplayed = 0; + K_EraseStats(); for (i = 0; i < PWRLV_NUMTYPES; i++) vspowerlevel[i] = PWRLVRECORD_START; F_StartIntro(); diff --git a/src/m_random.c b/src/m_random.c index 481fdb72b..fa4a00f63 100644 --- a/src/m_random.c +++ b/src/m_random.c @@ -14,7 +14,7 @@ #include "doomdef.h" #include "doomtype.h" -#include "doomstat.h" // totalplaytime +#include "k_stats.h" // kartstats.totalplaytime #include "m_random.h" #include "m_fixed.h" @@ -252,5 +252,5 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed) */ UINT32 M_RandomizedSeed(void) { - return ((totalplaytime & 0xFFFF) << 16)|M_RandomFixed(); + return ((kartstats.totalplaytime & 0xFFFF) << 16)|M_RandomFixed(); } diff --git a/src/p_map.c b/src/p_map.c index f65e23140..4ea3e6b12 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -25,7 +25,7 @@ #include "r_sky.h" #include "s_sound.h" #include "w_wad.h" - +#include "k_stats.h" #include "k_kart.h" // SRB2kart 011617 #include "k_collide.h" #include "hu_stuff.h" // SRB2kart diff --git a/src/p_tick.c b/src/p_tick.c index 691a717de..61d754dd7 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -22,6 +22,7 @@ #include "m_random.h" #include "lua_script.h" #include "lua_hook.h" +#include "k_stats.h" #include "m_perfstats.h" #include "i_system.h" // I_GetPreciseTime #include "i_video.h" @@ -746,8 +747,7 @@ void P_Ticker(boolean run) } // Keep track of how long they've been playing! - if (!demo.playback) // Don't increment if a demo is playing. - totalplaytime++; + K_StatTicker(); // formality so kitemcap gets updated properly each frame. P_RunKartItems();