blankart/src/k_stats.c
2025-10-18 22:20:52 +02:00

233 lines
6.3 KiB
C

#include "k_stats.h"
#include "doomstat.h"
#include "g_game.h"
#include "byteptr.h"
#include "z_zone.h"
kartstats_t kartstats = {0};
void K_StatTicker(void)
{
if (demo.playback)
return;
maprecord_t *record = G_AllocateMapRecord(G_BuildMapName(gamemap));
record->playtime++;
kartstats.totalplaytime++;
if (netgame)
{
kartstats.onlineplaytime++;
}
else if (modeattacking)
{
maprecordpreset_t *preset = G_AllocateMapRecordPreset(record, currentrecordpreset, currentrecordpresetversion);
preset->playtime++;
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->position == spbplace)
kartstats.spbtargettime++;
if (p->flipovertimer > 0)
kartstats.spinouttime++;
else if (max(p->spinouttimer, p->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;
maprecord_t *record = G_AllocateMapRecord(G_BuildMapName(gamemap));
record->roundsplayed++;
kartstats.matchesplayed++;
int numplayers = 0;
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && !players[i].spectator)
++numplayers;
}
if (numplayers > 1 && !players[consoleplayer].spectator)
{
if (players[consoleplayer].position == 1)
{
record->roundswon++;
kartstats.totalwins++;
}
else if (players[consoleplayer].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;
if (kartstats.copy)
Z_Free(kartstats.copy);
memset(&kartstats, 0, sizeof(kartstats_t));
kartstats.vanilla = vanilla;
}
static void K_ReadStatsCustom(savebuffer_t *save)
{
kartstats.size = READUINT32(save->p);
kartstats.version = READUINT32(save->p);
// 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);
// If kartstats gets updated, uncomment this and read next fields after this early return. Do same on next updates,
// this way even data is read on different versions, it doesn't get corrupted (as long as fields aren't removed, which shouldn't happen)
//if (kartstats.version < 2)
// return;
//
//kartstats.somenewfield = READUINT32(save->p);
}
void K_ReadStats(savebuffer_t *save, boolean vanilla)
{
// Basically, free old kartstats.copy if needed and memset kartstats with zeros
K_EraseStats();
kartstats.vanilla = vanilla;
kartstats.totalplaytime = READUINT32(save->p);
kartstats.matchesplayed = READUINT32(save->p);
// Vanilla only has those 2
if (vanilla)
return;
// Save where block of custom stats starts
UINT8 *customblockstart = save->p;
K_ReadStatsCustom(save);
// Make a copy of entire custom stats block, so unread values will still be written back
kartstats.copy = Z_Malloc(kartstats.size, PU_STATIC, NULL);
memcpy(kartstats.copy, customblockstart, kartstats.size);
// If there were extra fields we couldn't read, skip them
save->p = customblockstart + kartstats.size;
}
static void K_WriteStatsCustom(savebuffer_t *save)
{
// Update, if needed
kartstats.version = max(kartstats.version, KARTSTATSVERSION);
WRITEUINT32(save->p, kartstats.size); // Note: if we will end up writing more that stored in there, this field in save file would be updated
WRITEUINT32(save->p, kartstats.version);
// Version 1
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);
// No need for early returns, but please mark each new block of write's with version it corresponds to :3
// Version 2
//WRITEUINT32(save->p, kartstats.somenewfield);
}
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;
// Now need to be careful... We save start of custom stats block
UINT8 *customblockstart = save->p;
// Write everything we know how to write
K_WriteStatsCustom(save);
// Calculate size of data that we wrote
UINT32 size = (UINT32)(save->p - customblockstart);
// Now, if we wrote less data than save originally had, that means our copy that we kept in K_ReadStats
// has stats from newer version that we need to append
if (size < kartstats.size)
{
I_Assert(kartstats.copy != NULL); // If kartstats.copy is null, kartstats.size **always** should be 0, this can only happen if we create new save
WRITEMEM(save->p, kartstats.copy + size, kartstats.size - size);
}
else if (size > kartstats.size) // No need to do anything if we wrote exactly same amount...
{
// ...but, if we wrote more, we need to update size field in header. **It's always located at start of block**
// So we can just:
WRITEUINT32(customblockstart, size);
}
}