diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 1f9904ccd..c5e07f025 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -264,16 +264,12 @@ static bool ACS_GetStateFromString(const char *word, statenum_t *type) --------------------------------------------------*/ static bool ACS_GetSkinFromString(const char *word, INT32 *type) { - for (int i = 0; i < numskins; i++) - { - if (fastcmp(word, skins[i].name)) - { - *type = i; - return true; - } - } + INT32 skin = R_SkinAvailable(word); + if (skin == -1) + return false; - return false; + *type = skin; + return true; } /*-------------------------------------------------- @@ -293,7 +289,7 @@ static bool ACS_GetColorFromString(const char *word, skincolornum_t *type) { for (int i = 0; i < numskincolors; i++) { - if (fastcmp(word, skins[i].name)) + if (fastcmp(word, skincolors[i].name)) { *type = static_cast(i); return true; diff --git a/src/command.c b/src/command.c index 6cacd3bcd..157cb87bc 100644 --- a/src/command.c +++ b/src/command.c @@ -2051,7 +2051,7 @@ static void CV_SetValueMaybeStealth(consvar_t *var, INT32 value, boolean stealth tmpskin = "None"; else tmpskin = skins[value].name; - strncpy(val, tmpskin, SKINNAMESIZE); + strlcpy(val, tmpskin, SKINNAMESIZE+1); } else sprintf(val, "%d", value); diff --git a/src/d_main.c b/src/d_main.c index 1b562b238..5f4cf88ba 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1851,6 +1851,47 @@ void D_SRB2Main(void) { PR_ApplyProfile(cv_ttlprofilen.value, 0); + if (gamedata->importprofilewins == true) + { + profile_t *pr = PR_GetProfile(cv_ttlprofilen.value); + if (pr != NULL) + { + INT32 importskin = R_SkinAvailable(pr->skinname); + if (importskin != -1) + { + skins[importskin].records.wins = pr->wins; + + cupheader_t *cup; + for (cup = kartcupheaders; cup; cup = cup->next) + { + for (i = 0; i < KARTGP_MAX; i++) + { + if (cup->windata[i].best_placement == 0) + continue; + cup->windata[i].best_skin.id = importskin; + cup->windata[i].best_skin.unloaded = NULL; + } + } + + unloaded_cupheader_t *unloadedcup; + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next) + { + for (i = 0; i < KARTGP_MAX; i++) + { + if (unloadedcup->windata[i].best_placement == 0) + continue; + unloadedcup->windata[i].best_skin.id = importskin; + unloadedcup->windata[i].best_skin.unloaded = NULL; + } + } + + CONS_Printf(" Wins for profile \"%s\" imported onto character \"%s\"\n", pr->profilename, skins[importskin].name); + } + } + + gamedata->importprofilewins = false; + } + for (i = 1; i < cv_splitplayers.value; i++) { PR_ApplyProfile(cv_lastprofile[i].value, i); diff --git a/src/deh_soc.c b/src/deh_soc.c index 751c3c07a..b466f1158 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -170,7 +170,7 @@ void clear_unlockables(void) void clear_conditionsets(void) { - UINT8 i; + UINT16 i; for (i = 0; i < MAXCONDITIONSETS; ++i) M_ClearConditionSet(i); } @@ -192,8 +192,6 @@ void clear_levels(void) P_DeleteHeaderFollowers(nummapheaders); - Z_Free(mapheaderinfo[nummapheaders]->mainrecord); - Patch_Free(mapheaderinfo[nummapheaders]->thumbnailPic); Patch_Free(mapheaderinfo[nummapheaders]->minimapPic); @@ -952,7 +950,46 @@ void readlevelheader(MYFILE *f, char * name) if (mapheaderinfo[num]->lumpname == NULL) { - mapheaderinfo[num]->lumpname = Z_StrDup(name); + mapheaderinfo[num]->lumpnamehash = quickncasehash(name, MAXMAPLUMPNAME); + + // Check to see if we have any custom map record data that we could substitute in. + unloaded_mapheader_t *unloadedmap, *prev = NULL; + for (unloadedmap = unloadedmapheaders; unloadedmap; prev = unloadedmap, unloadedmap = unloadedmap->next) + { + if (unloadedmap->lumpnamehash != mapheaderinfo[num]->lumpnamehash) + continue; + + if (strcasecmp(name, unloadedmap->lumpname) != 0) + continue; + + // Copy in mapvisited, time, lap, etc. + unloadedmap->records.mapvisited &= MV_MAX; + M_Memcpy(&mapheaderinfo[num]->records, &unloadedmap->records, sizeof(recorddata_t)); + + // Reuse the zone-allocated lumpname string. + mapheaderinfo[num]->lumpname = unloadedmap->lumpname; + + // Remove this entry from the chain. + if (prev) + { + prev->next = unloadedmap->next; + } + else + { + unloadedmapheaders = unloadedmap->next; + } + + // Finally, free. + Z_Free(unloadedmap); + + break; + } + + if (mapheaderinfo[num]->lumpname == NULL) + { + // If there was no string to reuse, dup our own. + mapheaderinfo[num]->lumpname = Z_StrDup(name); + } } do @@ -2280,9 +2317,9 @@ void readunlockable(MYFILE *f, INT32 num) strupr(word2); if (fastcmp(word, "CONDITIONSET")) - unlockables[num].conditionset = (UINT8)i; + unlockables[num].conditionset = (UINT16)i; else if (fastcmp(word, "MAJORUNLOCK")) - unlockables[num].majorunlock = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + unlockables[num].majorunlock = (UINT8)(i != 0 || word2[0] == 'T' || word2[0] == 'Y'); else if (fastcmp(word, "TYPE")) { if (fastcmp(word2, "NONE")) @@ -2638,9 +2675,10 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ty = UCRP_PODIUMCUP; { cupheader_t *cup = kartcupheaders; + UINT32 hash = quickncasehash(params[1], MAXCUPNAME); while (cup) { - if (!strcmp(cup->name, params[1])) + if (hash == cup->namehash && !strcmp(cup->name, params[1])) break; cup = cup->next; } diff --git a/src/dehacked.c b/src/dehacked.c index 9e50a9f42..b46805a97 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -465,39 +465,81 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) // else if (fastcmp(word, "CUP")) { - cupheader_t *cup = kartcupheaders; - cupheader_t *prev = NULL; - - while (cup) + size_t len = strlen(word2); + if (len <= MAXCUPNAME-1) { - if (fastcmp(cup->name, word2)) + cupheader_t *cup = kartcupheaders; + cupheader_t *prev = NULL; + UINT32 hash = quickncasehash(word2, MAXCUPNAME); + + while (cup) { - // Only a major mod if editing stuff that isn't your own! - G_SetGameModified(multiplayer, true); - break; + if (hash == cup->namehash && fastcmp(cup->name, word2)) + { + // Only a major mod if editing stuff that isn't your own! + G_SetGameModified(multiplayer, true); + break; + } + + prev = cup; + cup = cup->next; } - prev = cup; - cup = cup->next; - } + // Nothing found, add to the end. + if (!cup) + { + cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); + cup->id = numkartcupheaders; + cup->monitor = 1; + deh_strlcpy(cup->name, word2, + sizeof(cup->name), va("Cup header %s: name", word2)); + cup->namehash = hash; - // Nothing found, add to the end. - if (!cup) + // Check to see if we have any custom cup record data that we could substitute in. + unloaded_cupheader_t *unloadedcup, *unloadedprev = NULL; + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedprev = unloadedcup, unloadedcup = unloadedcup->next) + { + if (unloadedcup->namehash != hash) + continue; + + if (strcasecmp(word2, unloadedcup->name) != 0) + continue; + + // Copy in standings, etc. + M_Memcpy(&cup->windata, &unloadedcup->windata, sizeof(cup->windata)); + + // Remove this entry from the chain. + if (unloadedprev) + { + unloadedprev->next = unloadedcup->next; + } + else + { + unloadedcupheaders = unloadedcup->next; + } + + // Finally, free. + Z_Free(unloadedcup); + + break; + } + + if (prev != NULL) + prev->next = cup; + if (kartcupheaders == NULL) + kartcupheaders = cup; + numkartcupheaders++; + CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + } + + readcupheader(f, cup); + + } + else { - cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); - cup->id = numkartcupheaders; - cup->monitor = 1; - deh_strlcpy(cup->name, word2, - sizeof(cup->name), va("Cup header %s: name", word2)); - if (prev != NULL) - prev->next = cup; - if (kartcupheaders == NULL) - kartcupheaders = cup; - numkartcupheaders++; - CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + deh_warning("Cup header's name %s is too long (%s characters VS %d max)", word2, sizeu1(len), (MAXCUPNAME-1)); + ignorelines(f); } - - readcupheader(f, cup); } else if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION")) { diff --git a/src/doomstat.h b/src/doomstat.h index 77317fd0a..619f01db3 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -112,12 +112,47 @@ extern preciptype_t curWeather; /** Time attack information, currently a very small structure. */ + +struct skinrecord_t +{ + UINT32 wins; + + // Purely assistive in gamedata save processes + UINT32 _saveid; +}; + +struct unloaded_skin_t +{ + char name[SKINNAMESIZE+1]; + UINT32 namehash; + + skinrecord_t records; + + unloaded_skin_t *next; +}; + +extern unloaded_skin_t *unloadedskins; + +struct skinreference_t +{ + unloaded_skin_t *unloaded; + UINT8 id; +}; + +// mapvisited is now a set of flags that says what we've done in the map. +#define MV_VISITED (1) +#define MV_BEATEN (1<<1) +#define MV_ENCORE (1<<2) +#define MV_SPBATTACK (1<<3) +#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK) +#define MV_FINISHNEEDED (1<<7) +#define MV_PERSISTUNLOADED (MV_SPBATTACK|MV_FINISHNEEDED) + struct recorddata_t { + UINT8 mapvisited; tic_t time; ///< Time in which the level was finished. tic_t lap; ///< Best lap time for this level. - //UINT32 score; ///< Score when the level was finished. - //UINT16 rings; ///< Rings when the level was finished. }; #define KARTSPEED_AUTO -1 @@ -142,16 +177,9 @@ struct cupwindata_t UINT8 best_placement; gp_rank_e best_grade; boolean got_emerald; + skinreference_t best_skin; }; -// mapvisited is now a set of flags that says what we've done in the map. -#define MV_VISITED (1) -#define MV_BEATEN (1<<1) -#define MV_ENCORE (1<<2) -#define MV_SPBATTACK (1<<3) -#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK) -#define MV_MP ((MV_MAX+1)<<1) - // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; extern boolean majormods; @@ -363,11 +391,16 @@ struct customoption_t #define CUPCACHE_SPECIAL (CUPCACHE_BONUS+MAXBONUSLIST) #define CUPCACHE_MAX (CUPCACHE_SPECIAL+1) +#define MAXCUPNAME 16 // includes \0, for cleaner savedata + struct cupheader_t { UINT16 id; ///< Cup ID UINT8 monitor; ///< Monitor graphic 1-9 or A-Z - char name[15]; ///< Cup title (14 chars) + + char name[MAXCUPNAME]; ///< Cup title + UINT32 namehash; ///< Cup title hash + char icon[9]; ///< Name of the icon patch char *levellist[CUPCACHE_MAX]; ///< List of levels that belong to this cup INT16 cachedlevels[CUPCACHE_MAX]; ///< IDs in levellist, bonusgame, and specialstage @@ -381,6 +414,18 @@ struct cupheader_t extern cupheader_t *kartcupheaders; // Start of cup linked list extern UINT16 numkartcupheaders; +struct unloaded_cupheader_t +{ + char name[MAXCUPNAME]; + UINT32 namehash; + + cupwindata_t windata[4]; + + unloaded_cupheader_t *next; +}; + +extern unloaded_cupheader_t *unloadedcupheaders; + #define MAXMAPLUMPNAME 64 // includes \0, for cleaner savedata #define MAXSTAFF 3 @@ -400,6 +445,7 @@ struct mapheader_t { // Core game information, not user-modifiable directly char *lumpname; ///< Lump name can be really long + UINT32 lumpnamehash; ///< quickncasehash(->lumpname, MAXMAPLUMPNAME) lumpnum_t lumpnum; ///< Lump number for the map, used by vres_GetMap void *thumbnailPic; ///< Lump data for the level select thumbnail. @@ -411,8 +457,7 @@ struct mapheader_t UINT8 ghostCount; ///< Count of valid staff ghosts staffbrief_t *ghostBrief[MAXSTAFF]; ///< Mallocated array of names for each staff ghost - UINT8 mapvisited; ///< A set of flags that says what we've done in the map. - recorddata_t *mainrecord; ///< Stores best time attack data + recorddata_t records; ///< Stores completion/record attack data cupheader_t *cup; ///< Cached cup @@ -496,6 +541,18 @@ struct mapheader_t extern mapheader_t** mapheaderinfo; extern INT32 nummapheaders, mapallocsize; +struct unloaded_mapheader_t +{ + char *lumpname; + UINT32 lumpnamehash; + + recorddata_t records; + + unloaded_mapheader_t *next; +}; + +extern unloaded_mapheader_t *unloadedmapheaders; + // Gametypes #define NUMGAMETYPEFREESLOTS (128) #define MAXGAMETYPELENGTH (32) diff --git a/src/g_game.c b/src/g_game.c index 9f75d137a..f65f0ba6d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -200,10 +200,14 @@ mapheader_t** mapheaderinfo = {NULL}; INT32 nummapheaders = 0; INT32 mapallocsize = 0; +unloaded_mapheader_t *unloadedmapheaders = NULL; + // Kart cup definitions cupheader_t *kartcupheaders = NULL; UINT16 numkartcupheaders = 0; +unloaded_cupheader_t *unloadedcupheaders = NULL; + static boolean exitgame = false; static boolean retrying = false; static boolean retryingmodeattack = false; @@ -444,44 +448,60 @@ consvar_t cv_rumble[MAXSPLITSCREENPLAYERS] = { char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; INT32 player_name_changes[MAXPLAYERS]; -// Allocation for time and nights data -void G_AllocMainRecordData(INT16 i) -{ - if (i > nummapheaders || !mapheaderinfo[i]) - I_Error("G_AllocMainRecordData: Internal map ID %d not found (nummapheaders = %d)\n", i, nummapheaders); - if (!mapheaderinfo[i]->mainrecord) - mapheaderinfo[i]->mainrecord = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL); - memset(mapheaderinfo[i]->mainrecord, 0, sizeof(recorddata_t)); -} - // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS void G_ClearRecords(void) { - INT16 i; - cupheader_t *cup; + UINT16 i; - for (i = 0; i < nummapheaders; ++i) + for (i = 0; i < numskins; i++) { - if (mapheaderinfo[i]->mainrecord) - { - Z_Free(mapheaderinfo[i]->mainrecord); - mapheaderinfo[i]->mainrecord = NULL; - } + memset(&skins[i].records, 0, sizeof(skins[i].records)); } + for (i = 0; i < nummapheaders; i++) + { + memset(&mapheaderinfo[i]->records, 0, sizeof(recorddata_t)); + } + + cupheader_t *cup; for (cup = kartcupheaders; cup; cup = cup->next) { memset(&cup->windata, 0, sizeof(cup->windata)); } + + unloaded_skin_t *unloadedskin, *nextunloadedskin = NULL; + for (unloadedskin = unloadedskins; unloadedskin; unloadedskin = nextunloadedskin) + { + nextunloadedskin = unloadedskin->next; + Z_Free(unloadedskin); + } + unloadedskins = NULL; + + unloaded_mapheader_t *unloadedmap, *nextunloadedmap = NULL; + for (unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = nextunloadedmap) + { + nextunloadedmap = unloadedmap->next; + Z_Free(unloadedmap->lumpname); + Z_Free(unloadedmap); + } + unloadedmapheaders = NULL; + + unloaded_cupheader_t *unloadedcup, *nextunloadedcup = NULL; + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = nextunloadedcup) + { + nextunloadedcup = unloadedcup->next; + Z_Free(unloadedcup); + } + unloadedcupheaders = NULL; } // For easy retrieval of records tic_t G_GetBestTime(INT16 map) { - if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->time <= 0) + if (!mapheaderinfo[map] || mapheaderinfo[map]->records.time <= 0) return (tic_t)UINT32_MAX; - return mapheaderinfo[map]->mainrecord->time; + return mapheaderinfo[map]->records.time; } // BE RIGHT BACK @@ -490,10 +510,10 @@ tic_t G_GetBestTime(INT16 map) /* tic_t G_GetBestLap(INT16 map) { - if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->lap <= 0) + if (!mapheaderinfo[map] || mapheaderinfo[map]->records.lap <= 0) return (tic_t)UINT32_MAX; - return mapheaderinfo[map]->mainrecord->lap; + return mapheaderinfo[map]->records.lap; } */ @@ -621,32 +641,28 @@ void G_UpdateRecords(void) { UINT8 earnedEmblems; - // Record new best time - if (!mapheaderinfo[gamemap-1]->mainrecord) - G_AllocMainRecordData(gamemap-1); - if (modeattacking & ATTACKING_TIME) { tic_t time = players[consoleplayer].realtime; if (players[consoleplayer].pflags & PF_NOCONTEST) time = UINT32_MAX; - if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (time < mapheaderinfo[gamemap-1]->mainrecord->time)) + if (((mapheaderinfo[gamemap-1]->records.time == 0) || (time < mapheaderinfo[gamemap-1]->records.time)) && (time < UINT32_MAX)) // DNF - mapheaderinfo[gamemap-1]->mainrecord->time = time; + mapheaderinfo[gamemap-1]->records.time = time; } else { - mapheaderinfo[gamemap-1]->mainrecord->time = 0; + mapheaderinfo[gamemap-1]->records.time = 0; } if (modeattacking & ATTACKING_LAP) { - if ((mapheaderinfo[gamemap-1]->mainrecord->lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->mainrecord->lap)) - mapheaderinfo[gamemap-1]->mainrecord->lap = bestlap; + if ((mapheaderinfo[gamemap-1]->records.lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->records.lap)) + mapheaderinfo[gamemap-1]->records.lap = bestlap; } else { - mapheaderinfo[gamemap-1]->mainrecord->lap = 0; + mapheaderinfo[gamemap-1]->records.lap = 0; } // Check emblems when level data is updated @@ -807,9 +823,13 @@ INT32 G_MapNumber(const char * name) #endif { INT32 map; + UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME); for (map = 0; map < nummapheaders; ++map) { + if (hash != mapheaderinfo[map]->lumpnamehash) + continue; + if (strcasecmp(mapheaderinfo[map]->lumpname, name) != 0) continue; @@ -3982,16 +4002,16 @@ static void G_UpdateVisited(void) return; // Update visitation flags - mapheaderinfo[prevmap]->mapvisited |= MV_BEATEN; + mapheaderinfo[prevmap]->records.mapvisited |= MV_BEATEN; if (encoremode == true) { - mapheaderinfo[prevmap]->mapvisited |= MV_ENCORE; + mapheaderinfo[prevmap]->records.mapvisited |= MV_ENCORE; } if (modeattacking & ATTACKING_SPB) { - mapheaderinfo[prevmap]->mapvisited |= MV_SPBATTACK; + mapheaderinfo[prevmap]->records.mapvisited |= MV_SPBATTACK; } if (modeattacking) @@ -4446,6 +4466,7 @@ static void G_DoCompleted(void) grandprixinfo.rank.prisons += numtargets; grandprixinfo.rank.position = MAXPLAYERS; + grandprixinfo.rank.skin = MAXSKINS; for (i = 0; i < MAXPLAYERS; i++) { @@ -4475,7 +4496,12 @@ static void G_DoCompleted(void) if (players[i].bot == false) { - grandprixinfo.rank.position = min(grandprixinfo.rank.position, K_GetPodiumPosition(&players[i])); + UINT8 podiumposition = K_GetPodiumPosition(&players[i]); + if (podiumposition <= grandprixinfo.rank.position) + { + grandprixinfo.rank.position = podiumposition; + grandprixinfo.rank.skin = players[i].skin; + } } } } @@ -4746,7 +4772,7 @@ void G_LoadGameSettings(void) } #define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual -#define GD_VERSIONMINOR 2 // Change every format update +#define GD_VERSIONMINOR 3 // Change every format update static const char *G_GameDataFolder(void) { @@ -4767,7 +4793,13 @@ void G_LoadGameData(void) boolean gridunusable = false; savebuffer_t save = {0}; + UINT16 emblemreadcount = MAXEMBLEMS; + UINT16 unlockreadcount = MAXUNLOCKABLES; + UINT16 conditionreadcount = MAXCONDITIONSETS; + size_t unlockreadsize = sizeof(UINT16); + //For records + UINT32 numgamedataskins; UINT32 numgamedatamapheaders; UINT32 numgamedatacups; @@ -4819,6 +4851,12 @@ void G_LoadGameData(void) P_SaveBufferFree(&save); I_Error("Game data is from the future! (expected %d, got %d)\nRename or delete %s (maybe in %s) and try again.", GD_VERSIONMINOR, versionMinor, gamedatafilename, gdfolder); } + else if (versionMinor < GD_VERSIONMINOR) + { + // We're converting - let'd create a backup. + FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, gamedatafilename), save.buffer, save.size); + } + if ((versionMinor == 0 || versionMinor == 1) #ifdef DEVELOP || M_CheckParm("-resetchallengegrid") @@ -4846,7 +4884,16 @@ void G_LoadGameData(void) gamedata->pendingkeyrounds = READUINT32(save.p); gamedata->pendingkeyroundoffset = READUINT8(save.p); - gamedata->keyspending = READUINT8(save.p); + + if (versionMinor < 3) + { + gamedata->keyspending = READUINT8(save.p); + } + else + { + gamedata->keyspending = READUINT16(save.p); + } + gamedata->chaokeys = READUINT16(save.p); gamedata->everloadedaddon = (boolean)READUINT8(save.p); @@ -4870,32 +4917,39 @@ void G_LoadGameData(void) } } + if (versionMinor < 3) + { + emblemreadcount = 512; + unlockreadcount = conditionreadcount = UINT8_MAX; + unlockreadsize = sizeof(UINT8); + } + // To save space, use one bit per collected/achieved/unlocked flag - for (i = 0; i < MAXEMBLEMS;) + for (i = 0; i < emblemreadcount;) { rtemp = READUINT8(save.p); - for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j) + for (j = 0; j < 8 && j+i < emblemreadcount; ++j) gamedata->collected[j+i] = ((rtemp >> j) & 1); i += j; } - for (i = 0; i < MAXUNLOCKABLES;) + for (i = 0; i < unlockreadcount;) { rtemp = READUINT8(save.p); - for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) + for (j = 0; j < 8 && j+i < unlockreadcount; ++j) gamedata->unlocked[j+i] = ((rtemp >> j) & 1); i += j; } - for (i = 0; i < MAXUNLOCKABLES;) + for (i = 0; i < unlockreadcount;) { rtemp = READUINT8(save.p); - for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) + for (j = 0; j < 8 && j+i < unlockreadcount; ++j) gamedata->unlockpending[j+i] = ((rtemp >> j) & 1); i += j; } - for (i = 0; i < MAXCONDITIONSETS;) + for (i = 0; i < conditionreadcount;) { rtemp = READUINT8(save.p); - for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j) + for (j = 0; j < 8 && j+i < conditionreadcount; ++j) gamedata->achieved[j+i] = ((rtemp >> j) & 1); i += j; } @@ -4904,7 +4958,7 @@ void G_LoadGameData(void) { UINT16 burn = READUINT16(save.p); // Previous challengegridwidth UINT8 height = (versionMinor > 0) ? CHALLENGEGRIDHEIGHT : 5; - save.p += (burn * height * sizeof(UINT8)); // Step over previous grid data + save.p += (burn * height * unlockreadsize); // Step over previous grid data gamedata->challengegridwidth = 0; Z_Free(gamedata->challengegrid); @@ -4917,11 +4971,23 @@ void G_LoadGameData(void) if (gamedata->challengegridwidth) { gamedata->challengegrid = Z_Malloc( - (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)), + (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT16)), PU_STATIC, NULL); - for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++) + if (unlockreadsize == sizeof(UINT8)) { - gamedata->challengegrid[i] = READUINT8(save.p); + for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++) + { + gamedata->challengegrid[i] = READUINT8(save.p); + if (gamedata->challengegrid[i] == unlockreadcount) + gamedata->challengegrid[i] = MAXUNLOCKABLES; + } + } + else + { + for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++) + { + gamedata->challengegrid[i] = READUINT16(save.p); + } } } else @@ -4933,44 +4999,129 @@ void G_LoadGameData(void) gamedata->timesBeaten = READUINT32(save.p); // Main records + + skinreference_t *tempskinreferences = NULL; + + if (versionMinor < 3) + { + gamedata->importprofilewins = true; + numgamedataskins = 0; + } + else + { + numgamedataskins = READUINT32(save.p); + + if (numgamedataskins) + { + tempskinreferences = Z_Malloc( + numgamedataskins * sizeof (skinreference_t), + PU_STATIC, + NULL + ); + + for (i = 0; i < numgamedataskins; i++) + { + char skinname[SKINNAMESIZE+1]; + INT32 skin; + + READSTRINGN(save.p, skinname, SKINNAMESIZE); + skin = R_SkinAvailable(skinname); + + skinrecord_t dummyrecord; + + dummyrecord.wins = READUINT32(save.p); + dummyrecord._saveid = i; + + CONS_Printf(" (TEMPORARY DISPLAY) skinname is \"%s\", has %u wins\n", skinname, dummyrecord.wins); + + tempskinreferences[i].id = MAXSKINS; + + if (skin != -1) + { + // We found a skin, so assign the win. + + M_Memcpy(&skins[skin].records, &dummyrecord, sizeof(skinrecord_t)); + + tempskinreferences[i].id = skin; + tempskinreferences[i].unloaded = NULL; + } + else if (dummyrecord.wins) + { + // Invalid, but we don't want to lose all the juicy statistics. + // Instead, update a FILO linked list of "unloaded skins". + + unloaded_skin_t *unloadedskin = + Z_Malloc( + sizeof(unloaded_skin_t), + PU_STATIC, NULL + ); + + // Establish properties, for later retrieval on file add. + strlcpy(unloadedskin->name, skinname, sizeof(unloadedskin->name)); + unloadedskin->namehash = quickncasehash(unloadedskin->name, SKINNAMESIZE); + + // Insert at the head, just because it's convenient. + unloadedskin->next = unloadedskins; + unloadedskins = unloadedskin; + + // Finally, copy into. + M_Memcpy(&unloadedskin->records, &dummyrecord, sizeof(skinrecord_t)); + + tempskinreferences[i].unloaded = unloadedskin; + } + } + } + } + numgamedatamapheaders = READUINT32(save.p); - if (numgamedatamapheaders >= NEXTMAP_SPECIAL) - goto datacorrupt; for (i = 0; i < numgamedatamapheaders; i++) { char mapname[MAXMAPLUMPNAME]; INT16 mapnum; - tic_t rectime; - tic_t reclap; - READSTRINGN(save.p, mapname, sizeof(mapname)); + READSTRINGL(save.p, mapname, MAXMAPLUMPNAME); mapnum = G_MapNumber(mapname); - rtemp = READUINT8(save.p); - rectime = (tic_t)READUINT32(save.p); - reclap = (tic_t)READUINT32(save.p); + recorddata_t dummyrecord; + + dummyrecord.mapvisited = READUINT8(save.p); + dummyrecord.time = (tic_t)READUINT32(save.p); + dummyrecord.lap = (tic_t)READUINT32(save.p); if (mapnum < nummapheaders && mapheaderinfo[mapnum]) { // Valid mapheader, time to populate with record data. - if ((mapheaderinfo[mapnum]->mapvisited = rtemp) & ~MV_MAX) - goto datacorrupt; - - if (rectime || reclap) - { - G_AllocMainRecordData((INT16)i); - mapheaderinfo[i]->mainrecord->time = rectime; - mapheaderinfo[i]->mainrecord->lap = reclap; - //CONS_Printf("ID %d, Time = %d, Lap = %d\n", i, rectime/35, reclap/35); - } + dummyrecord.mapvisited &= MV_MAX; + M_Memcpy(&mapheaderinfo[mapnum]->records, &dummyrecord, sizeof(recorddata_t)); } - else + else if ( + ((dummyrecord.mapvisited & MV_PERSISTUNLOADED) != 0 + && (dummyrecord.mapvisited & MV_BEATEN) != 0) + || dummyrecord.time != 0 + || dummyrecord.lap != 0 + ) { - // Since it's not worth declaring the entire gamedata - // corrupt over extra maps, we report and move on. - CONS_Alert(CONS_WARNING, "Map with lumpname %s does not exist, time record data will be discarded\n", mapname); + // Invalid, but we don't want to lose all the juicy statistics. + // Instead, update a FILO linked list of "unloaded mapheaders". + + unloaded_mapheader_t *unloadedmap = + Z_Malloc( + sizeof(unloaded_mapheader_t), + PU_STATIC, NULL + ); + + // Establish properties, for later retrieval on file add. + unloadedmap->lumpname = Z_StrDup(mapname); + unloadedmap->lumpnamehash = quickncasehash(unloadedmap->lumpname, MAXMAPLUMPNAME); + + // Insert at the head, just because it's convenient. + unloadedmap->next = unloadedmapheaders; + unloadedmapheaders = unloadedmap; + + // Finally, copy into. + M_Memcpy(&unloadedmap->records, &dummyrecord, sizeof(recorddata_t)); } } @@ -4980,15 +5131,22 @@ void G_LoadGameData(void) for (i = 0; i < numgamedatacups; i++) { - char cupname[16]; + char cupname[MAXCUPNAME]; cupheader_t *cup; + cupwindata_t dummywindata[4]; + // Find the relevant cup. - READSTRINGN(save.p, cupname, sizeof(cupname)); + READSTRINGL(save.p, cupname, sizeof(cupname)); + UINT32 hash = quickncasehash(cupname, MAXCUPNAME); for (cup = kartcupheaders; cup; cup = cup->next) { + if (cup->namehash != hash) + continue; + if (strcmp(cup->name, cupname)) continue; + break; } @@ -4997,23 +5155,92 @@ void G_LoadGameData(void) { rtemp = READUINT8(save.p); - // ...but only record it if we actually found the associated cup. - if (cup) - { - cup->windata[j].best_placement = (rtemp & 0x0F); - cup->windata[j].best_grade = (rtemp & 0x70)>>4; - if (rtemp & 0x80) - { - if (j == 0) - goto datacorrupt; + dummywindata[j].best_placement = (rtemp & 0x0F); + dummywindata[j].best_grade = (rtemp & 0x70)>>4; + dummywindata[j].got_emerald = !!(rtemp & 0x80); - cup->windata[j].got_emerald = true; + dummywindata[j].best_skin.id = MAXSKINS; + dummywindata[j].best_skin.unloaded = NULL; + if (versionMinor >= 3) + { + UINT32 _saveid = READUINT32(save.p); + if (_saveid < numgamedataskins) + { + M_Memcpy(&dummywindata[j].best_skin, &tempskinreferences[_saveid], sizeof(dummywindata[j].best_skin)); } } } + + if (versionMinor < 3) + { + // We now require backfilling of placement information. + + cupwindata_t bestwindata; + bestwindata.best_placement = 0; + + j = KARTGP_MAX; + while (j > 0) + { + j--; + + if (bestwindata.best_placement == 0) + { + if (dummywindata[j].best_placement != 0) + { + M_Memcpy(&bestwindata, &dummywindata[j], sizeof(bestwindata)); + } + continue; + } + + if (dummywindata[j].best_placement != 0) + { + if (dummywindata[j].best_placement < bestwindata.best_placement) + bestwindata.best_placement = dummywindata[j].best_placement; + + if (dummywindata[j].best_grade > bestwindata.best_grade) + bestwindata.best_grade = dummywindata[j].best_grade; + + bestwindata.got_emerald |= dummywindata[j].got_emerald; + } + + M_Memcpy(&dummywindata[j], &bestwindata, sizeof(dummywindata[j])); + } + } + + if (cup) + { + // We found a cup, so assign the windata. + + M_Memcpy(&cup->windata, &dummywindata, sizeof(cup->windata)); + } + else if (dummywindata[0].best_placement != 0) + { + // Invalid, but we don't want to lose all the juicy statistics. + // Instead, update a FILO linked list of "unloaded cupheaders". + + unloaded_cupheader_t *unloadedcup = + Z_Malloc( + sizeof(unloaded_cupheader_t), + PU_STATIC, NULL + ); + + // Establish properties, for later retrieval on file add. + strlcpy(unloadedcup->name, cupname, sizeof(unloadedcup->name)); + unloadedcup->namehash = quickncasehash(unloadedcup->name, MAXCUPNAME); + + // Insert at the head, just because it's convenient. + unloadedcup->next = unloadedcupheaders; + unloadedcupheaders = unloadedcup; + + // Finally, copy into. + M_Memcpy(&unloadedcup->windata, &dummywindata, sizeof(unloadedcup->windata)); + } } } + if (tempskinreferences) + Z_Free(tempskinreferences); + // done P_SaveBufferFree(&save); @@ -5074,7 +5301,7 @@ void G_DirtyGameData(void) void G_SaveGameData(void) { size_t length; - INT32 i, j, numcups; + INT32 i, j; cupheader_t *cup; UINT8 btemp; savebuffer_t save = {0}; @@ -5095,7 +5322,7 @@ void G_SaveGameData(void) length = (4+1+1+ 4+4+ (4*GDGT_MAX)+ - 4+1+1+2+ + 4+1+2+2+ 1+1+1+ 4+ (MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+ @@ -5103,16 +5330,105 @@ void G_SaveGameData(void) if (gamedata->challengegrid) { - length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; + length += (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT) * 2; } - length += 4 + (nummapheaders * (MAXMAPLUMPNAME+1+4+4)); - numcups = 0; + + UINT32 numgamedataskins = 0; + unloaded_skin_t *unloadedskin; + + for (i = 0; i < numskins; i++) + { + // It's safe to assume a skin with no wins will have no other data worth keeping + if (skins[i].records.wins == 0) + { + continue; + } + + numgamedataskins++; + } + + for (unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next) + { + // Ditto, with the exception that we should warn about it. + if (unloadedskin->records.wins == 0) + { + CONS_Alert(CONS_WARNING, "Unloaded skin \"%s\" has no wins!\n", unloadedskin->name); + continue; + } + + numgamedataskins++; + } + + length += 4 + (numgamedataskins * (SKINNAMESIZE+4)); + + + UINT32 numgamedatamapheaders = 0; + unloaded_mapheader_t *unloadedmap; + + for (i = 0; i < nummapheaders; i++) + { + // It's safe to assume a level with no mapvisited will have no other data worth keeping, since you get MV_VISITED just for opening it. + if (!(mapheaderinfo[i]->records.mapvisited & MV_MAX)) + { + continue; + } + + numgamedatamapheaders++; + } + + for (unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next) + { + // Ditto, with the exception that we should warn about it. + if (!(unloadedmap->records.mapvisited & MV_MAX)) + { + CONS_Alert(CONS_WARNING, "Unloaded map \"%s\" has no mapvisited!\n", unloadedmap->lumpname); + continue; + } + + // It's far off on the horizon, beyond many memory limits, but prevent a potential misery moment of losing ALL your data. + if (++numgamedatamapheaders == UINT32_MAX) + { + CONS_Alert(CONS_WARNING, "Some unloaded map record data has been dropped due to datatype limitations.\n"); + break; + } + } + + length += 4 + (numgamedatamapheaders * (MAXMAPLUMPNAME+1+4+4)); + + + UINT32 numgamedatacups = 0; + unloaded_cupheader_t *unloadedcup; + for (cup = kartcupheaders; cup; cup = cup->next) { - numcups++; + // Results are populated downwards, so no Easy win + // means there's no important player data to save. + if (cup->windata[0].best_placement == 0) + continue; + + numgamedatacups++; } - length += 4 + (numcups * (4+16)); + + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next) + { + // Ditto, with the exception that we should warn about it. + if (unloadedcup->windata[0].best_placement == 0) + { + CONS_Alert(CONS_WARNING, "Unloaded cup \"%s\" has no windata!\n", unloadedcup->name); + continue; + } + + // It's far off on the horizon, beyond many memory limits, but prevent a potential misery moment of losing ALL your data. + if (++numgamedatacups == UINT32_MAX) + { + CONS_Alert(CONS_WARNING, "Some unloaded cup standings data has been dropped due to datatype limitations.\n"); + break; + } + } + + length += 4 + (numgamedatacups * (MAXCUPNAME + 4*(1+4))); + if (P_SaveBufferAlloc(&save, length) == false) { @@ -5141,7 +5457,7 @@ void G_SaveGameData(void) WRITEUINT32(save.p, gamedata->pendingkeyrounds); // 4 WRITEUINT8(save.p, gamedata->pendingkeyroundoffset); // 1 - WRITEUINT8(save.p, gamedata->keyspending); // 1 + WRITEUINT16(save.p, gamedata->keyspending); // 2 WRITEUINT16(save.p, gamedata->chaokeys); // 2 WRITEUINT8(save.p, gamedata->everloadedaddon); // 1 @@ -5187,12 +5503,12 @@ void G_SaveGameData(void) i += j; } - if (gamedata->challengegrid) // 2 + gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT + if (gamedata->challengegrid) // 2 + (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT) * 2 { WRITEUINT16(save.p, gamedata->challengegridwidth); for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++) { - WRITEUINT8(save.p, gamedata->challengegrid[i]); + WRITEUINT16(save.p, gamedata->challengegrid[i]); } } else // 2 @@ -5203,45 +5519,161 @@ void G_SaveGameData(void) WRITEUINT32(save.p, gamedata->timesBeaten); // 4 // Main records - WRITEUINT32(save.p, nummapheaders); // 4 - for (i = 0; i < nummapheaders; i++) // nummapheaders * (255+1+4+4) + WRITEUINT32(save.p, numgamedataskins); // 4 + { - // For figuring out which header to assign it to on load - WRITESTRINGN(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME); + // numgamedataskins * (SKINNAMESIZE+4) - WRITEUINT8(save.p, (mapheaderinfo[i]->mapvisited & MV_MAX)); + UINT32 maxid = 0; - if (mapheaderinfo[i]->mainrecord) + for (i = 0; i < numskins; i++) { - WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->time); - WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->lap); + if (skins[i].records.wins == 0) + continue; + + WRITESTRINGN(save.p, skins[i].name, SKINNAMESIZE); + + WRITEUINT32(save.p, skins[i].records.wins); + + skins[i].records._saveid = maxid; + if (++maxid == numgamedataskins) + break; } - else + + if (maxid < numgamedataskins) { - WRITEUINT32(save.p, 0); - WRITEUINT32(save.p, 0); + for (unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next) + { + if (unloadedskin->records.wins == 0) + continue; + + WRITESTRINGN(save.p, unloadedskin->name, SKINNAMESIZE); + + WRITEUINT32(save.p, unloadedskin->records.wins); + + unloadedskin->records._saveid = maxid; + if (++maxid == numgamedataskins) + break; + } } } - WRITEUINT32(save.p, numcups); // 4 +#define GETSKINREFSAVEID(ref, var) \ + { \ + if (ref.unloaded != NULL) \ + var = ref.unloaded->records._saveid;\ + else if (ref.id < numskins)\ + var = skins[ref.id].records._saveid; \ + else \ + var = UINT32_MAX; \ + } - for (cup = kartcupheaders; cup; cup = cup->next) + WRITEUINT32(save.p, numgamedatamapheaders); // 4 + + if (numgamedatamapheaders) { - // For figuring out which header to assign it to on load - WRITESTRINGN(save.p, cup->name, 16); + // numgamedatamapheaders * (MAXMAPLUMPNAME+1+4+4) - for (i = 0; i < KARTGP_MAX; i++) + for (i = 0; i < nummapheaders; i++) { - btemp = min(cup->windata[i].best_placement, 0x0F); - btemp |= (cup->windata[i].best_grade<<4); - if (i != 0 && cup->windata[i].got_emerald == true) - btemp |= 0x80; + if (!(mapheaderinfo[i]->records.mapvisited & MV_MAX)) + continue; - WRITEUINT8(save.p, btemp); // 4 * numcups + WRITESTRINGL(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME); + + UINT8 mapvisitedtemp = (mapheaderinfo[i]->records.mapvisited & MV_MAX); + + if ((mapheaderinfo[i]->menuflags & LF2_FINISHNEEDED)) + { + mapvisitedtemp |= MV_FINISHNEEDED; + } + + WRITEUINT8(save.p, mapvisitedtemp); + + WRITEUINT32(save.p, mapheaderinfo[i]->records.time); + WRITEUINT32(save.p, mapheaderinfo[i]->records.lap); + + if (--numgamedatamapheaders == 0) + break; + } + + if (numgamedatamapheaders) + { + for (unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next) + { + if (!(unloadedmap->records.mapvisited & MV_MAX)) + continue; + + WRITESTRINGL(save.p, unloadedmap->lumpname, MAXMAPLUMPNAME); + + WRITEUINT8(save.p, unloadedmap->records.mapvisited); + + WRITEUINT32(save.p, unloadedmap->records.time); + WRITEUINT32(save.p, unloadedmap->records.lap); + + if (--numgamedatamapheaders == 0) + break; + } } } + + WRITEUINT32(save.p, numgamedatacups); // 4 + + if (numgamedatacups) + { + // numgamedatacups * (MAXCUPNAME + 4*(1+4)) + +#define WRITECUPWINDATA(maybeunloadedcup) \ + for (i = 0; i < KARTGP_MAX; i++) \ + { \ + btemp = min(maybeunloadedcup->windata[i].best_placement, 0x0F); \ + btemp |= (maybeunloadedcup->windata[i].best_grade<<4); \ + if (maybeunloadedcup->windata[i].got_emerald == true) \ + btemp |= 0x80; \ + \ + WRITEUINT8(save.p, btemp); \ + \ + GETSKINREFSAVEID(maybeunloadedcup->windata[i].best_skin, j); \ + \ + WRITEUINT32(save.p, j); \ + } + + for (cup = kartcupheaders; cup; cup = cup->next) + { + if (cup->windata[0].best_placement == 0) + continue; + + WRITESTRINGL(save.p, cup->name, MAXCUPNAME); + + WRITECUPWINDATA(cup); + + if (--numgamedatacups == 0) + break; + } + + if (numgamedatacups) + { + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next) + { + if (unloadedcup->windata[0].best_placement == 0) + continue; + + WRITESTRINGL(save.p, unloadedcup->name, MAXCUPNAME); + + WRITECUPWINDATA(unloadedcup); + + if (--numgamedatacups == 0) + break; + } + } + +#undef WRITECUPWINDATA + } + +#undef GETSKINREFSAVEID + length = save.p - save.buffer; FIL_WriteFile(va(pandf, srb2home, gamedatafilename), save.buffer, length); diff --git a/src/g_game.h b/src/g_game.h index e7e028250..2f498134e 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -264,7 +264,6 @@ void G_SetGameModified(boolean silent, boolean major); void G_SetUsedCheats(void); // Gamedata record shit -void G_AllocMainRecordData(INT16 i); void G_ClearRecords(void); tic_t G_GetBestTime(INT16 map); diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 26d342b1b..8772e152a 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -570,17 +570,15 @@ void HWR_InitModels(void) addskinmodel: // add player model - for (s = 0; s < MAXSKINS; s++) + s = R_SkinAvailable(skinname); + if (s != -1) { - if (stricmp(skinname, skins[s].name) == 0) - { - md2_playermodels[s].skin = s; - md2_playermodels[s].scale = scale; - md2_playermodels[s].offset = offset; - md2_playermodels[s].notfound = false; - strcpy(md2_playermodels[s].filename, filename); - goto modelfound; - } + md2_playermodels[s].skin = s; + md2_playermodels[s].scale = scale; + md2_playermodels[s].offset = offset; + md2_playermodels[s].notfound = false; + strcpy(md2_playermodels[s].filename, filename); + goto modelfound; } // no sprite/player skin name found?!?D diff --git a/src/k_follower.c b/src/k_follower.c index daa7b9d0d..48b9646e2 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -48,7 +48,7 @@ boolean K_FollowerUsable(INT32 skinnum) { // Unlike R_SkinUsable, not netsynced. // Solely used to prevent an invalid value being sent over the wire. - UINT8 i; + UINT16 i; INT32 fid; if (skinnum == -1 || demo.playback) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 151ab7724..dff88ceab 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -815,10 +815,13 @@ void K_PlayerFinishGrandPrix(player_t *player) grandprixinfo.wonround = true; // Increase your total rings - if (RINGTOTAL(player) > 0) + INT32 ringtotal = RINGTOTAL(player); + if (ringtotal > 0) { - player->totalring += RINGTOTAL(player); - grandprixinfo.rank.rings += RINGTOTAL(player); + if (ringtotal > 20) + ringtotal = 20; + player->totalring += ringtotal; + grandprixinfo.rank.rings += ringtotal; } if (grandprixinfo.eventmode == GPEVENT_NONE) diff --git a/src/k_hud.c b/src/k_hud.c index 0ce3ccbda..995877f8d 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -81,7 +81,7 @@ static patch_t *kp_nocontestminimap; static patch_t *kp_spbminimap; static patch_t *kp_wouldyoustillcatchmeifiwereaworm; static patch_t *kp_catcherminimap; -static patch_t *kp_emeraldminimap; +static patch_t *kp_emeraldminimap[2]; static patch_t *kp_capsuleminimap[3]; static patch_t *kp_ringsticker[2]; @@ -358,7 +358,8 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_wouldyoustillcatchmeifiwereaworm, "MINIPROG"); HU_UpdatePatch(&kp_catcherminimap, "UFOMAP"); - HU_UpdatePatch(&kp_emeraldminimap, "EMEMAP"); + HU_UpdatePatch(&kp_emeraldminimap[0], "EMEMAP"); + HU_UpdatePatch(&kp_emeraldminimap[1], "SUPMAP"); HU_UpdatePatch(&kp_capsuleminimap[0], "MINICAP1"); HU_UpdatePatch(&kp_capsuleminimap[1], "MINICAP2"); @@ -3880,7 +3881,11 @@ static void K_drawKartMinimap(void) } else { - workingPic = kp_emeraldminimap; + UINT8 emid = 0; + if (specialstageinfo.ufo->cvmem > 7) + emid = 1; + workingPic = kp_emeraldminimap[emid]; + if (specialstageinfo.ufo->color) { colormap = R_GetTranslationColormap(TC_DEFAULT, specialstageinfo.ufo->color, GTC_CACHE); diff --git a/src/k_menu.h b/src/k_menu.h index d461973b4..09cfbf772 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1189,13 +1189,13 @@ extern struct challengesmenu_s { tic_t ticker; // How long the menu's been open for INT16 offset; // To make the icons move smoothly when we transition! - UINT8 currentunlock; + UINT16 currentunlock; char *unlockcondition; tic_t unlockanim; - SINT8 row, hilix, focusx; - UINT8 col, hiliy; + INT16 row, hilix, focusx; + UINT16 col, hiliy; challengegridextradata_t *extradata; @@ -1205,7 +1205,7 @@ extern struct challengesmenu_s { boolean requestflip; - UINT8 unlockcount[CC_MAX]; + UINT16 unlockcount[CC_MAX]; UINT8 fade; } challengesmenu; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8c99f5e31..174868fbf 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -51,6 +51,7 @@ #include "d_player.h" // KITEM_ constants #include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "k_grandprix.h" // K_CanChangeRules +#include "k_rank.h" // K_GetGradeColor #include "y_inter.h" // Y_RoundQueueDrawer @@ -2298,7 +2299,6 @@ void M_DrawCupSelect(void) INT16 icony = 7; char status = 'A'; char monitor; - INT32 rankx = 0; if (!cupgrid.builtgrid[id]) break; @@ -2353,7 +2353,6 @@ void M_DrawCupSelect(void) if (monitor == '2') { icony = 5; - rankx = 2; } } else @@ -2385,43 +2384,94 @@ void M_DrawCupSelect(void) ; else if (windata->best_placement != 0) { - char gradeChar = '?'; + const INT32 rankw = 14 + 12 + 12 + 2; + INT32 rankx = (x + 19) - (rankw / 2); + const INT32 ranky = 8 + (j*100) - (30*menutransition.tics); - switch (windata->best_grade) + patch_t *gradePat = NULL; + colormap = NULL; + + const gp_rank_e grade = windata->best_grade; // (cupgrid.previewanim/TICRATE) % (GRADE_S + 1); -- testing + UINT16 gradecolor = K_GetGradeColor(grade); + + if (gradecolor != SKINCOLOR_NONE) + colormap = R_GetTranslationColormap(TC_DEFAULT, gradecolor, GTC_MENUCACHE); + + switch (grade) { - case GRADE_E: { gradeChar = 'E'; break; } - case GRADE_D: { gradeChar = 'D'; break; } - case GRADE_C: { gradeChar = 'C'; break; } - case GRADE_B: { gradeChar = 'B'; break; } - case GRADE_A: { gradeChar = 'A'; break; } - case GRADE_S: { gradeChar = 'S'; break; } - default: { break; } + case GRADE_E: + gradePat = W_CachePatchName("R_CUPRNE", PU_CACHE); + break; + case GRADE_D: + gradePat = W_CachePatchName("R_CUPRND", PU_CACHE); + break; + case GRADE_C: + gradePat = W_CachePatchName("R_CUPRNC", PU_CACHE); + break; + case GRADE_B: + gradePat = W_CachePatchName("R_CUPRNB", PU_CACHE); + break; + case GRADE_A: + gradePat = W_CachePatchName("R_CUPRNA", PU_CACHE); + break; + case GRADE_S: + gradePat = W_CachePatchName("R_CUPRNS", PU_CACHE); + break; + default: + break; } - V_DrawCharacter(x + 5 + rankx, y + icony + 14, gradeChar, false); // rank + if (gradePat) + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, gradePat, colormap); - if (windata->got_emerald == true) + rankx += 14 + 1; + + patch_t *charPat = NULL; + + if ((windata->best_skin.unloaded != NULL) + || (windata->best_skin.id > numskins)) { + colormap = NULL; + + charPat = W_CachePatchName("HUHMAP", PU_CACHE); + } + else + { + UINT8 skin = windata->best_skin.id; + + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + + charPat = faceprefix[skin][FACE_MINIMAP]; + } + + if (charPat) + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, charPat, colormap); + + if (cv_dummygpdifficulty.value > 0 + && windata->got_emerald == true) + { + rankx += 12 + 1; + if (templevelsearch.cup->emeraldnum == 0) - V_DrawCharacter(x + 26 - rankx, y + icony + 14, '*', false); // rank + V_DrawCharacter(rankx+2, ranky+2, '+', false); else { - UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (templevelsearch.cup->emeraldnum-1) % 7; - patch_t *em; + colormap = NULL; - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + if (!(cupgrid.previewanim & 1)) + { + UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (templevelsearch.cup->emeraldnum-1) % 7; - if (templevelsearch.cup->emeraldnum > 7) - { - em = W_CachePatchName("K_SUPER1", PU_CACHE); - rankx += 2; - } - else - { - em = W_CachePatchName("K_EMERC", PU_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); } - V_DrawFixedPatch((x + 26 - rankx)*FRACUNIT, (y + icony + 13)*FRACUNIT, FRACUNIT, 0, em, colormap); + const char *emname = va( + "%sMAP%c", + (templevelsearch.cup->emeraldnum > 7) ? "SUP" : "EME", + colormap ? '\0' : 'B' + ); + + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, W_CachePatchName(emname, PU_CACHE), colormap); } } } @@ -2640,11 +2690,8 @@ void M_DrawTimeAttack(void) if ((minimap = mapheaderinfo[map]->minimapPic)) V_DrawScaledPatch(24-t, 82, 0, minimap); - if (mapheaderinfo[map]->mainrecord) - { - timerec = mapheaderinfo[map]->mainrecord->time; - laprec = mapheaderinfo[map]->mainrecord->lap; - } + timerec = mapheaderinfo[map]->records.time; + laprec = mapheaderinfo[map]->records.lap; if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT) && (mapheaderinfo[map]->numlaps != 1)) @@ -4613,7 +4660,7 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili patch_t *pat = missingpat; UINT8 *colormap = NULL, *bgmap = NULL; fixed_t siz, accordion; - UINT8 id, num; + UINT16 id, num; boolean unlockedyet; boolean categoryside; @@ -5779,13 +5826,13 @@ void M_DrawStatistics(void) if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))) continue; - if (!mapheaderinfo[i]->mainrecord || mapheaderinfo[i]->mainrecord->time <= 0) + if (mapheaderinfo[i]->records.time <= 0) { mapsunfinished++; continue; } - besttime += mapheaderinfo[i]->mainrecord->time; + besttime += mapheaderinfo[i]->records.time; } V_DrawThinString(20, 60, V_6WIDTHSPACE|V_ALLOWLOWERCASE, "Combined time records:"); diff --git a/src/k_podium.c b/src/k_podium.c index f21196fd3..ae1d45eef 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -303,7 +303,7 @@ void K_FinishCeremony(void) --------------------------------------------------*/ void K_ResetCeremony(void) { - UINT8 i; + SINT8 i; memset(&podiumData, 0, sizeof(struct podiumData_s)); @@ -324,22 +324,43 @@ void K_ResetCeremony(void) // Write grade, position, and emerald-having-ness for later sessions! i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed; - if ((grandprixinfo.cup->windata[i].best_placement == 0) // First run - || (podiumData.rank.position < grandprixinfo.cup->windata[i].best_placement)) // Later, better run + // All results populate downwards in difficulty. This prevents someone + // who's just won on Normal from feeling obligated to complete Easy too. + for (; i >= 0; i--) { - grandprixinfo.cup->windata[i].best_placement = podiumData.rank.position; + boolean anymerit = false; - // The following will not occour in unmodified builds, but pre-emptively sanitise gamedata if someone just changes MAXPLAYERS and calls it a day - if (grandprixinfo.cup->windata[i].best_placement > 0x0F) - grandprixinfo.cup->windata[i].best_placement = 0x0F; + if ((grandprixinfo.cup->windata[i].best_placement == 0) // First run + || (podiumData.rank.position <= grandprixinfo.cup->windata[i].best_placement)) // Later, better run + { + grandprixinfo.cup->windata[i].best_placement = podiumData.rank.position; + + // The following will not occur in unmodified builds, but pre-emptively sanitise gamedata if someone just changes MAXPLAYERS and calls it a day + if (grandprixinfo.cup->windata[i].best_placement > 0x0F) + grandprixinfo.cup->windata[i].best_placement = 0x0F; + + anymerit = true; + } + + if (podiumData.grade >= grandprixinfo.cup->windata[i].best_grade) + { + grandprixinfo.cup->windata[i].best_grade = podiumData.grade; + anymerit = true; + } + + if (podiumData.rank.specialWon == true) + { + grandprixinfo.cup->windata[i].got_emerald = true; + anymerit = true; + } + + if (anymerit == true) + { + grandprixinfo.cup->windata[i].best_skin.id = podiumData.rank.skin; + grandprixinfo.cup->windata[i].best_skin.unloaded = NULL; + } } - if (podiumData.grade > grandprixinfo.cup->windata[i].best_grade) - grandprixinfo.cup->windata[i].best_grade = podiumData.grade; - - if (i != KARTSPEED_EASY && podiumData.rank.specialWon == true) - grandprixinfo.cup->windata[i].got_emerald = true; - // Save before playing the noise G_SaveGameData(); } diff --git a/src/k_profiles.c b/src/k_profiles.c index e536f95e3..48cf6472d 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -329,6 +329,11 @@ void PR_LoadProfiles(void) P_SaveBufferFree(&save); I_Error("Existing %s is from the future! (expected %d, got %d)", PROFILESFILE, PROFILEVER, version); } + else if (version < PROFILEVER) + { + // We're converting - let'd create a backup. + FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, PROFILESFILE), save.buffer, save.size); + } numprofiles = READUINT8(save.p); if (numprofiles > MAXPROFILES) diff --git a/src/k_rank.c b/src/k_rank.c index 61c5a49c6..1420cc7b3 100644 --- a/src/k_rank.c +++ b/src/k_rank.c @@ -417,3 +417,31 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) return retGrade; } + +/*-------------------------------------------------- + UINT16 K_GetGradeColor(gp_rank_e grade) + + See header file for description. +--------------------------------------------------*/ +UINT16 K_GetGradeColor(gp_rank_e grade) +{ + switch (grade) + { + case GRADE_E: + return SKINCOLOR_BLUE; + case GRADE_D: + return SKINCOLOR_TURTLE; + case GRADE_C: + return SKINCOLOR_ORANGE; + case GRADE_B: + return SKINCOLOR_RED; + case GRADE_A: + return SKINCOLOR_MAGENTA; + case GRADE_S: + return SKINCOLOR_PIGEON; + default: + break; + } + + return SKINCOLOR_NONE; +} diff --git a/src/k_rank.h b/src/k_rank.h index eb2c420cd..fd3e86f97 100644 --- a/src/k_rank.h +++ b/src/k_rank.h @@ -26,6 +26,7 @@ struct gpRank_t UINT8 totalPlayers; UINT8 position; + UINT8 skin; UINT32 winPoints; UINT32 totalPoints; @@ -81,6 +82,20 @@ void K_InitGrandPrixRank(gpRank_t *rankData); gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); +/*-------------------------------------------------- + UINT16 K_GetGradeColor(gp_rank_e grade) + + Maps grades to skincolors for HUD purposes. + + Input Arguments:- + grade - gp_rank_e representing an achieved ranking. + + Return:- + skincolor ID representing the achieved grade. +--------------------------------------------------*/ +UINT16 K_GetGradeColor(gp_rank_e grade); + + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 77ba7bb4a..3bdd1d7d6 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -98,7 +98,16 @@ void SV_LoadStats(void) save.p += headerlen; UINT8 version = READUINT8(save.p); - (void)version; // for now + if (version > SERVERSTATSVER) + { + P_SaveBufferFree(&save); + I_Error("Existing %s is from the future! (expected %d, got %d)", SERVERSTATSFILE, SERVERSTATSVER, version); + } + else if (version < SERVERSTATSVER) + { + // We're converting - let'd create a backup. + FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, SERVERSTATSFILE), save.buffer, save.size); + } numtracked = READUINT32(save.p); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index aab85e26c..f99f33f6b 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -386,10 +386,8 @@ static int libd_getSprite2Patch(lua_State *L) else // find skin by name { const char *name = luaL_checkstring(L, 1); - for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, name)) - break; - if (i >= numskins) + i = R_SkinAvailable(name); + if (i == -1) return 0; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 835e2bb4d..1ae7a6a78 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -662,17 +662,16 @@ static int mobj_set(lua_State *L) break; case mobj_skin: // set skin by name { - INT32 i; - char skin[SKINNAMESIZE+1]; // all skin names are limited to this length - strlcpy(skin, luaL_checkstring(L, 3), sizeof skin); - strlwr(skin); // all skin names are lowercase - for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, skin)) - { - if (!mo->player || R_SkinUsable(mo->player-players, i, false)) - mo->skin = &skins[i]; - return 0; - } + const char *name = luaL_checkstring(L, 3); + INT32 skin = R_SkinAvailable(name); + + if (skin != -1) + { + if (!mo->player || R_SkinUsable(mo->player-players, skin, false)) + mo->skin = &skins[skin]; + + return 0; + } return luaL_error(L, "mobj.skin '%s' not found!", skin); } case mobj_color: diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 6c4089de2..2950af461 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -196,12 +196,12 @@ static int lib_getSkin(lua_State *L) } // find skin by name - for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, field)) - { - LUA_PushUserdata(L, &skins[i], META_SKIN); - return 1; - } + i = R_SkinAvailable(field); + if (i != -1) + { + LUA_PushUserdata(L, &skins[i], META_SKIN); + return 1; + } return 0; } diff --git a/src/m_cheat.c b/src/m_cheat.c index e4aec3b41..72ffd6523 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -62,7 +62,7 @@ typedef struct // Cheat responders static UINT8 cheatf_warp(void) { - UINT8 i; + UINT16 i; boolean success = false; /*if (modifiedgame) @@ -98,7 +98,7 @@ static UINT8 cheatf_warp(void) #ifdef DEVELOP static UINT8 cheatf_devmode(void) { - UINT8 i; + UINT16 i; if (modifiedgame) return 0; diff --git a/src/m_cond.c b/src/m_cond.c index 5b551f646..728185978 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -60,7 +60,7 @@ void M_PopulateChallengeGrid(void) { UINT16 i, j; UINT16 numunlocks = 0, nummajorunlocks = 0, numempty = 0; - UINT8 selection[2][MAXUNLOCKABLES + (CHALLENGEGRIDHEIGHT-1)]; + UINT16 selection[2][MAXUNLOCKABLES + (CHALLENGEGRIDHEIGHT-1)]; UINT16 majorcompact = 2; if (gamedata->challengegrid != NULL) @@ -98,7 +98,7 @@ void M_PopulateChallengeGrid(void) if (nummajorunlocks) { // Getting the number of 2-highs you can fit into two adjacent columns. - UINT8 majorpad = (CHALLENGEGRIDHEIGHT/2); + UINT16 majorpad = (CHALLENGEGRIDHEIGHT/2); numempty = nummajorunlocks%majorpad; majorpad = (nummajorunlocks+(majorpad-1))/majorpad; @@ -128,7 +128,7 @@ void M_PopulateChallengeGrid(void) } gamedata->challengegrid = Z_Malloc( - (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8)), + (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT16)), PU_STATIC, NULL); if (!gamedata->challengegrid) @@ -136,9 +136,10 @@ void M_PopulateChallengeGrid(void) I_Error("M_PopulateChallengeGrid: was not able to allocate grid"); } - memset(gamedata->challengegrid, - MAXUNLOCKABLES, - (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT8))); + for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); ++i) + { + gamedata->challengegrid[i] = MAXUNLOCKABLES; + } // Attempt to place all large tiles first. if (nummajorunlocks) @@ -212,7 +213,7 @@ quickcheckagain: #if (CHALLENGEGRIDHEIGHT == 4) while (nummajorunlocks > 0) { - UINT8 unlocktomoveup = MAXUNLOCKABLES; + UINT16 unlocktomoveup = MAXUNLOCKABLES; j = gamedata->challengegridwidth-1; @@ -313,7 +314,7 @@ quickcheckagain: void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata) { - UINT8 i, j, num, id, tempid, work; + UINT16 i, j, num, id, tempid, work; boolean idchange; if (gamedata->challengegrid == NULL) @@ -478,7 +479,7 @@ void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata) } } -void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar) +void M_AddRawCondition(UINT16 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar) { condition_t *cond; UINT32 num, wnum; @@ -500,7 +501,7 @@ void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1 cond[wnum].stringvar = stringvar; } -void M_ClearConditionSet(UINT8 set) +void M_ClearConditionSet(UINT16 set) { if (conditionSets[set].numconditions) { @@ -531,17 +532,14 @@ void M_ClearStats(void) gamedata->everseenspecial = false; gamedata->evercrashed = false; gamedata->musicflags = 0; + + gamedata->importprofilewins = false; } void M_ClearSecrets(void) { INT32 i; - for (i = 0; i < nummapheaders; ++i) - { - mapheaderinfo[i]->mapvisited = 0; - } - for (i = 0; i < MAXEMBLEMS; ++i) gamedata->collected[i] = false; for (i = 0; i < MAXUNLOCKABLES; ++i) @@ -702,7 +700,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return ((cn->requirement < nummapheaders) && (mapheaderinfo[cn->requirement]) - && ((mapheaderinfo[cn->requirement]->mapvisited & mvtype) == mvtype)); + && ((mapheaderinfo[cn->requirement]->records.mapvisited & mvtype) == mvtype)); } case UC_MAPTIME: // Requires time on map <= x return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement); @@ -932,7 +930,7 @@ static char *M_BuildConditionTitle(UINT16 map) if (((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED) // the following is intentionally not MV_BEATEN, just in case the title is for "Finish a round on X" - && !(mapheaderinfo[map]->mapvisited & MV_VISITED)) + && !(mapheaderinfo[map]->records.mapvisited & MV_VISITED)) || M_MapLocked(map+1)) return Z_StrDup("???"); @@ -1398,7 +1396,7 @@ static const char *M_GetConditionString(condition_t *cn) #undef BUILDCONDITIONTITLE } -char *M_BuildConditionSetString(UINT8 unlockid) +char *M_BuildConditionSetString(UINT16 unlockid) { conditionset_t *c = NULL; UINT32 lastID = 0; @@ -1654,7 +1652,7 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall) UINT16 M_GetNextAchievedUnlock(void) { - UINT8 i; + UINT16 i; // Go through unlockables for (i = 0; i < MAXUNLOCKABLES; ++i) @@ -1686,14 +1684,14 @@ UINT16 M_GetNextAchievedUnlock(void) } // Emblem unlocking shit -UINT8 M_CheckLevelEmblems(void) +UINT16 M_CheckLevelEmblems(void) { INT32 i; INT32 valToReach; INT16 tag; INT16 levelnum; - UINT8 res; - UINT8 somethingUnlocked = 0; + boolean res; + UINT16 somethingUnlocked = 0; // Update Score, Time, Rings emblems for (i = 0; i < numemblems; ++i) @@ -1739,13 +1737,13 @@ UINT8 M_CheckLevelEmblems(void) return somethingUnlocked; } -UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough. +UINT16 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough. { INT32 i; INT32 embtype; INT16 levelnum; - UINT8 res; - UINT8 somethingUnlocked = 0; + boolean res; + UINT16 somethingUnlocked = 0; UINT8 flags; for (i = 0; i < numemblems; ++i) @@ -1770,7 +1768,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa if (embtype & ME_SPBATTACK) flags |= MV_SPBATTACK; - res = ((mapheaderinfo[levelnum]->mapvisited & flags) == flags); + res = ((mapheaderinfo[levelnum]->records.mapvisited & flags) == flags); gamedata->collected[i] = res; if (res) @@ -1784,7 +1782,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa // Quick unlock checks // ------------------- -boolean M_CheckNetUnlockByID(UINT8 unlockid) +boolean M_CheckNetUnlockByID(UINT16 unlockid) { if (unlockid >= MAXUNLOCKABLES || !unlockables[unlockid].conditionset) @@ -1827,7 +1825,7 @@ boolean M_SecretUnlocked(INT32 type, boolean local) boolean M_CupLocked(cupheader_t *cup) { - UINT8 i; + UINT16 i; // Don't lock maps in dedicated servers. // That just makes hosts' lives hell. @@ -1855,7 +1853,7 @@ boolean M_CupLocked(cupheader_t *cup) boolean M_MapLocked(UINT16 mapnum) { - UINT8 i; + UINT16 i; // Don't lock maps in dedicated servers. // That just makes hosts' lives hell. @@ -1921,7 +1919,7 @@ INT32 M_CountMedals(boolean all, boolean extraonly) // Theoretically faster than using M_CountMedals() // Stops when it reaches the target number of medals. -UINT8 M_GotEnoughMedals(INT32 number) +boolean M_GotEnoughMedals(INT32 number) { INT32 i, gottenmedals = 0; for (i = 0; i < numemblems; ++i) @@ -1945,7 +1943,7 @@ UINT8 M_GotEnoughMedals(INT32 number) return false; } -UINT8 M_GotLowEnoughTime(INT32 tictime) +boolean M_GotLowEnoughTime(INT32 tictime) { INT32 curtics = 0; INT32 i; @@ -1955,9 +1953,9 @@ UINT8 M_GotLowEnoughTime(INT32 tictime) if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK)) continue; - if (!mapheaderinfo[i]->mainrecord || !mapheaderinfo[i]->mainrecord->time) + if (!mapheaderinfo[i]->records.time) return false; - else if ((curtics += mapheaderinfo[i]->mainrecord->time) > tictime) + if ((curtics += mapheaderinfo[i]->records.time) > tictime) return false; } return true; @@ -2064,9 +2062,10 @@ cupheader_t *M_UnlockableCup(unlockable_t *unlock) if (unlock->stringVarCache == -1) { // Get the cup from the string. + UINT32 hash = quickncasehash(unlock->stringVar, MAXCUPNAME); while (cup) { - if (!strcmp(cup->name, unlock->stringVar)) + if (hash == cup->namehash && !strcmp(cup->name, unlock->stringVar)) break; cup = cup->next; } diff --git a/src/m_cond.h b/src/m_cond.h index 9fdb5bd2e..fb96a14b7 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -159,7 +159,7 @@ struct unlockable_t char name[64]; char *icon; UINT16 color; - UINT8 conditionset; + UINT16 conditionset; INT16 type; INT16 variable; char *stringVar; @@ -206,8 +206,8 @@ typedef enum // If you have more secrets than these variables allow in your game, // you seriously need to get a life. -#define MAXCONDITIONSETS UINT8_MAX -#define MAXEMBLEMS 512 +#define MAXCONDITIONSETS 1024 +#define MAXEMBLEMS (MAXCONDITIONSETS*2) #define MAXUNLOCKABLES MAXCONDITIONSETS #define CHALLENGEGRIDHEIGHT 4 @@ -260,7 +260,7 @@ struct gamedata_t // CHALLENGE GRID UINT16 challengegridwidth; - UINT8 *challengegrid; + UINT16 *challengegrid; // # OF TIMES THE GAME HAS BEEN BEATEN UINT32 timesBeaten; @@ -273,7 +273,7 @@ struct gamedata_t // Chao Key condition bypass UINT32 pendingkeyrounds; UINT8 pendingkeyroundoffset; - UINT8 keyspending; + UINT16 keyspending; UINT16 chaokeys; // SPECIFIC SPECIAL EVENTS @@ -282,6 +282,9 @@ struct gamedata_t boolean everseenspecial; boolean evercrashed; UINT8 musicflags; + + // BACKWARDS COMPAT ASSIST + boolean importprofilewins; }; extern gamedata_t *gamedata; @@ -314,15 +317,15 @@ void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata); #define CHE_CONNECTEDUP (1<<2) #define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP) -char *M_BuildConditionSetString(UINT8 unlockid); +char *M_BuildConditionSetString(UINT16 unlockid); #define DESCRIPTIONWIDTH 170 // Condition set setup -void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar); +void M_AddRawCondition(UINT16 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar); void M_UpdateConditionSetsPending(void); // Clearing secrets -void M_ClearConditionSet(UINT8 set); +void M_ClearConditionSet(UINT16 set); void M_ClearSecrets(void); void M_ClearStats(void); @@ -335,11 +338,11 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall); #define PENDING_CHAOKEYS (UINT16_MAX-1) UINT16 M_GetNextAchievedUnlock(void); -UINT8 M_CheckLevelEmblems(void); -UINT8 M_CompletionEmblems(void); +UINT16 M_CheckLevelEmblems(void); +UINT16 M_CompletionEmblems(void); // Checking unlockable status -boolean M_CheckNetUnlockByID(UINT8 unlockid); +boolean M_CheckNetUnlockByID(UINT16 unlockid); boolean M_SecretUnlocked(INT32 type, boolean local); boolean M_CupLocked(cupheader_t *cup); boolean M_MapLocked(UINT16 mapnum); @@ -353,8 +356,8 @@ const char *M_GetEmblemPatch(emblem_t *em, boolean big); // If you're looking to compare stats for unlocks or what not, use these // They stop checking upon reaching the target number so they // should be (theoretically?) slightly faster. -UINT8 M_GotEnoughMedals(INT32 number); -UINT8 M_GotLowEnoughTime(INT32 tictime); +boolean M_GotEnoughMedals(INT32 number); +boolean M_GotLowEnoughTime(INT32 tictime); INT32 M_UnlockableSkinNum(unlockable_t *unlock); INT32 M_UnlockableFollowerNum(unlockable_t *unlock); diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index c7d6674e1..eb49b762a 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -53,8 +53,8 @@ struct challengesmenu_s challengesmenu; static void M_ChallengesAutoFocus(UINT16 unlockid, boolean fresh) { - UINT8 i; - SINT8 work; + UINT16 i; + INT16 work; if (unlockid >= MAXUNLOCKABLES && gamedata->pendingkeyrounds > 0 && (gamedata->chaokeys < GDMAX_CHAOKEYS)) @@ -62,8 +62,8 @@ static void M_ChallengesAutoFocus(UINT16 unlockid, boolean fresh) if (fresh && unlockid >= MAXUNLOCKABLES) { - UINT8 selection[MAXUNLOCKABLES]; - UINT8 numunlocks = 0; + UINT16 selection[MAXUNLOCKABLES]; + UINT16 numunlocks = 0; // Get a random available unlockable. for (i = 0; i < MAXUNLOCKABLES; i++) @@ -512,7 +512,7 @@ void M_ChallengesTick(void) boolean M_ChallengesInputs(INT32 ch) { const UINT8 pid = 0; - UINT8 i; + UINT16 i; const boolean start = M_MenuButtonPressed(pid, MBT_START); const boolean move = (menucmd[pid].dpad_ud != 0 || menucmd[pid].dpad_lr != 0); (void) ch; diff --git a/src/menus/extras-statistics.c b/src/menus/extras-statistics.c index 356bf46b2..bc4843b27 100644 --- a/src/menus/extras-statistics.c +++ b/src/menus/extras-statistics.c @@ -26,7 +26,7 @@ static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headere // Check for completion if ((mapheaderinfo[map]->menuflags & LF2_FINISHNEEDED) - && !(mapheaderinfo[map]->mapvisited & MV_BEATEN)) + && !(mapheaderinfo[map]->records.mapvisited & MV_BEATEN)) return false; // Check for unlock diff --git a/src/menus/options-data-erase-1.c b/src/menus/options-data-erase-1.c index 2af6f0ffb..88d5b474b 100644 --- a/src/menus/options-data-erase-1.c +++ b/src/menus/options-data-erase-1.c @@ -19,7 +19,7 @@ menuitem_t OPTIONS_DataErase[] = {IT_STRING | IT_CALL, "Erase Statistics Data", "Be careful! What's deleted is gone forever!", NULL, {.routine = M_EraseData}, EC_STATISTICS, 0}, - {IT_STRING | IT_CALL, "Erase GP & Time Attack Data", "Be careful! What's deleted is gone forever!", + {IT_STRING | IT_CALL, "Erase GP & Record Data", "Be careful! What's deleted is gone forever!", NULL, {.routine = M_EraseData}, EC_TIMEATTACK, 0}, {IT_STRING | IT_CALL, "\x85\x45rase all Game Data", "Be careful! What's deleted is gone forever!", @@ -89,7 +89,7 @@ void M_EraseData(INT32 choice) else if (optionsmenu.erasecontext == EC_STATISTICS) eschoice = M_GetText("Statistics data"); else if (optionsmenu.erasecontext == EC_TIMEATTACK) - eschoice = M_GetText("GP & Time Attack data"); + eschoice = M_GetText("GP & Record data"); else if (optionsmenu.erasecontext == EC_ALLGAME) eschoice = M_GetText("ALL game data"); else diff --git a/src/menus/play-char-select.c b/src/menus/play-char-select.c index c21990ba4..0110870d9 100644 --- a/src/menus/play-char-select.c +++ b/src/menus/play-char-select.c @@ -404,6 +404,8 @@ void M_CharacterSelectInit(void) memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; + UINT32 localskinhash[MAXSPLITSCREENPLAYERS]; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { // Default to no follower / match colour. @@ -414,6 +416,9 @@ void M_CharacterSelectInit(void) // Set default selected profile to the last used profile for each player: // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) setup_player[i].profilen = min(cv_lastprofile[i].value, PR_GetNumProfiles()); + + // Assistant for comparisons. + localskinhash[i] = quickncasehash(cv_skin[i].string, SKINNAMESIZE); } for (i = 0; i < numskins; i++) @@ -436,7 +441,14 @@ void M_CharacterSelectInit(void) for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) { - if (!strcmp(cv_skin[j].string, skins[i].name)) + // See also R_SkinAvailable + + if (localskinhash[j] != skins[i].namehash) + continue; + + if (!stricmp(cv_skin[j].string, skins[i].name)) + continue; + { setup_player[j].gridx = x; setup_player[j].gridy = y; diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index db6e257bb..2aeb50f55 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -88,7 +88,7 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) { // Check for completion if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) - && !(mapheaderinfo[mapnum]->mapvisited & MV_BEATEN)) + && !(mapheaderinfo[mapnum]->records.mapvisited & MV_BEATEN)) return false; // Check for unlock diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7a4cf56c0..dacce52e6 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -38,6 +38,7 @@ #define UFO_NUMARMS (3) #define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) +#define ufo_emeraldnum(o) ((o)->cvmem) #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) @@ -1005,7 +1006,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); ufo->color = SKINCOLOR_CHAOSEMERALD1; - i = P_GetNextEmerald(); + i = ufo_emeraldnum(ufo) = P_GetNextEmerald(); if (i > 0) { ufo->color += (i - 1) % 7; diff --git a/src/p_setup.c b/src/p_setup.c index 3df1bd163..ea1185a67 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -446,9 +446,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) P_DeleteHeaderFollowers(num); #endif - mapheaderinfo[num]->mapvisited = 0; - Z_Free(mapheaderinfo[num]->mainrecord); - mapheaderinfo[num]->mainrecord = NULL; + memset(&mapheaderinfo[num]->records, 0, sizeof(recorddata_t)); mapheaderinfo[num]->justPlayed = 0; mapheaderinfo[num]->anger = 0; @@ -506,7 +504,6 @@ void P_AllocMapHeader(INT16 i) mapheaderinfo[i]->minimapPic = NULL; mapheaderinfo[i]->ghostCount = 0; mapheaderinfo[i]->cup = NULL; - mapheaderinfo[i]->mainrecord = NULL; mapheaderinfo[i]->followers = NULL; nummapheaders++; } @@ -8309,7 +8306,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (!demo.playback) { - mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED; + mapheaderinfo[gamemap-1]->records.mapvisited |= MV_VISITED; M_UpdateUnlockablesAndExtraEmblems(true, true); G_SaveGameData(); diff --git a/src/p_user.c b/src/p_user.c index 3afb5b7bf..dd2179fce 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1355,11 +1355,19 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) G_UpdateRecords(); } - profile_t *pr = PR_GetPlayerProfile(player); - if (pr != NULL && !losing) + if (!losing) { - pr->wins++; - PR_SaveProfiles(); + profile_t *pr = PR_GetPlayerProfile(player); + if (pr != NULL) + { + pr->wins++; + PR_SaveProfiles(); + } + + if (P_IsLocalPlayer(player) && player->skin < numskins) + { + skins[player->skin].records.wins++; + } } player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation diff --git a/src/r_skins.c b/src/r_skins.c index 9a9df4f88..30c4fa487 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -41,6 +41,8 @@ INT32 numskins = 0; skin_t skins[MAXSKINS]; +unloaded_skin_t *unloadedskins = NULL; + // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES #ifdef SKINVALUES @@ -125,6 +127,8 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->highresscale = FRACUNIT; + // no specific memset for skinrecord_t as it's already nuked by the full skin_t wipe + for (i = 0; i < sfx_skinsoundslot0; i++) if (S_sfx[i].skinsound != -1) skin->soundsid[S_sfx[i].skinsound] = i; @@ -183,7 +187,8 @@ void R_InitSkins(void) UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots) { - UINT8 i, shif, byte; + UINT16 i; + UINT8 shif, byte; INT32 skinid; static UINT8 responsebuffer[MAXAVAILABILITY]; UINT8 defaultbotskin = R_BotDefaultSkin(); @@ -221,7 +226,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) { boolean needsunlocked = false; boolean useplayerstruct = (Playing() && playernum != -1); - UINT8 i; + UINT16 i; INT32 skinid; if (skinnum == -1) @@ -314,12 +319,17 @@ UINT32 R_GetLocalRandomSkin(void) INT32 R_SkinAvailable(const char *name) { INT32 i; + UINT32 hash = quickncasehash(name, SKINNAMESIZE); for (i = 0; i < numskins; i++) { - // search in the skin list - if (stricmp(skins[i].name,name)==0) - return i; + if (skins[i].namehash != hash) + continue; + + if (stricmp(skins[i].name,name)!=0) + continue; + + return i; } return -1; } @@ -727,9 +737,59 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski // returns whether found appropriate property static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) { + if (!stricmp(stoken, "rivals")) + { + size_t len = strlen(value); + size_t i; + char rivalname[SKINNAMESIZE+1] = ""; + UINT8 pos = 0; + UINT8 numrivals = 0; + + // Can't use strtok, because the above function's already using it. + // Using it causes it to upset the saved pointer, + // corrupting the reading for the rest of the file. + + // So instead we get to crawl through the value, character by character, + // and write it down as we go, until we hit a comma or the end of the string. + // Yaaay. + + for (i = 0; i <= len; i++) + { + if (numrivals >= SKINRIVALS) + { + break; + } + + if (value[i] == ',' || i == len) + { + if (pos == 0) + continue; + + STRBUFCPY(skin->rivals[numrivals], rivalname); + strlwr(skin->rivals[numrivals]); + numrivals++; + + if (i == len) + break; + + for (; pos > 0; pos--) + { + rivalname[pos] = '\0'; + } + + continue; + } + + rivalname[pos] = value[i]; + pos++; + } + } + // custom translation table - if (!stricmp(stoken, "startcolor")) + else if (!stricmp(stoken, "startcolor")) + { skin->starttranscolor = atoi(value); + } #define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value); // character type identification @@ -917,45 +977,6 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile) STRBUFCPY(skin->realname, value); SYMBOLCONVERT(skin->realname) } - else if (!stricmp(stoken, "rivals")) - { - size_t len = strlen(value); - size_t i; - char rivalname[SKINNAMESIZE] = ""; - UINT8 pos = 0; - UINT8 numrivals = 0; - - // Can't use strtok, because this function's already using it. - // Using it causes it to upset the saved pointer, - // corrupting the reading for the rest of the file. - - // So instead we get to crawl through the value, character by character, - // and write it down as we go, until we hit a comma or the end of the string. - // Yaaay. - - for (i = 0; i <= len; i++) - { - if (numrivals >= SKINRIVALS) - { - break; - } - - if (value[i] == ',' || i == len) - { - STRBUFCPY(skin->rivals[numrivals], rivalname); - strlwr(skin->rivals[numrivals]); - numrivals++; - - memset(rivalname, 0, sizeof (rivalname)); - pos = 0; - - continue; - } - - rivalname[pos] = value[i]; - pos++; - } - } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); @@ -987,6 +1008,68 @@ next_token: HWR_AddPlayerModel(numskins); #endif + // Finally, conclude by setting up final properties. + skin->namehash = quickncasehash(skin->name, SKINNAMESIZE); + + { + // Check to see if we have any custom skin wins data that we could substitute in. + unloaded_skin_t *unloadedskin, *unloadedprev = NULL; + for (unloadedskin = unloadedskins; unloadedskin; unloadedprev = unloadedskin, unloadedskin = unloadedskin->next) + { + if (unloadedskin->namehash != skin->namehash) + continue; + + if (strcasecmp(skin->name, unloadedskin->name) != 0) + continue; + + // Copy in wins, etc. + M_Memcpy(&skin->records, &unloadedskin->records, sizeof(skin->records)); + + // Remove this entry from the chain. + if (unloadedprev) + { + unloadedprev->next = unloadedskin->next; + } + else + { + unloadedskins = unloadedskin->next; + } + + // Now... we assign everything which used this pointer the new skin id. + UINT8 i; + + cupheader_t *cup; + for (cup = kartcupheaders; cup; cup = cup->next) + { + for (i = 0; i < KARTGP_MAX; i++) + { + if (cup->windata[i].best_skin.unloaded != unloadedskin) + continue; + cup->windata[i].best_skin.id = numskins; + cup->windata[i].best_skin.unloaded = NULL; + } + } + + unloaded_cupheader_t *unloadedcup; + for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next) + { + for (i = 0; i < KARTGP_MAX; i++) + { + if (unloadedcup->windata[i].best_skin.unloaded != unloadedskin) + continue; + unloadedcup->windata[i].best_skin.id = numskins; + unloadedcup->windata[i].best_skin.unloaded = NULL; + } + } + + // Finally, free. + Z_Free(unloadedskin); + + break; + + } + } + numskins++; } return; @@ -1073,45 +1156,6 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile) STRBUFCPY(skin->realname, value); SYMBOLCONVERT(skin->realname) } - else if (!stricmp(stoken, "rivals")) - { - size_t len = strlen(value); - size_t i; - char rivalname[SKINNAMESIZE] = ""; - UINT8 pos = 0; - UINT8 numrivals = 0; - - // Can't use strtok, because this function's already using it. - // Using it causes it to upset the saved pointer, - // corrupting the reading for the rest of the file. - - // So instead we get to crawl through the value, character by character, - // and write it down as we go, until we hit a comma or the end of the string. - // Yaaay. - - for (i = 0; i <= len; i++) - { - if (numrivals >= SKINRIVALS) - { - break; - } - - if (value[i] == ',' || i == len) - { - STRBUFCPY(skin->rivals[numrivals], rivalname); - strlwr(skin->rivals[numrivals]); - numrivals++; - - memset(rivalname, 0, sizeof (rivalname)); - pos = 0; - - continue; - } - - rivalname[pos] = value[i]; - pos++; - } - } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); } diff --git a/src/r_skins.h b/src/r_skins.h index 2770846f1..9a1c45ce6 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -37,7 +37,8 @@ extern "C" { /// The skin_t struct struct skin_t { - char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin + char name[SKINNAMESIZE+1]; // descriptive name of the skin + UINT32 namehash; // quickncasehash(->name, SKINNAMESIZE) UINT16 wadnum; skinflags_t flags; @@ -58,6 +59,8 @@ struct skin_t fixed_t highresscale; // scale of highres, default is 0.5 + skinrecord_t records; + char rivals[SKINRIVALS][SKINNAMESIZE+1]; // Your top 3 rivals for GP mode. Uses names so that you can reference skins that aren't added // specific sounds per skin diff --git a/src/s_sound.c b/src/s_sound.c index 9f04c82e9..9a2fbe466 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1534,7 +1534,7 @@ static boolean S_SoundTestDefLocked(musicdef_t *def) // Is the level tied to SP progression? if ((mapheaderinfo[def->sequence.map]->menuflags & LF2_FINISHNEEDED) - && !(mapheaderinfo[def->sequence.map]->mapvisited & MV_BEATEN)) + && !(mapheaderinfo[def->sequence.map]->records.mapvisited & MV_BEATEN)) return true; // Finally, do a full-fat map check. diff --git a/src/typedef.h b/src/typedef.h index 00eb492a0..a54ae82e5 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -113,6 +113,9 @@ TYPEDEF (skincolor_t); // doomstat.h TYPEDEF (precipprops_t); +TYPEDEF (skinrecord_t); +TYPEDEF (unloaded_skin_t); +TYPEDEF (skinreference_t); TYPEDEF (recorddata_t); TYPEDEF (cupwindata_t); TYPEDEF (scene_t); @@ -125,8 +128,10 @@ TYPEDEF (customoption_t); TYPEDEF (gametype_t); TYPEDEF (staffbrief_t); TYPEDEF (mapheader_t); +TYPEDEF (unloaded_mapheader_t); TYPEDEF (tolinfo_t); TYPEDEF (cupheader_t); +TYPEDEF (unloaded_cupheader_t); // font.h TYPEDEF (font_t);