Kartstats #161
No files matched your search
@@ -173,6 +173,7 @@ set(SRB2_CORE_GAME_SOURCES
|
||||
p_user.c
|
||||
k_director.c
|
||||
k_kart.c
|
||||
k_stats.c
|
||||
i_time.c
|
||||
|
||||
p_local.h
|
||||
@@ -187,6 +188,7 @@ set(SRB2_CORE_GAME_SOURCES
|
||||
p_tick.h
|
||||
k_director.h
|
||||
k_kart.h
|
||||
k_stats.h
|
||||
)
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
|
||||
@@ -506,6 +506,7 @@ OBJS:=$(i_main_o) \
|
||||
$(OBJDIR)/st_stuff.o \
|
||||
$(OBJDIR)/k_director.o \
|
||||
$(OBJDIR)/k_kart.o \
|
||||
$(OBJDIR)/k_stats.o \
|
||||
$(OBJDIR)/m_aatree.o \
|
||||
$(OBJDIR)/m_anigif.o \
|
||||
$(OBJDIR)/m_argv.o \
|
||||
|
||||
+2
-6
@@ -5801,12 +5801,8 @@ static void Fishcake_OnChange(void)
|
||||
*/
|
||||
static void Command_Isgamemodified_f(void)
|
||||
{
|
||||
if (majormods)
|
||||
CONS_Printf("The game has been modified with major addons, so you cannot play Record Attack.\n");
|
||||
else if (savemoddata)
|
||||
CONS_Printf("The game has been modified with an addon with its own save data, so you can play Record Attack and earn medals.\n");
|
||||
else if (modifiedgame)
|
||||
CONS_Printf("The game has been modified with only minor addons. You can play Record Attack, earn medals and unlock extras.\n");
|
||||
if (majormods || modifiedgame)
|
||||
CONS_Printf("The game has been modified, Record Attack data will be saved to a seperate savegame.\n");
|
||||
else
|
||||
CONS_Printf("The game has not been modified. You can play Record Attack, earn medals and unlock extras.\n");
|
||||
}
|
||||
|
||||
+5
-5
@@ -1756,7 +1756,7 @@ static void readsound(MYFILE *f, INT32 num, const char *savesfxnames[])
|
||||
* \sa readmaincfg()
|
||||
* \author Graue <graue@oceanbase.org>
|
||||
*/
|
||||
static boolean GoodDataFileName(const char *s)
|
||||
/*static boolean GoodDataFileName(const char *s)
|
||||
{
|
||||
const char *p;
|
||||
const char *tail = ".dat";
|
||||
@@ -1776,7 +1776,7 @@ static boolean GoodDataFileName(const char *s)
|
||||
if (fasticmp(s, "online.dat")) return false; // SRB2Kart online replay folder
|
||||
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
static void reademblemdata(MYFILE *f, INT32 num)
|
||||
{
|
||||
@@ -2554,10 +2554,10 @@ static void readmaincfg(MYFILE *f)
|
||||
{
|
||||
maxXtraLife = (UINT8)get_number(word2);
|
||||
}
|
||||
|
||||
else if (fastcmp(word, "GAMEDATA"))
|
||||
{
|
||||
size_t filenamelen;
|
||||
// just ignore it but dont throw a warning
|
||||
/*size_t filenamelen;
|
||||
|
||||
// Check the data filename so that mods
|
||||
// can't write arbitrary files.
|
||||
@@ -2581,7 +2581,7 @@ static void readmaincfg(MYFILE *f)
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
|
||||
refreshdirmenu |= REFRESHDIR_GAMEDATA;
|
||||
refreshdirmenu |= REFRESHDIR_GAMEDATA;*/
|
||||
}
|
||||
else if (fastcmp(word, "RESETDATA"))
|
||||
{
|
||||
|
||||
@@ -338,9 +338,6 @@ enum GameType // SRB2Kart
|
||||
// String names for gametypes
|
||||
extern const char *Gametype_Names[NUMGAMETYPES];
|
||||
|
||||
extern tic_t totalplaytime;
|
||||
extern UINT32 matchesplayed;
|
||||
|
||||
extern UINT8 stagefailed;
|
||||
|
||||
// Emeralds stored as bits to throw savegame hackers off.
|
||||
|
||||
+61
-14
@@ -52,6 +52,7 @@
|
||||
#include "md5.h" // demo checksums
|
||||
#include "k_director.h" // SRB2kart
|
||||
#include "k_kart.h" // SRB2kart
|
||||
#include "k_stats.h" // SRB2kart
|
||||
#include "r_fps.h" // frame interpolation/uncapped
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
@@ -175,8 +176,6 @@ INT32 tokenbits; // Used for setting token bits
|
||||
// Old Special Stage
|
||||
INT32 sstimer; // Time allotted in the special stage
|
||||
|
||||
tic_t totalplaytime;
|
||||
UINT32 matchesplayed; // SRB2Kart
|
||||
boolean gamedataloaded = false;
|
||||
|
||||
// Time attack data for levels
|
||||
@@ -600,6 +599,36 @@ tic_t G_GetBestTime(INT16 map)
|
||||
return mainrecords[map-1]->time;
|
||||
}
|
||||
|
||||
// kinda hacky way to do this, but this sets the game to use a seperate savefile if you have addons loaded
|
||||
static void G_SetSaveGameModified(void)
|
||||
{
|
||||
size_t filenamelen;
|
||||
|
||||
if (savemoddata)
|
||||
return;
|
||||
|
||||
// save vanilla data just to be sure
|
||||
G_SaveGameData(true);
|
||||
|
||||
savemoddata = true;
|
||||
|
||||
strlcpy(gamedatafilename, "modkartdata.dat", sizeof (gamedatafilename));
|
||||
strlwr(gamedatafilename);
|
||||
|
||||
// Also save a time attack folder
|
||||
filenamelen = strlen(gamedatafilename)-4; // Strip off the extension
|
||||
filenamelen = min(filenamelen, sizeof (timeattackfolder));
|
||||
memcpy(timeattackfolder, gamedatafilename, filenamelen);
|
||||
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
|
||||
|
||||
strcpy(savegamename, timeattackfolder);
|
||||
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
|
||||
G_LoadGameData();
|
||||
}
|
||||
|
||||
// for consistency among messages: this modifies the game and removes savemoddata.
|
||||
void G_SetGameModified(boolean silent, boolean major)
|
||||
{
|
||||
@@ -614,8 +643,11 @@ void G_SetGameModified(boolean silent, boolean major)
|
||||
//savemoddata = false; -- there is literally no reason to do this anymore.
|
||||
majormods = true;
|
||||
|
||||
// should this only be done when you load a "major" gameplay modifieng addon?
|
||||
G_SetSaveGameModified();
|
||||
|
||||
if (!silent)
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to play Record Attack.\n"));
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Record Attack data will be saved to a seperate save file.\n"));
|
||||
|
||||
// If in record attack recording, cancel it.
|
||||
if (modeattacking)
|
||||
@@ -2011,7 +2043,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(true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
G_SaveGameData(true);
|
||||
@@ -2684,6 +2716,9 @@ void G_DoReborn(INT32 playernum)
|
||||
G_SpawnPlayer(playernum, starpost);
|
||||
if (oldmo)
|
||||
G_ChangePlayerReferences(oldmo, players[playernum].mo);
|
||||
|
||||
if (!demo.playback && playernum == consoleplayer)
|
||||
kartstats.respawns++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3113,6 +3148,8 @@ static void G_DoCompleted(void)
|
||||
if (metalrecording)
|
||||
G_StopMetalRecording();
|
||||
|
||||
K_StatRound();
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
{
|
||||
@@ -3482,8 +3519,7 @@ void G_LoadGameData(void)
|
||||
// to new gamedata
|
||||
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
|
||||
K_EraseStats(); // stats
|
||||
|
||||
if (M_CheckParm("-nodata"))
|
||||
return; // Don't load.
|
||||
@@ -3512,8 +3548,8 @@ void G_LoadGameData(void)
|
||||
I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
|
||||
}
|
||||
|
||||
totalplaytime = READUINT32(save.p);
|
||||
matchesplayed = READUINT32(save.p);
|
||||
// well no clue but dont think it would like reading garbage from vanilla files
|
||||
K_ReadStats(&save, !savemoddata);
|
||||
|
||||
modded = READUINT8(save.p);
|
||||
|
||||
@@ -3606,6 +3642,8 @@ void G_SaveGameData(boolean force)
|
||||
INT32 i, j;
|
||||
UINT8 btemp;
|
||||
savebuffer_t save;
|
||||
(void)force;
|
||||
char backupfile[MAX_WADPATH+4];
|
||||
|
||||
if (!gamedataloaded)
|
||||
return; // If never loaded (-nodata), don't save
|
||||
@@ -3617,18 +3655,27 @@ void G_SaveGameData(boolean force)
|
||||
return;
|
||||
}
|
||||
|
||||
if (majormods && !force)
|
||||
// Create backup of the save data
|
||||
snprintf(backupfile, sizeof(backupfile), "%s.bak", gamedatafilename);
|
||||
backupfile[sizeof(backupfile) - 1] = '\0';
|
||||
|
||||
FILE *gamedata = fopen(gamedatafilename, "r");
|
||||
|
||||
if (gamedata != NULL)
|
||||
{
|
||||
free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
return;
|
||||
fclose(gamedata);
|
||||
|
||||
if (!FIL_CopyFile(gamedatafilename, backupfile))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING,"Failed to create a backup of save data. Will not attempt to write to save data\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Version test
|
||||
WRITEUINT32(save.p, 0xFCAFE211);
|
||||
|
||||
WRITEUINT32(save.p, totalplaytime);
|
||||
WRITEUINT32(save.p, matchesplayed);
|
||||
K_WriteStats(&save, !savemoddata);
|
||||
|
||||
btemp = (UINT8)(savemoddata); // what used to be here was profoundly dunderheaded
|
||||
WRITEUINT8(save.p, btemp);
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "m_cond.h"
|
||||
#include "k_director.h"
|
||||
#include "k_kart.h"
|
||||
#include "k_stats.h"
|
||||
#include "f_finale.h"
|
||||
#include "lua_hud.h" // For Lua hud checks
|
||||
#include "lua_hook.h" // For MobjDamage and ShouldDamage
|
||||
@@ -2518,6 +2519,8 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto
|
||||
if (LUA_HookPlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case.
|
||||
return;
|
||||
|
||||
K_StatPlayerHit(player, source ? source->player : NULL);
|
||||
|
||||
if (source && source != player->mo && source->player)
|
||||
K_PlayHitEmSound(source, player->mo);
|
||||
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
#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;
|
||||
|
||||
kartstats.totalplaytime++;
|
||||
|
||||
if (netgame)
|
||||
kartstats.onlineplaytime++;
|
||||
else if (modeattacking)
|
||||
kartstats.raplaytime++;
|
||||
|
||||
if (G_RaceGametype())
|
||||
kartstats.raceplaytime++;
|
||||
else
|
||||
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 && !players[consoleplayer].spectator)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef __K_STATS__
|
||||
#define __K_STATS__
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "d_player.h"
|
||||
#include "p_saveg.h"
|
||||
|
||||
#define KARTSTATSVERSION 1
|
||||
|
||||
typedef struct kartstats_s {
|
||||
// Remember if stats we loaded are vanilla ones or not
|
||||
boolean vanilla;
|
||||
|
||||
// Vanilla
|
||||
tic_t totalplaytime;
|
||||
UINT32 matchesplayed;
|
||||
|
||||
// Custom data, fields go in same order as they are written into game save
|
||||
|
||||
// IMPORTANT: size **must be first**, so K_WriteStats can overwrite it (not that we're gonna change order, but just in case)
|
||||
UINT32 size; // Size required for our stats. If older version reads save from newer one, it will just skip unread fields, avoiding reading corrupted values
|
||||
UINT32 version; // Version, so we know which fields we actually can read
|
||||
|
||||
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;
|
||||
|
||||
UINT8 *copy; // Copy of stats block that has been read from file. In case we're older version that reads newer save, we will keep values that we couldn't read
|
||||
} 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
|
||||
+4
-5
@@ -21,6 +21,7 @@
|
||||
#include "r_things.h" // numskins
|
||||
//#include "r_draw.h" // R_GetColorByName
|
||||
#include "k_kart.h" // K_GetKartColorByName
|
||||
#include "k_stats.h"
|
||||
|
||||
// Map triggers for linedef executors
|
||||
// 32 triggers, one bit each
|
||||
@@ -286,9 +287,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_GAMECLEAR: // Requires game beaten >= x times
|
||||
return (timesBeaten >= (unsigned)cn->requirement);
|
||||
case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times
|
||||
@@ -384,9 +385,7 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(boolean force)
|
||||
INT32 i;
|
||||
char cechoText[992] = "";
|
||||
UINT8 cechoLines = 0;
|
||||
|
||||
if (majormods && !force) // SRB2Kart: for enabling unlocks online in modified servers
|
||||
return false;
|
||||
(void)force;
|
||||
|
||||
M_CheckUnlockConditions();
|
||||
|
||||
|
||||
+156
-57
@@ -70,6 +70,7 @@
|
||||
#include "st_stuff.h"
|
||||
#include "i_sound.h"
|
||||
#include "k_kart.h" // SRB2kart
|
||||
#include "k_stats.h" // SRB2kart
|
||||
#include "d_player.h" // KITEM_ constants
|
||||
|
||||
#include "i_joy.h" // for joystick menu controls
|
||||
@@ -474,6 +475,12 @@ static void Dummystaff_OnChange(void);
|
||||
// crap to force hud to show when in saturns hud options
|
||||
boolean forceshowhud = false;
|
||||
|
||||
// smol text indicating if game is modified
|
||||
// so ppl dont wonder where their ra times went and stuff
|
||||
#define SHOWMODDEDGAME \
|
||||
if (savemoddata) \
|
||||
V_DrawThinString(0, 0, V_REDMAP|V_SNAPTOTOP|V_SNAPTOLEFT|V_TRANSLUCENT|V_ALLOWLOWERCASE, ("Modified Game"));
|
||||
|
||||
// ==========================================================================
|
||||
// CONSOLE VARIABLES AND THEIR POSSIBLE VALUES GO HERE.
|
||||
// ==========================================================================
|
||||
@@ -4099,17 +4106,6 @@ boolean M_Responder(event_t *ev)
|
||||
|
||||
if (routine)
|
||||
{
|
||||
if (((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_CALL
|
||||
|| (currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SUBMENU)
|
||||
&& (currentMenu->menuitems[itemOn].status & IT_CALLTYPE))
|
||||
{
|
||||
if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods)
|
||||
{
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
M_StartMessage(M_GetText("This cannot be done with complex addons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
switch (currentMenu->menuitems[itemOn].status & IT_TYPE)
|
||||
{
|
||||
@@ -6193,7 +6189,7 @@ static boolean M_AddonsRefresh(void)
|
||||
else if (majormods && !prevmajormods)
|
||||
{
|
||||
S_StartSound(NULL, sfx_s221);
|
||||
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
|
||||
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack data will be saved to a seperate save file.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
|
||||
prevmajormods = majormods;
|
||||
}
|
||||
|
||||
@@ -8615,9 +8611,28 @@ static void M_Credits(INT32 choice)
|
||||
// STATISTICS MENU
|
||||
// ===============
|
||||
|
||||
static void M_DrawStatsMaps(void);
|
||||
static void M_DrawStatsPlaytime(void);
|
||||
static void M_DrawStatsExtra(void); // dunno how to name this one
|
||||
|
||||
static INT32 statsLocation;
|
||||
static INT32 statsMax;
|
||||
static INT16 statsMapList[NUMMAPS+1];
|
||||
static UINT8 statsCurrentPage = 0;
|
||||
|
||||
typedef struct statpage_s {
|
||||
const char *title;
|
||||
void (*drawer)(void);
|
||||
} statpage_t;
|
||||
|
||||
static statpage_t statsPages[] = {
|
||||
{ "Play Time Statistics", M_DrawStatsPlaytime, },
|
||||
{ "Level Statistics", M_DrawStatsMaps, },
|
||||
{ "Extra Statistics", M_DrawStatsExtra, },
|
||||
};
|
||||
|
||||
#define LENSTATSPAGES (sizeof(statsPages)/sizeof(statsPages[0]))
|
||||
#define NUMSTATSPAGES (kartstats.vanilla ? 2 : LENSTATSPAGES)
|
||||
|
||||
static void M_Statistics(INT32 choice)
|
||||
{
|
||||
@@ -8644,6 +8659,7 @@ static void M_Statistics(INT32 choice)
|
||||
statsMapList[j] = -1;
|
||||
statsMax = j - 11 + numextraemblems;
|
||||
statsLocation = 0;
|
||||
statsCurrentPage = 0;
|
||||
|
||||
if (statsMax < 0)
|
||||
statsMax = 0;
|
||||
@@ -8651,13 +8667,47 @@ static void M_Statistics(INT32 choice)
|
||||
M_SetupNextMenu(&SP_LevelStatsDef);
|
||||
}
|
||||
|
||||
static void M_DrawStatsMaps(int location)
|
||||
static void M_DrawStatsMaps(void)
|
||||
{
|
||||
INT32 y = 80, i = -1;
|
||||
char beststr[40];
|
||||
tic_t besttime = 0;
|
||||
INT32 mapsunfinished = 0;
|
||||
|
||||
int location = statsLocation;
|
||||
INT32 y = 62, i = -1, j;
|
||||
INT16 mnum;
|
||||
extraemblem_t *exemblem;
|
||||
boolean dotopname = true, dobottomarrow = (location < statsMax);
|
||||
|
||||
SHOWMODDEDGAME
|
||||
|
||||
for (j = 0; j < NUMMAPS; j++)
|
||||
{
|
||||
if (!mapheaderinfo[j] || !(mapheaderinfo[j]->menuflags & LF2_RECORDATTACK))
|
||||
continue;
|
||||
|
||||
if (!mainrecords[j] || mainrecords[j]->time <= 0)
|
||||
{
|
||||
mapsunfinished++;
|
||||
continue;
|
||||
}
|
||||
|
||||
besttime += mainrecords[j]->time;
|
||||
}
|
||||
|
||||
V_DrawString(20, 42, highlightflags|MENUCAPS, "Combined time records:");
|
||||
|
||||
sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime));
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, (mapsunfinished ? warningflags : 0), beststr);
|
||||
|
||||
if (mapsunfinished)
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 50, warningflags|MENUCAPS, va("(%d unfinished)", mapsunfinished));
|
||||
else
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 50, recommendedflags|MENUCAPS, "(complete)");
|
||||
|
||||
V_DrawString(32, 50, MENUCAPS, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
|
||||
V_DrawSmallScaledPatch(20, 50, 0, W_CachePatchName("GOTITA", PU_STATIC));
|
||||
|
||||
if (location)
|
||||
V_DrawCharacter(10, y-(skullAnimCounter/5),
|
||||
'\x1A' | highlightflags, false); // up arrow
|
||||
@@ -8746,53 +8796,78 @@ bottomarrow:
|
||||
'\x1B' | highlightflags, false); // down arrow
|
||||
}
|
||||
|
||||
static void M_DrawLevelStats(void)
|
||||
{
|
||||
char beststr[40];
|
||||
|
||||
tic_t besttime = 0;
|
||||
|
||||
INT32 i;
|
||||
INT32 mapsunfinished = 0;
|
||||
|
||||
M_DrawMenuTitle();
|
||||
|
||||
V_DrawString(20, 24, highlightflags|MENUCAPS, "Total Play Time:");
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, 32, MENUCAPS, va("%i hours, %i minutes, %i seconds",
|
||||
G_TicsToHours(totalplaytime),
|
||||
G_TicsToMinutes(totalplaytime, false),
|
||||
G_TicsToSeconds(totalplaytime)));
|
||||
V_DrawString(20, 42, highlightflags|MENUCAPS, "Total Matches:");
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, MENUCAPS, va("%i played", matchesplayed));
|
||||
|
||||
for (i = 0; i < NUMMAPS; i++)
|
||||
{
|
||||
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
|
||||
continue;
|
||||
|
||||
if (!mainrecords[i] || mainrecords[i]->time <= 0)
|
||||
{
|
||||
mapsunfinished++;
|
||||
continue;
|
||||
}
|
||||
|
||||
besttime += mainrecords[i]->time;
|
||||
#define DRAWTIMESTAT(y, title, field) { \
|
||||
char timebuf[80]; \
|
||||
V_DrawString(20, (y), highlightflags|MENUCAPS, title); \
|
||||
tic_t timeval = kartstats.field; \
|
||||
snprintf(timebuf, 80, "%02i:%02i:%02i", G_TicsToHours(timeval), G_TicsToMinutes(timeval, false), G_TicsToSeconds(timeval)); \
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, (y), MENUCAPS, timebuf); \
|
||||
}
|
||||
|
||||
V_DrawString(20, 62, highlightflags|MENUCAPS, "Combined time records:");
|
||||
#define DRAWAMOUNTSTAT(y, title, field) { \
|
||||
V_DrawString(20, (y), highlightflags|MENUCAPS, title); \
|
||||
unsigned amountval = kartstats.field; \
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, (y), MENUCAPS, va("%u", amountval)); \
|
||||
}
|
||||
|
||||
sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime));
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 62, (mapsunfinished ? warningflags : 0), beststr);
|
||||
static void M_DrawStatsPlaytime(void)
|
||||
{
|
||||
V_DrawString(20, 42, highlightflags|MENUCAPS, "Total Play Time:");
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, 52, MENUCAPS, va("%i hours, %i minutes, %i seconds",
|
||||
G_TicsToHours(kartstats.totalplaytime),
|
||||
G_TicsToMinutes(kartstats.totalplaytime, false),
|
||||
G_TicsToSeconds(kartstats.totalplaytime)));
|
||||
V_DrawString(20, 62, highlightflags|MENUCAPS, "Total Matches:");
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 62, MENUCAPS, va("%i played", kartstats.matchesplayed));
|
||||
|
||||
if (mapsunfinished)
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, warningflags|MENUCAPS, va("(%d unfinished)", mapsunfinished));
|
||||
else
|
||||
V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, recommendedflags|MENUCAPS, "(complete)");
|
||||
// Nothing else to draw
|
||||
if (kartstats.vanilla)
|
||||
return;
|
||||
|
||||
V_DrawString(32, 70, MENUCAPS, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
|
||||
V_DrawSmallScaledPatch(20, 70, 0, W_CachePatchName("GOTITA", PU_STATIC));
|
||||
SHOWMODDEDGAME
|
||||
|
||||
M_DrawStatsMaps(statsLocation);
|
||||
DRAWTIMESTAT(82, "RA Play Time:", raplaytime);
|
||||
DRAWTIMESTAT(92, "Online Play Time:", onlineplaytime);
|
||||
DRAWTIMESTAT(102, "Race Play Time:", raceplaytime);
|
||||
DRAWTIMESTAT(112, "Battle Play Time:", battleplaytime);
|
||||
}
|
||||
|
||||
// Note: only available with non-vanilla stats loaded, so it doesn't check for that
|
||||
static void M_DrawStatsExtra(void)
|
||||
{
|
||||
SHOWMODDEDGAME
|
||||
|
||||
DRAWTIMESTAT(42, "Time being SPB target:", spbtargettime);
|
||||
DRAWTIMESTAT(52, "Time spent in spinout:", spinouttime);
|
||||
|
||||
DRAWAMOUNTSTAT(72, "Total wins:", totalwins);
|
||||
DRAWAMOUNTSTAT(82, "Total podium (2nd/3rd place):", totalpodium);
|
||||
|
||||
DRAWAMOUNTSTAT(102, "Hits landed:", hits);
|
||||
DRAWAMOUNTSTAT(112, "Self-hits landed:", selfhits);
|
||||
|
||||
DRAWAMOUNTSTAT(132, "Sinks landed:", sinks);
|
||||
DRAWAMOUNTSTAT(142, "Times hit by sink:", sinked);
|
||||
|
||||
DRAWAMOUNTSTAT(162, "Total respawns:", respawns);
|
||||
}
|
||||
|
||||
#undef DRAWAMOUNTSTAT
|
||||
#undef DRAWTIMESTAT
|
||||
|
||||
static void M_DrawLevelStats(void)
|
||||
{
|
||||
M_DrawMenuTitle();
|
||||
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, 28, highlightflags|MENUCAPS, statsPages[statsCurrentPage].title);
|
||||
|
||||
INT32 w = V_StringWidth(statsPages[statsCurrentPage].title, highlightflags|MENUCAPS);
|
||||
V_DrawCharacter(BASEVIDWIDTH/2 - w/2 - 10 - (skullAnimCounter/5), 28,
|
||||
'\x1C' | highlightflags, false); // left arrow
|
||||
V_DrawCharacter(BASEVIDWIDTH/2 + w/2 + 2 + (skullAnimCounter/5), 28,
|
||||
'\x1D' | highlightflags, false); // right arrow
|
||||
|
||||
statsPages[statsCurrentPage].drawer();
|
||||
}
|
||||
|
||||
// Handle statistics.
|
||||
@@ -8803,23 +8878,46 @@ static void M_HandleLevelStats(INT32 choice)
|
||||
switch (choice)
|
||||
{
|
||||
case KEY_DOWNARROW:
|
||||
if (statsCurrentPage != 1) // Must be on level stats page
|
||||
break;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
if (statsLocation < statsMax)
|
||||
++statsLocation;
|
||||
break;
|
||||
|
||||
case KEY_UPARROW:
|
||||
if (statsCurrentPage != 1) // Must be on level stats page
|
||||
break;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
if (statsLocation)
|
||||
--statsLocation;
|
||||
break;
|
||||
|
||||
case KEY_RIGHTARROW:
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
statsCurrentPage++;
|
||||
if (statsCurrentPage >= NUMSTATSPAGES)
|
||||
statsCurrentPage = 0;
|
||||
break;
|
||||
|
||||
case KEY_LEFTARROW:
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
if (statsCurrentPage == 0)
|
||||
statsCurrentPage = NUMSTATSPAGES-1;
|
||||
else
|
||||
--statsCurrentPage;
|
||||
break;
|
||||
|
||||
case KEY_PGDN:
|
||||
if (statsCurrentPage != 1) // Must be on level stats page
|
||||
break;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
statsLocation += (statsLocation+13 >= statsMax) ? statsMax-statsLocation : 13;
|
||||
break;
|
||||
|
||||
case KEY_PGUP:
|
||||
if (statsCurrentPage != 1) // Must be on level stats page
|
||||
break;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
statsLocation -= (statsLocation < 13) ? statsLocation : 13;
|
||||
break;
|
||||
@@ -8860,6 +8958,8 @@ void M_DrawTimeAttackMenu(void)
|
||||
x = currentMenu->x;
|
||||
y = currentMenu->y;
|
||||
|
||||
SHOWMODDEDGAME
|
||||
|
||||
// Character face!
|
||||
if (W_CheckNumForName(skins[cv_chooseskin.value-1].facewant) != LUMPERROR)
|
||||
{
|
||||
@@ -11745,8 +11845,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();
|
||||
F_StartIntro();
|
||||
}
|
||||
if (erasecontext != 1)
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "s_sound.h"
|
||||
#include "w_wad.h"
|
||||
#include "k_kart.h" // SRB2kart 011617
|
||||
#include "k_stats.h"
|
||||
|
||||
#include "hu_stuff.h" // SRB2kart
|
||||
#include "i_system.h" // SRB2kart
|
||||
@@ -719,6 +720,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
|
||||
|
||||
if (thing->type == MT_PLAYER)
|
||||
{
|
||||
K_StatPlayerSink(thing->player, P_MobjWasRemoved(tmthing->target) ? NULL : tmthing->target->player);
|
||||
|
||||
S_StartSound(NULL, sfx_bsnipe); //let all players hear it.
|
||||
HU_SetCEchoFlags(0);
|
||||
HU_SetCEchoDuration(5);
|
||||
@@ -986,6 +989,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
|
||||
if ((thing->target == tmthing) && (thing->threshold > 0))
|
||||
return true;
|
||||
|
||||
K_StatPlayerSink(tmthing->player, P_MobjWasRemoved(thing->target) ? NULL : thing->target->player);
|
||||
|
||||
S_StartSound(NULL, sfx_cgot); //let all players hear it.
|
||||
HU_SetCEchoFlags(0);
|
||||
HU_SetCEchoDuration(5);
|
||||
|
||||
+2
-2
@@ -988,7 +988,7 @@ static inline void P_SpawnEmblems(void)
|
||||
static void P_SpawnSecretItems(boolean loademblems)
|
||||
{
|
||||
// Now let's spawn those funky emblem things! Tails 12-08-2002
|
||||
if (netgame || multiplayer || majormods) // No cheating!!
|
||||
if (netgame || multiplayer) // No cheating!!
|
||||
return;
|
||||
|
||||
if (loademblems)
|
||||
@@ -2976,7 +2976,7 @@ boolean P_SetupLevel(boolean skipprecip, boolean reloadinggamestate)
|
||||
nextmapoverride = 0;
|
||||
skipstats = false;
|
||||
|
||||
if (!(netgame || multiplayer) && !majormods)
|
||||
if (!(netgame || multiplayer))
|
||||
mapvisited[gamemap-1] |= MV_VISITED;
|
||||
|
||||
levelloading = false;
|
||||
|
||||
+2
-2
@@ -25,6 +25,7 @@
|
||||
#include "lua_hook.h"
|
||||
#include "k_director.h"
|
||||
#include "k_kart.h"
|
||||
#include "k_stats.h"
|
||||
#include "i_system.h"
|
||||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
@@ -610,8 +611,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();
|
||||
|
||||
if (run)
|
||||
{
|
||||
|
||||
+1
-1
@@ -844,7 +844,7 @@ void Y_StartIntermission(void)
|
||||
}
|
||||
case int_race: // (time-only race)
|
||||
{
|
||||
if (!majormods && !multiplayer && !demo.playback) // remove this once we have a proper time attack screen
|
||||
if (!multiplayer && !demo.playback) // remove this once we have a proper time attack screen
|
||||
{
|
||||
// Update visitation flags
|
||||
mapvisited[gamemap-1] |= MV_BEATEN;
|
||||
|
||||
Reference in new issue
Block a user