From fb8795c8da9372fe2a8313c1424eff7124c675b3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 12:21:15 +0100 Subject: [PATCH 01/36] M_FinaliseGameData Creates a central landing point where gamedata loads/creates can be finalised properly. In addition, gamedata wipes caused by data erase or custom SOC gamedata can no longer be saved in a partway corrupted state if they were to crash midway through. --- src/deh_soc.c | 1 + src/dehacked.c | 2 +- src/g_game.c | 9 +-------- src/m_cond.c | 15 +++++++++++++++ src/m_cond.h | 5 ++++- src/menus/options-data-erase-1.c | 5 ++++- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 7e5613444..0194af15c 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3007,6 +3007,7 @@ void readmaincfg(MYFILE *f, boolean mainfile) strlwr(gamedatafilename); savemoddata = true; majormods = false; + gamedata->loaded = false; // Also save a time attack folder filenamelen = strlen(gamedatafilename)-4; // Strip off the extension diff --git a/src/dehacked.c b/src/dehacked.c index c63ab376b..6d44b87fb 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -607,7 +607,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) if (gamedataadded) G_LoadGameData(); - if (gamestate == GS_TITLESCREEN) + if (gamestate == GS_MENU || gamestate == GS_TITLESCREEN) { if (introchanged) { diff --git a/src/g_game.c b/src/g_game.c index 550324341..f90b1aaec 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4863,14 +4863,7 @@ void G_LoadGameData(void) finalisegamedata: { - // Don't consider loaded until it's a success! - // It used to do this much earlier, but this would cause the gamedata to - // save over itself when it I_Errors from the corruption landing point below, - // which can accidentally delete players' legitimate data if the code ever has any tiny mistakes! - gamedata->loaded = true; - - // Silent update unlockables in case they're out of sync with conditions - M_UpdateUnlockablesAndExtraEmblems(false, true); + M_FinaliseGameData(); return; } diff --git a/src/m_cond.c b/src/m_cond.c index b66ee1af8..de7a032e0 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -640,6 +640,21 @@ void M_ClearSecrets(void) gamedata->chaokeys = 3; // Start with 3 !! } +void M_FinaliseGameData(void) +{ + //M_PopulateChallengeGrid(); -- This can be done lazily when we actually need it + + // Don't consider loaded until it's a success! + // It used to do this much earlier, but this would cause the gamedata + // to save over itself when it I_Errors from corruption, which can + // accidentally delete players' legitimate data if the code ever has + // any tiny mistakes! + gamedata->loaded = true; + + // Silent update unlockables in case they're out of sync with conditions + M_UpdateUnlockablesAndExtraEmblems(false, true); +} + // ---------------------- // Condition set checking // ---------------------- diff --git a/src/m_cond.h b/src/m_cond.h index e93ef7857..b08ff611e 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -257,6 +257,8 @@ struct gamedata_t { // WHENEVER OR NOT WE'RE READY TO SAVE boolean loaded; + + // DEFERRED EVENTS RELATING TO CHALLENGE PROCESSING boolean deferredsave; boolean deferredconditioncheck; @@ -338,10 +340,11 @@ char *M_BuildConditionSetString(UINT16 unlockid); void M_AddRawCondition(UINT16 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2, char *stringvar); void M_UpdateConditionSetsPending(void); -// Clearing secrets +// Gamedata clear/init void M_ClearConditionSet(UINT16 set); void M_ClearSecrets(void); void M_ClearStats(void); +void M_FinaliseGameData(void); boolean M_NotFreePlay(void); UINT16 M_CheckCupEmeralds(UINT8 difficulty); diff --git a/src/menus/options-data-erase-1.c b/src/menus/options-data-erase-1.c index a4a75202c..ac31e16e2 100644 --- a/src/menus/options-data-erase-1.c +++ b/src/menus/options-data-erase-1.c @@ -60,6 +60,9 @@ static void M_EraseDataResponse(INT32 ch) // Delete the data // see also G_LoadGameData // We do these in backwards order to prevent things from being immediately re-unlocked. + + gamedata->loaded = false; + if (optionsmenu.erasecontext & EC_TIMEATTACK) G_ClearRecords(); if (optionsmenu.erasecontext & EC_STATISTICS) @@ -67,7 +70,7 @@ static void M_EraseDataResponse(INT32 ch) if (optionsmenu.erasecontext & EC_CHALLENGES) M_ClearSecrets(); - M_UpdateUnlockablesAndExtraEmblems(false, true); + M_FinaliseGameData(); // Don't softlock the Stereo on if you won't be able to access it anymore!? if (soundtest.playing && M_SecretUnlocked(SECRET_SOUNDTEST, true) == false) From be1d3e49e88424ea111e723707586d13d658eab2 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 14:44:01 +0100 Subject: [PATCH 02/36] Change indentation/bracketing on gamedata load for map records To make the next commit less painful to parse. Also fixes signedness of mapnum from INT16 to UINT16 --- src/g_game.c | 83 ++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index f90b1aaec..19f203995 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4680,53 +4680,58 @@ void G_LoadGameData(void) numgamedatamapheaders = READUINT32(save.p); - for (i = 0; i < numgamedatamapheaders; i++) + if (numgamedatamapheaders) { - char mapname[MAXMAPLUMPNAME]; - INT16 mapnum; - READSTRINGL(save.p, mapname, MAXMAPLUMPNAME); - mapnum = G_MapNumber(mapname); - - 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]) + for (i = 0; i < numgamedatamapheaders; i++) { - // Valid mapheader, time to populate with record data. + char mapname[MAXMAPLUMPNAME]; + UINT16 mapnum; - dummyrecord.mapvisited &= MV_MAX; - M_Memcpy(&mapheaderinfo[mapnum]->records, &dummyrecord, sizeof(recorddata_t)); - } - else if ( - ((dummyrecord.mapvisited & MV_PERSISTUNLOADED) != 0 - && (dummyrecord.mapvisited & MV_BEATEN) != 0) - || dummyrecord.time != 0 - || dummyrecord.lap != 0 - ) - { - // Invalid, but we don't want to lose all the juicy statistics. - // Instead, update a FILO linked list of "unloaded mapheaders". + READSTRINGL(save.p, mapname, MAXMAPLUMPNAME); + mapnum = G_MapNumber(mapname); - 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); + recorddata_t dummyrecord; - // Insert at the head, just because it's convenient. - unloadedmap->next = unloadedmapheaders; - unloadedmapheaders = unloadedmap; + dummyrecord.mapvisited = READUINT8(save.p); + dummyrecord.time = (tic_t)READUINT32(save.p); + dummyrecord.lap = (tic_t)READUINT32(save.p); - // Finally, copy into. - M_Memcpy(&unloadedmap->records, &dummyrecord, sizeof(recorddata_t)); + if (mapnum < nummapheaders && mapheaderinfo[mapnum]) + { + // Valid mapheader, time to populate with record data. + + dummyrecord.mapvisited &= MV_MAX; + M_Memcpy(&mapheaderinfo[mapnum]->records, &dummyrecord, sizeof(recorddata_t)); + } + else if ( + ((dummyrecord.mapvisited & MV_PERSISTUNLOADED) != 0 + && (dummyrecord.mapvisited & MV_BEATEN) != 0) + || dummyrecord.time != 0 + || dummyrecord.lap != 0 + ) + { + // 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)); + } } } From 1d06637a3880845bafcf03563ea1f961de80cda3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 15:06:36 +0100 Subject: [PATCH 03/36] First pass at assigning unique spraycans to level headers Increments gamedata minor version, be aware - M_AssignSpraycans - Called in M_FinaliseGameData. - Attaches a hardcoded set of colours to all race maps in cup order, stopping once we run out. - The colours are shuffled, with some "freebies" always at the head of the list. - Integrates partial lists pretty well. - In DEVELOP builds, I_Errors if it produces corrupted state. - G_LoadGameData, G_SaveGameData - Save & Load is implemented for these assignments --- src/deh_soc.c | 7 ++ src/deh_tables.c | 1 + src/doomdef.h | 4 +- src/doomstat.h | 3 + src/g_game.c | 88 +++++++++++++++++++++- src/k_menudraw.c | 24 +++++- src/m_cond.c | 188 +++++++++++++++++++++++++++++++++++++++++++++-- src/m_cond.h | 10 +++ src/p_setup.c | 2 + src/typedef.h | 1 + 10 files changed, 315 insertions(+), 13 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 0194af15c..a748747c1 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -216,6 +216,13 @@ void clear_levels(void) } } + if (gamedata) + { + UINT16 i; + for (i = 1; i < MAXCANCOLORS; i++) + gamedata->spraycans[i].map = 0; + } + // Exit the current gamemap as a safeguard if (Playing()) COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed diff --git a/src/deh_tables.c b/src/deh_tables.c index 97a78e936..58f70bf80 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6538,6 +6538,7 @@ struct int_const_s const INT_CONST[] = { // SKINCOLOR_ doesn't include these..! {"MAXSKINCOLORS",MAXSKINCOLORS}, + {"MAXCANCOLORS",MAXCANCOLORS}, {"FIRSTSUPERCOLOR",FIRSTSUPERCOLOR}, {"NUMSUPERCOLORS",NUMSUPERCOLORS}, diff --git a/src/doomdef.h b/src/doomdef.h index 75264bbd0..c9f8b1702 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -346,7 +346,9 @@ typedef enum SKINCOLOR_BLOSSOM, SKINCOLOR_TAFFY, - FIRSTSUPERCOLOR, + MAXCANCOLORS, + + FIRSTSUPERCOLOR = MAXCANCOLORS, // Super special awesome Super flashing colors! SKINCOLOR_SUPERSILVER1 = FIRSTSUPERCOLOR, diff --git a/src/doomstat.h b/src/doomstat.h index c4f8b37e7..eecb6d1c7 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -531,6 +531,9 @@ struct mapheader_t UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts. UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. + UINT32 _saveid; ///< Purely assistive in gamedata save processes + UINT16 cachedcan; ///< Cached Spraycan ID + // Lua information UINT8 numCustomOptions; ///< Internal. For Lua custom value support. customoption_t *customopts; ///< Custom options. Allocated dynamically for space reasons. Be careful. diff --git a/src/g_game.c b/src/g_game.c index 19f203995..718b7836d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4357,7 +4357,7 @@ void G_LoadGameSettings(void) } #define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual -#define GD_VERSIONMINOR 5 // Change every format update +#define GD_VERSIONMINOR 6 // Change every format update typedef enum { @@ -4678,10 +4678,17 @@ void G_LoadGameData(void) } } + UINT16 *tempmapidreferences = NULL; + numgamedatamapheaders = READUINT32(save.p); if (numgamedatamapheaders) { + tempmapidreferences = Z_Malloc( + numgamedatamapheaders * sizeof (UINT16), + PU_STATIC, + NULL + ); for (i = 0; i < numgamedatamapheaders; i++) { @@ -4691,6 +4698,7 @@ void G_LoadGameData(void) READSTRINGL(save.p, mapname, MAXMAPLUMPNAME); mapnum = G_MapNumber(mapname); + tempmapidreferences[i] = (UINT16)mapnum; recorddata_t dummyrecord; @@ -4735,6 +4743,49 @@ void G_LoadGameData(void) } } + if (versionMinor > 5) + { + UINT16 numgamedatacans = READUINT32(save.p); + + if (numgamedatacans != MAXCANCOLORS - 1) + { + save.p += (1 + 4) * numgamedatacans; + } + else + { + for (i = 1; i < MAXCANCOLORS; i++) + { + gamedata->spraycans[i].got = (boolean)READUINT8(save.p); + gamedata->spraycans[i].map = 0; + + UINT32 _saveid = READUINT32(save.p); + + if (_saveid >= numgamedatamapheaders) + { + //CONS_Printf("LOAD - Color %s - id %u, map 0 (invalid id)\n", skincolors[i].name, _saveid); + continue; + } + + UINT16 map = tempmapidreferences[_saveid]; + if (map >= nummapheaders || !mapheaderinfo[map]) + { + //CONS_Printf("LOAD - Color %s - id %u, map 0 (unloaded header)\n", skincolors[i].name, _saveid); + continue; + } + + //CONS_Printf("LOAD - Color %s - id %u, map %d\n", skincolors[i].name, _saveid, map+1); + + gamedata->spraycans[i].map = map+1; + mapheaderinfo[map]->cachedcan = i; + + numgamedatacans--; // this one was successfully placed + } + + gamedata->allspraycansplaced = (numgamedatacans == 0); + //CONS_Printf("CCC - all spray cans placed? %c\n", gamedata->allspraycansplaced ? 'Y' : 'N'); + } + } + if (versionMinor > 1) { numgamedatacups = READUINT32(save.p); @@ -4862,6 +4913,8 @@ void G_LoadGameData(void) if (tempskinreferences) Z_Free(tempskinreferences); + if (tempmapidreferences) + Z_Free(tempmapidreferences); // done P_SaveBufferFree(&save); @@ -4983,12 +5036,16 @@ void G_SaveGameData(void) for (i = 0; i < nummapheaders; i++) { + // No spraycan attached. + if (mapheaderinfo[i]->cachedcan == 0 // 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)) + && !(mapheaderinfo[i]->records.mapvisited & MV_MAX)) { + mapheaderinfo[i]->_saveid = UINT32_MAX; continue; } + mapheaderinfo[i]->_saveid = numgamedatamapheaders; numgamedatamapheaders++; } @@ -5012,6 +5069,9 @@ void G_SaveGameData(void) length += 4 + (numgamedatamapheaders * (MAXMAPLUMPNAME+1+4+4)); + length += 4 + ((MAXCANCOLORS - 1) * (1 + 4)); + + UINT32 numgamedatacups = 0; unloaded_cupheader_t *unloadedcup; @@ -5206,7 +5266,8 @@ void G_SaveGameData(void) for (i = 0; i < nummapheaders; i++) { - if (!(mapheaderinfo[i]->records.mapvisited & MV_MAX)) + if (mapheaderinfo[i]->cachedcan == 0 + && !(mapheaderinfo[i]->records.mapvisited & MV_MAX)) continue; WRITESTRINGL(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME); @@ -5247,6 +5308,27 @@ void G_SaveGameData(void) } } + WRITEUINT32(save.p, MAXCANCOLORS - 1); // 4 + + // (MAXCANCOLORS - 1) * (1 + 4) + + for (i = 1; i < MAXCANCOLORS; i++) + { + WRITEUINT8(save.p, gamedata->spraycans[i].got); + + UINT32 _saveid = UINT32_MAX; + + UINT16 map = gamedata->spraycans[i].map; + + if (map > 0 && map <= nummapheaders && mapheaderinfo[map - 1]) + { + _saveid = mapheaderinfo[map - 1]->_saveid; + } + + //CONS_Printf("SAVE - Color %s - id %u, map %d\n", skincolors[i].name, _saveid, map); + + WRITEUINT32(save.p, _saveid); + } WRITEUINT32(save.p, numgamedatacups); // 4 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b789a97f4..6b7b6fa7f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6049,6 +6049,26 @@ challengedesc: static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) { UINT8 lasttype = UINT8_MAX, curtype; + + boolean start = false; + + if (mapheaderinfo[mapnum]->cachedcan != 0 && mapheaderinfo[mapnum]->cachedcan < MAXCANCOLORS) + { + V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName("GOTITA", PU_CACHE), + R_GetTranslationColormap(TC_RAINBOW, mapheaderinfo[mapnum]->cachedcan, GTC_MENUCACHE)); + //V_DrawRightAlignedThinString(x - 2, y, 0, skincolors[mapheaderinfo[mapnum]->cachedcan].name); + x -= 8; + + start = true; + } + + // Shift over if emblem is of a different discipline + if (start) + x -= 4; + + // M_GetLevelEmblems is ONE-indexed, urgh + mapnum++; + emblem_t *emblem = M_GetLevelEmblems(mapnum); while (emblem) @@ -6091,7 +6111,7 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) if (gamedata->collected[emblem-emblemlocations]) V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); else V_DrawSmallScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); @@ -6246,7 +6266,7 @@ static void M_DrawStatsMaps(void) } } - M_DrawMapMedals(mnum+1, medalspos - 8, y); + M_DrawMapMedals(mnum, medalspos - 8, y); if (mapheaderinfo[mnum]->menuttl[0]) { diff --git a/src/m_cond.c b/src/m_cond.c index de7a032e0..ae8143b2d 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -621,14 +621,23 @@ void M_ClearStats(void) void M_ClearSecrets(void) { - INT32 i; + memset(gamedata->collected, 0, sizeof(gamedata->collected)); + memset(gamedata->unlocked, 0, sizeof(gamedata->unlocked)); + memset(gamedata->unlockpending, 0, sizeof(gamedata->unlockpending)); + if (!dedicated) + memset(netUnlocked, 0, sizeof(netUnlocked)); + memset(gamedata->achieved, 0, sizeof(gamedata->achieved)); - for (i = 0; i < MAXEMBLEMS; ++i) - gamedata->collected[i] = false; - for (i = 0; i < MAXUNLOCKABLES; ++i) - gamedata->unlocked[i] = gamedata->unlockpending[i] = netUnlocked[i] = false; - for (i = 0; i < MAXCONDITIONSETS; ++i) - gamedata->achieved[i] = false; + gamedata->allspraycansplaced = false; + memset(gamedata->spraycans, 0, sizeof(gamedata->spraycans)); + + INT32 i; + for (i = 0; i < nummapheaders; i++) + { + if (!mapheaderinfo[i]) + continue; + mapheaderinfo[i]->cachedcan = 0; + } Z_Free(gamedata->challengegrid); gamedata->challengegrid = NULL; @@ -640,10 +649,175 @@ void M_ClearSecrets(void) gamedata->chaokeys = 3; // Start with 3 !! } +// For lack of a better idea on where to put this +static void M_Shuffle_UINT16(UINT16 *list, size_t len) +{ + size_t i; + UINT16 temp; + while (--len > 1) // no need to swap on == + { + i = M_RandomKey(len); + temp = list[i]; + list[i] = list[len]; + list[len] = temp; + } +} + +static void M_AssignSpraycans(void) +{ + // Very convenient I'm programming this on + // the release date of "Bomb Rush Cyberfunk". + // ~toast 180823 (committed a day later) + + if (gamedata->allspraycansplaced) + return; + + // Init ordered list of skincolors + UINT16 tempcanlist[MAXCANCOLORS]; + size_t listlen = 0; + + // Todo one of these should be a freebie + UINT16 prependlist[] = + { + SKINCOLOR_RED, + SKINCOLOR_ORANGE, + SKINCOLOR_YELLOW, + SKINCOLOR_GREEN, + SKINCOLOR_BLUE, + SKINCOLOR_PURPLE, + 0 + }; + + UINT16 i; + + for (i = 0; prependlist[i]; i++) + { + if (gamedata->spraycans[prependlist[i]].map > 0 + && gamedata->spraycans[prependlist[i]].map <= nummapheaders) + continue; + + CONS_Printf("DDD - Prepending %d\n", prependlist[i]); + + tempcanlist[listlen] = prependlist[i]; + gamedata->spraycans[prependlist[i]].got = 2; // invalid set to detect in below loop, rather than having to iterate over prependlist again + listlen++; + } + + size_t prepend = listlen; + + for (i = 1; i < MAXCANCOLORS; i++) + { + if (gamedata->spraycans[i].map > 0 + && gamedata->spraycans[i].map <= nummapheaders) + continue; + + if (gamedata->spraycans[i].got == 2) + { + // re-make valid, reject duplicating prepended + gamedata->spraycans[i].got = false; + continue; + } + + CONS_Printf("DDD - Adding %d\n", i); + + tempcanlist[listlen] = i; + listlen++; + } + + if (!listlen) + goto cansdone; + + if (prepend > 0) + { + // Swap the prepend for random order + M_Shuffle_UINT16(tempcanlist, prepend); + } + + if (listlen > prepend) + { + // Swap everything else for random order + M_Shuffle_UINT16(tempcanlist + prepend, listlen - prepend); + } + + i = 0; + + cupheader_t *cup; + + UINT16 level; + + for (cup = kartcupheaders; cup; cup = cup->next) + { + UINT8 j; + for (j = 0; j < cup->numlevels; j++) + { + level = cup->cachedlevels[j]; + + if (level > nummapheaders) + continue; + + if (mapheaderinfo[level]->cachedcan != 0) + continue; + + gamedata->spraycans[tempcanlist[i]].map = level + 1; + mapheaderinfo[level]->cachedcan = tempcanlist[i]; + + if (++i < listlen) + continue; + + goto cansdone; + } + } + + for (level = 0; level < nummapheaders; level++) + { + if (!mapheaderinfo[level] + || !(mapheaderinfo[level]->typeoflevel & TOL_RACE) + || mapheaderinfo[level]->cachedcan != 0) + continue; + + gamedata->spraycans[tempcanlist[i]].map = level + 1; + mapheaderinfo[level]->cachedcan = tempcanlist[i]; + + if (++i < listlen) + continue; + + goto cansdone; + } + +cansdone: + +#ifdef PARANOIA + for (i = 1; i < MAXCANCOLORS; i++) + { + if (gamedata->spraycans[i].map == 0) + I_Error("CANPROBLEM - BAD MAP FOR CAN %d\n", i); + if (gamedata->spraycans[i].map > nummapheaders) + I_Error("CANPROBLEM - TOO BIG MAP FOR CAN %d\n", i); + if (mapheaderinfo[gamedata->spraycans[i].map-1]->cachedcan != i) + I_Error("CANPROBLEM - MAP AND CAN DISAGREE FOR %d (%d)\n", i, mapheaderinfo[gamedata->spraycans[i].map-1]->cachedcan); + } + + for (i = 0; i < nummapheaders; i++) + { + if (!mapheaderinfo[i] || mapheaderinfo[i]->cachedcan == 0) + continue; + if (mapheaderinfo[i]->cachedcan > MAXCANCOLORS) + I_Error("MAPPROBLEM - BAD CAN FOR MAP %d\n", i); + if (gamedata->spraycans[mapheaderinfo[i]->cachedcan].map-1 != i) + I_Error("MAPPROBLEM - CAN AND MAP DISAGREE FOR %d (%d)\n", i, gamedata->spraycans[mapheaderinfo[i]->cachedcan].map-1); + } +#endif + + gamedata->allspraycansplaced = true; +} + void M_FinaliseGameData(void) { //M_PopulateChallengeGrid(); -- This can be done lazily when we actually need it + // Place the spraycans, which CAN'T be done lazily. + M_AssignSpraycans(); + // Don't consider loaded until it's a success! // It used to do this much earlier, but this would cause the gamedata // to save over itself when it I_Errors from corruption, which can diff --git a/src/m_cond.h b/src/m_cond.h index b08ff611e..e01c1b2d9 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -251,6 +251,12 @@ typedef enum { GDGT_MAX } roundsplayed_t; +struct candata_t +{ + UINT16 map; + boolean got; +}; + // GAMEDATA STRUCTURE // Everything that would get saved in gamedata.dat struct gamedata_t @@ -272,6 +278,10 @@ struct gamedata_t boolean unlocked[MAXUNLOCKABLES]; boolean unlockpending[MAXUNLOCKABLES]; + // SPRAYCANS COLLECTED + boolean allspraycansplaced; + candata_t spraycans[MAXCANCOLORS]; + // CHALLENGE GRID UINT16 challengegridwidth; UINT16 *challengegrid; diff --git a/src/p_setup.c b/src/p_setup.c index 3f9bfcd2e..b687cd347 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -460,6 +460,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->justPlayed = 0; mapheaderinfo[num]->anger = 0; + mapheaderinfo[num]->cachedcan = 0; + mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; } diff --git a/src/typedef.h b/src/typedef.h index 7bbc232b6..a27ca364f 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -238,6 +238,7 @@ TYPEDEF (condition_t); TYPEDEF (conditionset_t); TYPEDEF (emblem_t); TYPEDEF (unlockable_t); +TYPEDEF (candata_t); TYPEDEF (gamedata_t); TYPEDEF (challengegridextradata_t); From b03c82b8b26cfd2b74d1973f991e2bf6d0b3121d Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 15:09:18 +0100 Subject: [PATCH 04/36] Implement UC_SPRAYCAN condition Currently impossible to achieve, but will work once I draw the rest of the owl --- src/deh_soc.c | 6 ++++++ src/m_cond.c | 26 ++++++++++++++++++++++++++ src/m_cond.h | 2 ++ 3 files changed, 34 insertions(+) diff --git a/src/deh_soc.c b/src/deh_soc.c index a748747c1..a092384f6 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2620,6 +2620,12 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) stringvar = Z_StrDup(params[1]); re = -1; } + else if (fastcmp(params[0], "SPRAYCAN")) + { + PARAMCHECK(1); + ty = UC_SPRAYCAN; + re = get_skincolor(params[1]); + } else if ((offset=0) || fastcmp(params[0], "AND") || (++offset && fastcmp(params[0], "COMMA"))) { diff --git a/src/m_cond.c b/src/m_cond.c index ae8143b2d..dee2c008e 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1059,6 +1059,14 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return false; case UC_PASSWORD: return (cn->stringvar == NULL); + case UC_SPRAYCAN: + { + if (cn->requirement <= 0 + || cn->requirement >= MAXCANCOLORS) + return false; + + return gamedata->spraycans[cn->requirement].got; + } // Just for string building case UC_AND: @@ -1511,6 +1519,24 @@ static const char *M_GetConditionString(condition_t *cn) return NULL; case UC_PASSWORD: return "enter a secret password"; + case UC_SPRAYCAN: + { + if (cn->requirement <= 0 + || cn->requirement >= MAXCANCOLORS) + return va("INVALID SPRAYCAN COLOR \"%d\"", cn->requirement); + + INT32 checkLevel = gamedata->spraycans[cn->requirement].map - 1; + + if (checkLevel < 0 || checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + return va("INVALID SPRAYCAN MAP \"%d:%d\"", cn->requirement, checkLevel); + + title = BUILDCONDITIONTITLE(checkLevel); + + work = va("%s: grab the spraycan", title); + + Z_Free(title); + return work; + } case UC_AND: return "&"; diff --git a/src/m_cond.h b/src/m_cond.h index e01c1b2d9..09c850aa6 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -58,6 +58,8 @@ typedef enum UC_PASSWORD, // Type in something funny + UC_SPRAYCAN, // Grab a spraycan + // Just for string building UC_AND, UC_COMMA, From 78850c48b25c3a548a40023a9133a35fa65e964b Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 17:16:36 +0100 Subject: [PATCH 05/36] Disable some testing prints --- src/m_cond.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index dee2c008e..d6921a30c 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -696,7 +696,7 @@ static void M_AssignSpraycans(void) && gamedata->spraycans[prependlist[i]].map <= nummapheaders) continue; - CONS_Printf("DDD - Prepending %d\n", prependlist[i]); + //CONS_Printf("DDD - Prepending %d\n", prependlist[i]); tempcanlist[listlen] = prependlist[i]; gamedata->spraycans[prependlist[i]].got = 2; // invalid set to detect in below loop, rather than having to iterate over prependlist again @@ -718,7 +718,7 @@ static void M_AssignSpraycans(void) continue; } - CONS_Printf("DDD - Adding %d\n", i); + //CONS_Printf("DDD - Adding %d\n", i); tempcanlist[listlen] = i; listlen++; From d19e98beb836daf4ece19f7c5fe34d558629357f Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 21:33:49 +0100 Subject: [PATCH 06/36] Proper-Nounify the Spray Cans on the Challenges descriptions --- src/m_cond.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index d6921a30c..554cba4a6 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1532,7 +1532,7 @@ static const char *M_GetConditionString(condition_t *cn) title = BUILDCONDITIONTITLE(checkLevel); - work = va("%s: grab the spraycan", title); + work = va("%s: grab the Spray Can", title); Z_Free(title); return work; From 3036eaf35db038da08d4ba5fbc8e822ccdb0485b Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 22:11:56 +0100 Subject: [PATCH 07/36] MT_EMBLEM: Rework grabbable conditions, so you pass through collected ones in both offline and online for consistency --- src/p_inter.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index fb2a7a071..9d3751395 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -596,26 +596,22 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Secret emblem thingy case MT_EMBLEM: { - boolean gotcollected = false; - if (!P_CanPickupEmblem(player, special->health - 1)) return; - if (P_IsLocalPlayer(player) && !gamedata->collected[special->health-1]) + if (P_IsLocalPlayer(player)) { - gamedata->collected[special->health-1] = gotcollected = true; - if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) - S_StartSound(NULL, sfx_ncitem); - gamedata->deferredsave = true; + if (!gamedata->collected[special->health-1]) + { + gamedata->collected[special->health-1] = true; + if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) + S_StartSound(NULL, sfx_ncitem); + gamedata->deferredsave = true; + } } - if (netgame) - { - // Don't delete the object in netgames, just fade it. - return; - } - - break; + // Don't delete the object, just fade it. + return; } // CTF Flags From dc695e7acf3dbf2b4cc74376e336c2045956385e Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 19 Aug 2023 22:21:20 +0100 Subject: [PATCH 08/36] First pass at implementing Spray Can pickups - Replaces a few D00DKart objects because the doomednum specifically replaced one of these - Reports on load if the map has too many, or if one's assigned but the object doesn't exist --- src/deh_tables.c | 28 +------ src/doomstat.h | 2 + src/g_game.c | 2 + src/info.c | 211 +++++++---------------------------------------- src/info.h | 30 ++----- src/k_menudraw.c | 32 +++---- src/p_inter.c | 37 +++++++++ src/p_local.h | 2 + src/p_mobj.c | 36 +++++++- src/p_saveg.c | 4 + src/p_setup.c | 17 ++++ 11 files changed, 156 insertions(+), 245 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 58f70bf80..130ffbf40 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1190,6 +1190,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_EMBLEM25", "S_EMBLEM26", + // Spray Can + "S_SPRAYCAN", + // Chaos Emeralds "S_CHAOSEMERALD1", "S_CHAOSEMERALD2", @@ -3921,24 +3924,6 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_APPLE7", "S_APPLE8", - // D00Dkart - Fall Flowers - "S_DOOD_FLOWER1", - "S_DOOD_FLOWER2", - "S_DOOD_FLOWER3", - "S_DOOD_FLOWER4", - "S_DOOD_FLOWER5", - "S_DOOD_FLOWER6", - - // D00Dkart - Super Circuit Box - "S_DOOD_BOX1", - "S_DOOD_BOX2", - "S_DOOD_BOX3", - "S_DOOD_BOX4", - "S_DOOD_BOX5", - - // D00Dkart - Diddy Kong Racing Bumper - "S_DOOD_BALLOON", - // Chaotix Big Ring "S_BIGRING01", "S_BIGRING02", @@ -4805,6 +4790,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_REDFLAG", // Red CTF Flag "MT_BLUEFLAG", // Blue CTF Flag "MT_EMBLEM", + "MT_SPRAYCAN", "MT_EMERALD", "MT_EMERALDSPARK", "MT_EMERHUNT", // Emerald Hunt @@ -5580,12 +5566,6 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BIGPUMA", "MT_APPLE", - "MT_DOOD_FLOWER1", - "MT_DOOD_FLOWER2", - "MT_DOOD_FLOWER3", - "MT_DOOD_FLOWER4", - "MT_DOOD_BOX", - "MT_DOOD_BALLOON", "MT_BIGRING", "MT_SNES_DONUTBUSH1", diff --git a/src/doomstat.h b/src/doomstat.h index eecb6d1c7..b1fae2b26 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -713,6 +713,8 @@ extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins +extern UINT8 numspraycans; + extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 redscore; ///< Red Team Scores diff --git a/src/g_game.c b/src/g_game.c index 718b7836d..17e5736d1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -215,6 +215,8 @@ UINT32 bluescore, redscore; // CTF and Team Match team scores // ring count... for PERFECT! INT32 nummaprings = 0; +UINT8 numspraycans = 0; + // Elminates unnecessary searching. boolean CheckForBustableBlocks; boolean CheckForBouncySector; diff --git a/src/info.c b/src/info.c index a3d358d7e..d6fa42032 100644 --- a/src/info.c +++ b/src/info.c @@ -636,7 +636,7 @@ char sprnames[NUMSPRITES + 1][5] = "POKE", // Pokey "AUDI", // Audience members "DECO", // Old 1.0 Kart Decoratives + New misc ones - "DOOD", // All the old D00Dkart objects + "SPCN", // Spray Can replaces all the old D00Dkart objects "SNES", // Sprites for SNES remake maps "GBAS", // Sprites for GBA remake maps "SPRS", // Sapphire Coast Spring Shell @@ -1860,6 +1860,9 @@ state_t states[NUMSTATES] = {SPR_EMBM, 24, -1, {NULL}, 0, 0, S_NULL}, // S_EMBLEM25 {SPR_EMBM, 25, -1, {NULL}, 0, 0, S_NULL}, // S_EMBLEM26 + // Spray Can + {SPR_SPCN, FF_ANIMATE|FF_SEMIBRIGHT, -1, {NULL}, 15, 2, S_NULL}, // S_SPRAYCAN + // Chaos Emeralds {SPR_EMRC, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CHAOSEMERALD2}, // S_CHAOSEMERALD1 {SPR_EMRC, FF_FULLBRIGHT|FF_ADD, 1, {NULL}, 0, 0, S_CHAOSEMERALD1}, // S_CHAOSEMERALD2 @@ -4626,21 +4629,6 @@ state_t states[NUMSTATES] = {SPR_DECO, FF_FULLBRIGHT|20, 2, {NULL}, 0, 0, S_APPLE8}, //S_APPLE7 {SPR_DECO, FF_FULLBRIGHT|21, 2, {NULL}, 0, 0, S_APPLE1}, //S_APPLE8 - {SPR_DOOD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_DOOD_FLOWER1 - {SPR_DOOD, 1, 14, {NULL}, 0, 0, S_DOOD_FLOWER3}, // S_DOOD_FLOWER2 - {SPR_DOOD, 2, 14, {NULL}, 0, 0, S_DOOD_FLOWER2}, // S_DOOD_FLOWER3 - {SPR_DOOD, 3, 7, {NULL}, 0, 0, S_DOOD_FLOWER5}, // S_DOOD_FLOWER4 - {SPR_DOOD, 4, 7, {NULL}, 0, 0, S_DOOD_FLOWER4}, // S_DOOD_FLOWER5 - {SPR_DOOD, 5, -1, {NULL}, 0, 0, S_NULL}, // S_DOOD_FLOWER6 - - {SPR_DOOD, 6, 2, {NULL}, 0, 0, S_DOOD_BOX2}, // S_DOOD_BOX1 - {SPR_DOOD, 7, 2, {NULL}, 0, 0, S_DOOD_BOX3}, // S_DOOD_BOX2 - {SPR_DOOD, 8, 2, {NULL}, 0, 0, S_DOOD_BOX4}, // S_DOOD_BOX3 - {SPR_DOOD, 9, 2, {NULL}, 0, 0, S_DOOD_BOX5}, // S_DOOD_BOX4 - {SPR_DOOD, 10, 2, {NULL}, 0, 0, S_DOOD_BOX1}, // S_DOOD_BOX5 - - {SPR_DOOD, 11, -1, {NULL}, 0, 0, S_NULL}, // S_DOOD_BALLOON - {SPR_BRNG, 0, 2, {NULL}, 0, 0, S_BIGRING02}, // S_BIGRING01 {SPR_BRNG, 1, 2, {NULL}, 0, 0, S_BIGRING03}, // S_BIGRING02 {SPR_BRNG, 2, 2, {NULL}, 0, 0, S_BIGRING04}, // S_BIGRING03 @@ -8272,7 +8260,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SPRAYCAN + 2807, // doomednum + S_SPRAYCAN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 30*FRACUNIT, // radius + 80*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -25673,168 +25688,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_DOOD_FLOWER1 - 2805, // doomednum - S_DOOD_FLOWER1, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 1048576, // radius - 2097152, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - 33558528, // flags - S_NULL // raisestate - }, - - { // MT_DOOD_FLOWER2 - 2800, // doomednum - S_DOOD_FLOWER2, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 1048576, // radius - 2621440, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - 33558528, // flags - S_NULL // raisestate - }, - - { // MT_DOOD_FLOWER3 - 2801, // doomednum - S_DOOD_FLOWER4, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 1048576, // radius - 6291456, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - 33558528, // flags - S_NULL // raisestate - }, - - { // MT_DOOD_FLOWER4 - 2802, // doomednum - S_DOOD_FLOWER6, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 524288, // radius - 2097152, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - 33558528, // flags - S_NULL // raisestate - }, - - { // MT_DOOD_BOX - 2809, // doomednum - S_DOOD_BOX1, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 1048576, // radius - 2097152, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - 33554944, // flags - S_NULL // raisestate - }, - - { // MT_DOOD_BALLOON - 2807, // doomednum - S_DOOD_BALLOON, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 91*FRACUNIT, // radius - 166*FRACUNIT, // height - 0, // display offset - 0, // mass - 0, // damage - sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags - S_NULL // raisestate - }, - { // MT_BIGRING 2808, // doomednum S_BIGRING01, // spawnstate diff --git a/src/info.h b/src/info.h index 9dc14922b..ac593e259 100644 --- a/src/info.h +++ b/src/info.h @@ -1189,7 +1189,7 @@ typedef enum sprite SPR_POKE, // Pokey SPR_AUDI, // Audience members SPR_DECO, // Old 1.0 Kart Decoratives + New misc ones - SPR_DOOD, // All the old D00Dkart objects + SPR_SPCN, // Spray Can replaces all the old D00Dkart objects SPR_SNES, // Sprites for SNES remake maps SPR_GBAS, // Sprites for GBA remake maps SPR_SPRS, // Sapphire Coast Spring Shell @@ -2344,6 +2344,9 @@ typedef enum state S_EMBLEM25, S_EMBLEM26, + // Spray Can + S_SPRAYCAN, + // Chaos Emeralds S_CHAOSEMERALD1, S_CHAOSEMERALD2, @@ -5074,24 +5077,6 @@ typedef enum state S_APPLE7, S_APPLE8, - // D00Dkart - Fall Flowers - S_DOOD_FLOWER1, - S_DOOD_FLOWER2, - S_DOOD_FLOWER3, - S_DOOD_FLOWER4, - S_DOOD_FLOWER5, - S_DOOD_FLOWER6, - - // D00Dkart - Super Circuit Box - S_DOOD_BOX1, - S_DOOD_BOX2, - S_DOOD_BOX3, - S_DOOD_BOX4, - S_DOOD_BOX5, - - // D00Dkart - Diddy Kong Racing Bumper - S_DOOD_BALLOON, - // Chaotix Big Ring S_BIGRING01, S_BIGRING02, @@ -5994,6 +5979,7 @@ typedef enum mobj_type MT_REDFLAG, // Red CTF Flag MT_BLUEFLAG, // Blue CTF Flag MT_EMBLEM, + MT_SPRAYCAN, MT_EMERALD, MT_EMERALDSPARK, MT_EMERHUNT, // Emerald Hunt @@ -6768,12 +6754,6 @@ typedef enum mobj_type MT_BIGPUMA, MT_APPLE, - MT_DOOD_FLOWER1, - MT_DOOD_FLOWER2, - MT_DOOD_FLOWER3, - MT_DOOD_FLOWER4, - MT_DOOD_BOX, - MT_DOOD_BALLOON, MT_BIGRING, MT_SNES_DONUTBUSH1, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 6b7b6fa7f..4899b4c06 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6050,27 +6050,13 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) { UINT8 lasttype = UINT8_MAX, curtype; - boolean start = false; - - if (mapheaderinfo[mapnum]->cachedcan != 0 && mapheaderinfo[mapnum]->cachedcan < MAXCANCOLORS) - { - V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName("GOTITA", PU_CACHE), - R_GetTranslationColormap(TC_RAINBOW, mapheaderinfo[mapnum]->cachedcan, GTC_MENUCACHE)); - //V_DrawRightAlignedThinString(x - 2, y, 0, skincolors[mapheaderinfo[mapnum]->cachedcan].name); - x -= 8; - - start = true; - } - - // Shift over if emblem is of a different discipline - if (start) - x -= 4; - // M_GetLevelEmblems is ONE-indexed, urgh mapnum++; emblem_t *emblem = M_GetLevelEmblems(mapnum); + boolean hasmedals = (emblem != NULL); + while (emblem) { switch (emblem->type) @@ -6118,6 +6104,20 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) emblem = M_GetLevelEmblems(-1); x -= 8; } + + // Undo offset + mapnum--; + + if (hasmedals) + x -= 4; + + if (mapheaderinfo[mapnum]->cachedcan != 0 && mapheaderinfo[mapnum]->cachedcan < MAXCANCOLORS && gamedata->spraycans[mapheaderinfo[mapnum]->cachedcan].got == true) + { + V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), + R_GetTranslationColormap(TC_RAINBOW, mapheaderinfo[mapnum]->cachedcan, GTC_MENUCACHE)); + //V_DrawRightAlignedThinString(x - 2, y, 0, skincolors[mapheaderinfo[mapnum]->cachedcan].name); + x -= 8; + } } static void M_DrawStatsMaps(void) diff --git a/src/p_inter.c b/src/p_inter.c index 9d3751395..a171df1e1 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -614,6 +614,43 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } + case MT_SPRAYCAN: + { + UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; + + if (col == 0 || col > MAXCANCOLORS) + { + return; + } + + if (demo.playback) + { + // Never collect emblems in replays. + return; + } + + if (player->bot) + { + // Your nefarious opponent puppy can't grab these for you. + return; + } + + if (P_IsLocalPlayer(player)) + { + if (!gamedata->spraycans[col].got) + { + gamedata->spraycans[col].got = true; + if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) + S_StartSound(NULL, sfx_ncitem); + gamedata->deferredsave = true; + } + } + + // Don't delete the object, just fade it. + P_SprayCanInit(special); + return; + } + // CTF Flags case MT_REDFLAG: case MT_BLUEFLAG: diff --git a/src/p_local.h b/src/p_local.h index 20ad1d5d2..fe75081d5 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -199,6 +199,8 @@ boolean P_AutoPause(void); void P_ElementalFire(player_t *player, boolean cropcircle); void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound); +void P_SprayCanInit(mobj_t* mobj); + void P_HaltPlayerOrbit(player_t *player); void P_ExitPlayerOrbit(player_t *player); boolean P_PlayerOrbit(player_t *player); diff --git a/src/p_mobj.c b/src/p_mobj.c index 47d20059b..5f265b508 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7170,7 +7170,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(&players[consoleplayer], mobj->health - 1)) { trans = tr_trans50; - mobj->renderflags |= (tr_trans50 << RF_TRANSSHIFT); } if (mobj->reactiontime > 0 @@ -12197,6 +12196,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt // Ring-like items, float additional units unless args[0] is set. case MT_SPIKEBALL: case MT_EMBLEM: + case MT_SPRAYCAN: case MT_RING: case MT_BLUESPHERE: offset += mthing->args[0] ? 0 : 24*FRACUNIT; @@ -12406,6 +12406,23 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) return true; } +void P_SprayCanInit(mobj_t* mobj) +{ + UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; + + if (col == 0 || col > MAXCANCOLORS) + { + mobj->renderflags = RF_DONTDRAW; + return; + } + + mobj->color = col; + + mobj->renderflags = (gamedata->spraycans[col].got) + ? (tr_trans50 << RF_TRANSSHIFT) + : 0; +} + static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj) { fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor; @@ -12866,6 +12883,23 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) } break; } + case MT_SPRAYCAN: + { + if (numspraycans) + { + if (numspraycans != UINT8_MAX) + numspraycans++; + + P_RemoveMobj(mobj); + return false; + } + + P_SetScale(mobj, mobj->destscale = 2*mobj->scale); + + P_SprayCanInit(mobj); + numspraycans++; + break; + } case MT_SKYBOX: { P_InitSkyboxPoint(mobj, mthing); diff --git a/src/p_saveg.c b/src/p_saveg.c index f948c7fa0..c8574c911 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4190,6 +4190,10 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) { P_InitSkyboxPoint(mobj, mobj->spawnpoint); } + else if (mobj->type == MT_SPRAYCAN) + { + P_SprayCanInit(mobj); + } if (diff2 & MD2_WAYPOINTCAP) P_SetTarget(&waypointcap, mobj); diff --git a/src/p_setup.c b/src/p_setup.c index b687cd347..4b9b1d4a8 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -813,6 +813,21 @@ static void P_SpawnMapThings(boolean spawnemblems) } Z_Free(loopends); + + if (spawnemblems) + { + if (numspraycans == 0) + { + UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; + + if (col > 0 && col <= MAXCANCOLORS) + { + CONS_Alert(CONS_WARNING, "SPRAY CANS: Map has assigned Spray Cans but no pickup placed!\n"); + } + } + else if (numspraycans > 1) + CONS_Alert(CONS_ERROR, "SPRAY CANS: Map has too many Spray Cans (%d)!", numspraycans); + } } // Experimental groovy write function! @@ -7472,6 +7487,8 @@ static void P_InitLevelSettings(void) maptargets = numtargets = 0; battleprisons = false; + numspraycans = 0; + // emerald hunt hunt1 = hunt2 = hunt3 = NULL; From 8a72f42818ad4779bfe29a8b751cb08da7ac6ddb Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Aug 2023 16:40:00 -0700 Subject: [PATCH 09/36] Split G_ExitLevel into G_BeginLevelExit and G_FinishExitLevel - Save retry condition in G_BeginLevelExit - Apply condition in G_FinishExitLevel Preparation for ACS level end scripts, since the exit condition will need be known when the countdown starts, not when it ends (that'd be too late to do anything in the level). --- src/acs/call-funcs.cpp | 1 + src/d_netcmd.c | 2 +- src/doomstat.h | 7 ++ src/g_demo.c | 2 +- src/g_game.c | 168 ++++++++++++++++++++++------------------- src/g_game.h | 3 +- src/k_grandprix.c | 6 ++ src/lua_baselib.c | 3 +- src/p_saveg.c | 8 ++ src/p_setup.c | 3 + src/p_user.c | 13 +--- src/typedef.h | 1 + 12 files changed, 126 insertions(+), 91 deletions(-) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 747d74144..dff63f890 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1802,6 +1802,7 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor if (argV[1] == 0) skipstats = 1; + G_BeginLevelExit(); exitcountdown = 1; if (server) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f49647810..582f3d98d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5776,7 +5776,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) if (G_GamestateUsesExitLevel() == false) return; - G_ExitLevel(); + G_FinishExitLevel(); } static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) diff --git a/src/doomstat.h b/src/doomstat.h index c4f8b37e7..01c2acfaa 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -762,8 +762,15 @@ extern UINT8 maxXtraLife; // Max extra lives from rings extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations +struct exitcondition_t +{ + boolean losing; + boolean retry; +}; + // For racing extern tic_t racecountdown, exitcountdown, musiccountdown; +extern exitcondition_t g_exit; #define DEFAULT_GRAVITY (4*FRACUNIT/5) extern fixed_t gravity; diff --git a/src/g_demo.c b/src/g_demo.c index 7f58d289e..de15625c0 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -4161,7 +4161,7 @@ boolean G_CheckDemoStatus(void) I_Quit(); if (multiplayer && !demo.title) - G_ExitLevel(); + G_FinishExitLevel(); else { G_StopDemo(); diff --git a/src/g_game.c b/src/g_game.c index 08fc60159..1d4218062 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -274,6 +274,7 @@ mobj_t *hunt2; mobj_t *hunt3; tic_t racecountdown, exitcountdown, musiccountdown; // for racing +exitcondition_t g_exit; fixed_t gravity; fixed_t mapobjectscale; @@ -2927,95 +2928,108 @@ void G_AddPlayer(INT32 playernum) demo_extradata[playernum] |= DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything } -void G_ExitLevel(void) +void G_BeginLevelExit(void) +{ + g_exit.losing = true; + g_exit.retry = false; + + if (grandprixinfo.gp == true) + { + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + K_PlayerFinishGrandPrix(&players[i]); + } + } + } + + if (!G_GametypeUsesLives() || skipstats != 0) + { + g_exit.losing = false; // never force a retry + } + else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS)) + { + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && !players[i].bot) + { + if (!K_IsPlayerLosing(&players[i])) + { + g_exit.losing = false; + break; + } + } + } + } + else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE) + { + g_exit.losing = (grandprixinfo.wonround != true); + } + + if (g_exit.losing) + { + // You didn't win... + + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && !players[i].bot) + { + if (players[i].lives > 0) + { + g_exit.retry = true; + break; + } + } + } + } + + if (g_exit.losing && specialstageinfo.valid) + { + exitcountdown = TICRATE; + } + else + { + exitcountdown = raceexittime+1; + } +} + +void G_FinishExitLevel(void) { G_ResetAllDeviceRumbles(); if (gamestate == GS_LEVEL) { - UINT8 i; - boolean doretry = false; - - if (grandprixinfo.gp == true) + if (g_exit.retry) { - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator) - { - K_PlayerFinishGrandPrix(&players[i]); - } - } - } - - if (!G_GametypeUsesLives() || skipstats != 0) - ; // never force a retry - else if (specialstageinfo.valid == true || (gametyperules & GTR_BOSS)) - { - doretry = true; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator && !players[i].bot) - { - if (!K_IsPlayerLosing(&players[i])) - { - doretry = false; - break; - } - } - } - } - else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE) - { - doretry = (grandprixinfo.wonround != true); - } - - if (doretry) - { - // You didn't win... - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator && !players[i].bot) - { - if (players[i].lives > 0) - { - break; - } - } - } - - if (i == MAXPLAYERS) - { - // GAME OVER, try again from the start! - if (grandprixinfo.gp == true - && grandprixinfo.eventmode == GPEVENT_SPECIAL) - { - // We were in a Special Stage. - // We can still progress to the podium when we game over here. - doretry = false; - } - else if (netgame) - { - ; // Restart cup here whenever we do Online GP - } - else - { - // Back to the menu with you. - G_HandleSaveLevel(true); - D_QuitNetGame(); - CL_Reset(); - D_ClearState(); - M_StartControlPanel(); - } - } - else + // Restart cup here whenever we do Online GP + if (!netgame) { // We have lives, just redo this one course. G_SetRetryFlag(); + return; } + } + else if (g_exit.losing) + { + // We were in a Special Stage. + // We can still progress to the podium when we game over here. + const boolean special = grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_SPECIAL; - if (doretry == true) + if (!netgame && !special) { + // Back to the menu with you. + G_HandleSaveLevel(true); + D_QuitNetGame(); + CL_Reset(); + D_ClearState(); + M_StartControlPanel(); return; } } diff --git a/src/g_game.h b/src/g_game.h index 72e9ce005..0b922b4da 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -206,7 +206,8 @@ boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); INT16 G_SometimesGetDifferentEncore(void); -void G_ExitLevel(void); +void G_BeginLevelExit(void); +void G_FinishExitLevel(void); void G_NextLevel(void); void G_GetNextMap(void); void G_Continue(void); diff --git a/src/k_grandprix.c b/src/k_grandprix.c index dfbb65669..0d4b4b654 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -844,6 +844,12 @@ boolean K_CanChangeRules(boolean allowdemos) --------------------------------------------------*/ void K_PlayerFinishGrandPrix(player_t *player) { + if (grandprixinfo.wonround == true) + { + // This was already completed. + return; + } + if (player->exiting == false) { // You did not finish diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9af9ccd32..bd1771a89 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2869,7 +2869,8 @@ static int lib_gExitLevel(lua_State *L) // Moved this bit to G_SetCustomExitVars if (n >= 1) // Don't run the reset to defaults option lib_gSetCustomExitVars(L); - G_ExitLevel(); + G_BeginLevelExit(); + G_FinishExitLevel(); return 0; } diff --git a/src/p_saveg.c b/src/p_saveg.c index f948c7fa0..aa1f970c5 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -5731,6 +5731,10 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITEUINT32(save->p, racecountdown); WRITEUINT32(save->p, exitcountdown); + // exitcondition_t + WRITEUINT8(save->p, g_exit.losing); + WRITEUINT8(save->p, g_exit.retry); + WRITEFIXED(save->p, gravity); WRITEFIXED(save->p, mapobjectscale); @@ -5906,6 +5910,10 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) racecountdown = READUINT32(save->p); exitcountdown = READUINT32(save->p); + // exitcondition_t + g_exit.losing = READUINT8(save->p); + g_exit.retry = READUINT8(save->p); + gravity = READFIXED(save->p); mapobjectscale = READFIXED(save->p); diff --git a/src/p_setup.c b/src/p_setup.c index 48caf293a..727fddb7e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7505,6 +7505,9 @@ static void P_InitLevelSettings(void) racecountdown = exitcountdown = musiccountdown = exitfadestarted = 0; curlap = bestlap = 0; // SRB2Kart + g_exit.losing = false; + g_exit.retry = false; + // Gamespeed and frantic items gamespeed = KARTSPEED_EASY; franticitems = false; diff --git a/src/p_user.c b/src/p_user.c index f7a7967b6..4129881c3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1318,19 +1318,12 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) if (P_CheckRacers() && !exitcountdown) { - if (specialout == true) - { - exitcountdown = TICRATE; - } - else - { - exitcountdown = raceexittime+1; - } + G_BeginLevelExit(); } } else if (!exitcountdown) // All other gametypes { - exitcountdown = raceexittime+1; + G_BeginLevelExit(); } if (grandprixinfo.gp == true && player->bot == false && losing == false) @@ -3777,7 +3770,7 @@ void P_DoTimeOver(player_t *player) if (!exitcountdown) { - exitcountdown = raceexittime; + G_BeginLevelExit(); } } diff --git a/src/typedef.h b/src/typedef.h index 7bbc232b6..ee15f0ab4 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -134,6 +134,7 @@ TYPEDEF (unloaded_mapheader_t); TYPEDEF (tolinfo_t); TYPEDEF (cupheader_t); TYPEDEF (unloaded_cupheader_t); +TYPEDEF (exitcondition_t); // font.h TYPEDEF (font_t); From 9b4367773cdb4f5fa0ad5ee56a95e8cf2c5da25c Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Aug 2023 17:00:03 -0700 Subject: [PATCH 10/36] Add GAMEOVER ACS script type Triggered when the level ends with a losing condition and there are no extra lives. --- src/acs/interface.cpp | 16 ++++++++++++++++ src/acs/interface.h | 11 +++++++++++ src/acs/thread.hpp | 1 + src/g_game.c | 8 ++++++++ 4 files changed, 36 insertions(+) diff --git a/src/acs/interface.cpp b/src/acs/interface.cpp index bba86c3b9..4c14a8e2e 100644 --- a/src/acs/interface.cpp +++ b/src/acs/interface.cpp @@ -315,6 +315,22 @@ void ACS_RunEmeraldScript(mobj_t *mo) map->scriptStartType(ACS_ST_EMERALD, scriptInfo); } +/*-------------------------------------------------- + void ACS_RunGameOverScript(void) + + See header file for description. +--------------------------------------------------*/ +void ACS_RunGameOverScript(void) +{ + Environment *env = &ACSEnv; + + ACSVM::GlobalScope *const global = env->getGlobalScope(0); + ACSVM::HubScope *const hub = global->getHubScope(0); + ACSVM::MapScope *const map = hub->getMapScope(0); + + map->scriptStartType(ACS_ST_GAMEOVER, {}); +} + /*-------------------------------------------------- void ACS_Tick(void) diff --git a/src/acs/interface.h b/src/acs/interface.h index 206414663..5e9c9ba9f 100644 --- a/src/acs/interface.h +++ b/src/acs/interface.h @@ -179,6 +179,17 @@ void ACS_RunCatcherScript(mobj_t *mo); void ACS_RunEmeraldScript(mobj_t *mo); +/*-------------------------------------------------- + void ACS_RunGameOverScript(void); + + Runs the map's special scripts for exiting + the level, due to a losing condition and + without any extra lives to retry. +--------------------------------------------------*/ + +void ACS_RunGameOverScript(void); + + /*-------------------------------------------------- void ACS_Tick(void); diff --git a/src/acs/thread.hpp b/src/acs/thread.hpp index 3c146ddf6..561945cf0 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -42,6 +42,7 @@ enum acs_scriptType_e ACS_ST_OVERTIME = 7, // OVERTIME: Runs when Overtime starts in timed game modes. ACS_ST_UFO = 8, // UFO: Runs when the UFO Catcher is destroyed in a Special Stage. ACS_ST_EMERALD = 9, // EMERALD: Runs when the Chaos Emerald is collected in a Special Stage. + ACS_ST_GAMEOVER = 10, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life. }; // diff --git a/src/g_game.c b/src/g_game.c index 1d4218062..a248e4907 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2998,6 +2998,14 @@ void G_BeginLevelExit(void) { exitcountdown = raceexittime+1; } + + if (g_exit.losing) + { + if (!g_exit.retry) + { + ACS_RunGameOverScript(); + } + } } void G_FinishExitLevel(void) From 1a3ef843a586154d72951c1d1f8481499aa7a0b9 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Aug 2023 17:46:44 -0700 Subject: [PATCH 11/36] acs/call-funcs.cpp: add StopLevelExit function --- src/acs/call-funcs.cpp | 14 ++++++++++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 1 + 3 files changed, 16 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index dff63f890..88424cf2d 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1857,6 +1857,20 @@ bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word return false; } +/*-------------------------------------------------- + bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Halts the level exit if it's happening. +--------------------------------------------------*/ +bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argV; + (void)argC; + + exitcountdown = 0; + return false; +} + /*-------------------------------------------------- bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index bb9aa23b5..9c75cbed0 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -87,6 +87,7 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 78ffa2256..87ff3cdaf 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -171,6 +171,7 @@ Environment::Environment() addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle)); addFuncDataACS0( 504, addCallFunc(CallFunc_MapWarp)); addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot)); + addFuncDataACS0( 506, addCallFunc(CallFunc_StopLevelExit)); } ACSVM::Thread *Environment::allocThread() From 2ffc41b52e52b1e0a4733ec5b05e78d4c4b405f2 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Aug 2023 17:50:20 -0700 Subject: [PATCH 12/36] G_MapNumber: let parse NEXTMAP --- src/g_game.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index a248e4907..569bae9a4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -806,9 +806,7 @@ const char *G_BuildMapName(INT32 map) */ INT32 G_MapNumber(const char * name) { -#ifdef NEXTMAPINSOC if (strncasecmp("NEXTMAP_", name, 8) != 0) -#endif { INT32 map; UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME); @@ -827,7 +825,6 @@ INT32 G_MapNumber(const char * name) return NEXTMAP_INVALID; } -#ifdef NEXTMAPINSOC name += 8; if (strcasecmp("EVALUATION", name) == 0) @@ -836,9 +833,10 @@ INT32 G_MapNumber(const char * name) return NEXTMAP_CREDITS; if (strcasecmp("CEREMONY", name) == 0) return NEXTMAP_CEREMONY; - //if (strcasecmp("TITLE", name) == 0) + if (strcasecmp("TITLE", name) == 0) return NEXTMAP_TITLE; -#endif + + return NEXTMAP_INVALID; } /** Clips the console player's mouse aiming to the current view. From 0b9d691ad61a35d33bdb201f9ec56fe37547486d Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Aug 2023 00:56:05 -0700 Subject: [PATCH 13/36] acs/call-funcs.cpp: add ExitLevel function --- src/acs/call-funcs.cpp | 30 ++++++++++++++++++++++++++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 1 + 3 files changed, 32 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 88424cf2d..4103be255 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1871,6 +1871,36 @@ bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSV return false; } +/*-------------------------------------------------- + bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Exits the level. +--------------------------------------------------*/ +bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argV; + (void)argC; + + if (exitcountdown == 1) + { + // An exit is already in progress. + return false; + } + + if (argC >= 1) + { + skipstats = (argV[0] == 0); + } + + G_BeginLevelExit(); + exitcountdown = 1; + + if (server) + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + + return false; +} + /*-------------------------------------------------- bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 9c75cbed0..864c0b2c8 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -88,6 +88,7 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 87ff3cdaf..1a43f1a2b 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -172,6 +172,7 @@ Environment::Environment() addFuncDataACS0( 504, addCallFunc(CallFunc_MapWarp)); addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot)); addFuncDataACS0( 506, addCallFunc(CallFunc_StopLevelExit)); + addFuncDataACS0( 507, addCallFunc(CallFunc_ExitLevel)); } ACSVM::Thread *Environment::allocThread() From 99d9d4452b6737c40d7fe303c289b870cb498d3c Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Aug 2023 14:55:54 -0700 Subject: [PATCH 14/36] Offline: intermission doesn't move automatically, skip with A button - Press A button the first time to do the card flip. - Press A button a second time to end the intermission. - Will not end literally immediately, in order to let tally sounds finish and GP progression bar animate. --- src/y_inter.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/y_inter.c b/src/y_inter.c index 391d0861b..6c980a7ee 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -71,6 +71,7 @@ static patch_t *widebgpatch = NULL; static patch_t *bgtile = NULL; // SPECTILE/SRB2BACK static patch_t *interpic = NULL; // custom picture defined in map header +#define INFINITE_TIMER (INT16_MAX) // just some arbitrarily large value that won't easily overflow static INT32 timer; static INT32 powertype = PWRLV_DISABLED; @@ -86,6 +87,16 @@ intertype_t intertype = int_none; static huddrawlist_h luahuddrawlist_intermission; +static boolean Y_CanSkipIntermission(void) +{ + if (!netgame) + { + return true; + } + + return false; +} + static void Y_UnloadData(void); // @@ -1533,6 +1544,17 @@ finalcounter: } } + if (Y_CanSkipIntermission()) + { + K_drawButton( + 2*FRACUNIT, + (BASEVIDHEIGHT - 16)*FRACUNIT, + 0, + kp_button_a[1], + M_MenuConfirmHeld(0) + ); + } + else { const INT32 tickDown = (timer + 1)/TICRATE; @@ -1578,6 +1600,46 @@ void Y_Ticker(void) LUA_HOOK(IntermissionThinker); + if (Y_CanSkipIntermission()) + { + if (M_MenuConfirmPressed(0)) + { + // If there is a roundqueue, make time for it. + // Else, end instantly on button press. + // Actually, give it a slight delay, so the "kaching" sound isn't cut off. + const tic_t end = roundqueue.size != 0 ? 3*TICRATE : TICRATE; + + if (intertic == -1) // card flip hasn't started + { + if (sorttic != -1) + { + intertic = sorttic; + } + else + { + intertic = 0; + timer = end; + } + } + else if (timer >= INFINITE_TIMER && intertic >= sorttic + 16) // card done flipping + { + const INT32 kaching = sorttic + 16 + (2*TICRATE); + + if (intertic < kaching) + { + intertic = kaching; // kaching immediately + } + + timer = end; + } + } + + if (intertic == -1) + { + return; + } + } + intertic++; // Team scramble code for team match and CTF. @@ -1590,7 +1652,7 @@ void Y_Ticker(void) P_DoTeamscrambling(); }*/ - if ((timer && !--timer) + if ((timer < INFINITE_TIMER && --timer <= 0) || (intertic == endtic)) { Y_EndIntermission(); @@ -1665,6 +1727,12 @@ void Y_Ticker(void) r++; data.jitter[data.num[q]] = 1; + // Player can skip the tally, kaching! + if (Y_CanSkipIntermission() && timer < INFINITE_TIMER) + { + data.increase[data.num[q]] = 0; + } + if (powertype != PWRLV_DISABLED) { // Power Levels @@ -1815,6 +1883,21 @@ void Y_StartIntermission(void) sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); } + // TODO: code's a mess, I'm just making it extra clear + // that this piece of code is supposed to take priority + // over the above. :) + if (Y_CanSkipIntermission()) + { + timer = INFINITE_TIMER; // doesn't count down + + if (sorttic != -1) + { + // Will start immediately, but must be triggered. + // Needs to be TICRATE to bypass a condition in Y_Ticker. + sorttic = TICRATE; + } + } + // We couldn't display the intermission even if we wanted to. // But we still need to give the players their score bonuses, dummy. //if (dedicated) return; From dbe80ab3a29d0892946215ee721b9f1b66dd3d9f Mon Sep 17 00:00:00 2001 From: SteelT Date: Sun, 20 Aug 2023 19:43:05 -0400 Subject: [PATCH 15/36] Update Catch2 to latest version Fixes build errors related to it --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c35ff50f..d3c9be35b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,7 @@ if(SRB2_CONFIG_ENABLE_TESTS) # https://github.com/catchorg/Catch2 CPMAddPackage( NAME Catch2 - VERSION 3.1.1 + VERSION 3.4.0 GITHUB_REPOSITORY catchorg/Catch2 OPTIONS "CATCH_INSTALL_DOCS OFF" From f67c3df3ed805c4a5da9d4e8c744fa5b38a1f9c7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Aug 2023 16:46:01 -0700 Subject: [PATCH 16/36] HWR_ProjectSprite: fix rollangle for non-papersprites --- src/hardware/hw_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 976d210c2..580f0e0ad 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -4773,7 +4773,7 @@ static void HWR_ProjectSprite(mobj_t *thing) if (spriterotangle != 0 && !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))) { - rollangle = R_GetRollAngle(vflip + rollangle = R_GetRollAngle(papersprite == vflip ? spriterotangle : InvAngle(spriterotangle)); rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle); From 78ee9a7e0b689cee449fc94ec99122fdd646d3a0 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Aug 2023 17:18:39 -0700 Subject: [PATCH 17/36] G_StartTitleCard: don't play titlecard sfx for Podium --- src/g_game.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_game.c b/src/g_game.c index 08fc60159..aa4241ef2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1284,6 +1284,7 @@ void G_StartTitleCard(void) ST_startTitleCard(); // play the sound + if (gamestate != GS_CEREMONY) { sfxenum_t kstart = sfx_kstart; if (K_CheckBossIntro() == true) From 2b02daca148e62e075694863974a23f9a71d9b51 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 20 Aug 2023 17:40:43 -0700 Subject: [PATCH 18/36] P_LoadLevel: don't randomize Podium music --- src/p_setup.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 48caf293a..1b9542bd3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8148,7 +8148,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (K_PodiumSequence()) { // mapmusrng is set by local player position in K_ResetCeremony - P_ResetLevelMusic(); P_LoadLevelMusic(); } else if (gamestate == GS_LEVEL) From 5f9f573f20671dbf10b12ce4936852e0882d2e37 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 21 Aug 2023 00:44:27 -0700 Subject: [PATCH 19/36] Remove all code related to linedef 413, Change music --- src/p_setup.c | 62 -------------------------------------- src/p_spec.c | 82 --------------------------------------------------- 2 files changed, 144 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 727fddb7e..048f0a54e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1203,46 +1203,6 @@ static void P_LoadSidedefs(UINT8 *data) sd->toptexture = sd->midtexture = sd->bottomtexture = 0; break; - case 413: // Change music - { - if (!isfrontside) - break; - - char process[8+1]; - - sd->toptexture = sd->midtexture = sd->bottomtexture = 0; - if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0') - { - M_Memcpy(process,msd->bottomtexture,8); - process[8] = '\0'; - sd->bottomtexture = get_number(process); - } - - if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0') - { - M_Memcpy(process,msd->midtexture,8); - process[8] = '\0'; - sd->midtexture = get_number(process); - } - - if (msd->toptexture[0] != '-' && msd->toptexture[1] != '\0') - { - sd->line->stringargs[0] = Z_Malloc(7, PU_LEVEL, NULL); - M_Memcpy(process,msd->toptexture,8); - process[8] = '\0'; - - // If they type in O_ or D_ and their music name, just shrug, - // then copy the rest instead. - if ((process[0] == 'O' || process[0] == 'D') && process[7]) - M_Memcpy(sd->line->stringargs[0], process+2, 6); - else // Assume it's a proper music name. - M_Memcpy(sd->line->stringargs[0], process, 6); - sd->line->stringargs[0][6] = '\0'; - } - - break; - } - case 414: // Play SFX { sd->toptexture = sd->midtexture = sd->bottomtexture = 0; @@ -5569,28 +5529,6 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS; lines[i].args[4] = lines[i].frontsector->ceilingheight >> FRACBITS; break; - case 413: //Change music - if (lines[i].flags & ML_NOCLIMB) - lines[i].args[1] |= TMM_ALLPLAYERS; - if (lines[i].flags & ML_SKEWTD) - lines[i].args[1] |= TMM_OFFSET; - if (lines[i].flags & ML_NOSKEW) - lines[i].args[1] |= TMM_FADE; - if (lines[i].flags & ML_BLOCKPLAYERS) - lines[i].args[1] |= TMM_NORELOAD; - if (lines[i].flags & ML_NOTBOUNCY) - lines[i].args[1] |= TMM_FORCERESET; - if (lines[i].flags & ML_MIDSOLID) - lines[i].args[1] |= TMM_NOLOOP; - if (lines[i].flags & ML_MIDPEG) - lines[i].args[1] |= TMM_NOCREDIT; - lines[i].args[2] = sides[lines[i].sidenum[0]].midtexture; - lines[i].args[3] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; - lines[i].args[4] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS; - lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0; - lines[i].args[6] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1; - lines[i].args[7] = sides[lines[i].sidenum[0]].bottomtexture; - break; case 414: //Play sound effect lines[i].args[3] = tag; if (tag != 0) diff --git a/src/p_spec.c b/src/p_spec.c index 0d08cdcc0..5bb9131cb 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2886,88 +2886,6 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha } break; - case 413: // Change music - // FIXME: port to new music system -#if 0 - // console player only unless TMM_ALLPLAYERS is set - if ((args[1] & TMM_ALLPLAYERS) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction) - { - boolean musicsame = (!stringargs[0] || !stringargs[0][0] || !strnicmp(stringargs[0], S_MusicName(), 7)); - UINT16 tracknum = (UINT16)max(args[7], 0); - INT32 position = (INT32)max(args[2], 0); - UINT32 prefadems = (UINT32)max(args[3], 0); - UINT32 postfadems = (UINT32)max(args[4], 0); - UINT8 fadetarget = (UINT8)max(args[5], 0); - INT16 fadesource = (INT16)max(args[6], -1); - - // Seek offset from current song position - if (args[1] & TMM_OFFSET) - { - // adjust for loop point if subtracting - if (position < 0 && S_GetMusicLength() && - S_GetMusicPosition() > S_GetMusicLoopPoint() && - S_GetMusicPosition() + position < S_GetMusicLoopPoint()) - position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0); - else - position = max(S_GetMusicPosition() + position, 0); - } - - // Fade current music to target volume (if music won't be changed) - if ((args[1] & TMM_FADE) && fadetarget && musicsame) - { - // 0 fadesource means fade from current volume. - // meaning that we can't specify volume 0 as the source volume -- this starts at 1. - if (!fadesource) - fadesource = -1; - - if (!postfadems) - S_SetInternalMusicVolume(fadetarget); - else - S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); - - if (position) - S_SetMusicPosition(position); - } - // Change the music and apply position/fade operations - else - { - if (!stringargs[0]) - break; - - strncpy(mapmusname, stringargs[0], 7); - mapmusname[6] = 0; - - mapmusflags = tracknum & MUSIC_TRACKMASK; - if (!(args[1] & TMM_NORELOAD)) - mapmusflags |= MUSIC_RELOADRESET; - if (args[1] & TMM_FORCERESET) - mapmusflags |= MUSIC_FORCERESET; - - mapmusposition = position; - mapmusresume = 0; - - S_ChangeMusicEx(mapmusname, mapmusflags, !(args[1] & TMM_NOLOOP), position, - !(args[1] & TMM_FADE) ? prefadems : 0, - !(args[1] & TMM_FADE) ? postfadems : 0); - - if (!(args[1] & TMM_NOCREDIT)) - S_ShowMusicCredit(); - - if ((args[1] & TMM_FADE) && fadetarget) - { - if (!postfadems) - S_SetInternalMusicVolume(fadetarget); - else - S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); - } - } - - // Except, you can use the TMM_NORELOAD flag to change this behavior. - // if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn. - } -#endif - break; - case 414: // Play SFX P_PlaySFX(stringargs[0] ? get_number(stringargs[0]) : sfx_None, mo, callsec, args[3], args[1], args[2]); break; From f234c47d8a8877292cfcaf413797179f6d576506 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 21 Aug 2023 01:04:19 -0700 Subject: [PATCH 20/36] ACS: add Music_Play, Music_StopAll and Music_Remap --- src/acs/call-funcs.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ src/acs/call-funcs.hpp | 3 +++ src/acs/environment.cpp | 3 +++ 3 files changed, 48 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 4103be255..b122c9943 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -43,6 +43,7 @@ #include "../k_podium.h" #include "../k_bot.h" #include "../z_zone.h" +#include "../music.h" #include "call-funcs.hpp" @@ -1901,6 +1902,47 @@ bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W return false; } +/*-------------------------------------------------- + bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Play a tune. If it's already playing, restart from the + beginning. +--------------------------------------------------*/ +bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + ACSVM::MapScope *map = thread->scopeMap; + + Music_Play(map->getString(argV[0])->str); + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Stop every tune that is currently playing. +--------------------------------------------------*/ +bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + Music_StopAll(); + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Change the actual song lump that a tune will play. +--------------------------------------------------*/ +bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + ACSVM::MapScope *map = thread->scopeMap; + + Music_Remap(map->getString(argV[0])->str, map->getString(argV[1])->str); + + return false; +} + /*-------------------------------------------------- bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 864c0b2c8..b67302f47 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -89,6 +89,9 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_StopLevelExit(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 1a43f1a2b..bfa296485 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -173,6 +173,9 @@ Environment::Environment() addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot)); addFuncDataACS0( 506, addCallFunc(CallFunc_StopLevelExit)); addFuncDataACS0( 507, addCallFunc(CallFunc_ExitLevel)); + addFuncDataACS0( 508, addCallFunc(CallFunc_MusicPlay)); + addFuncDataACS0( 509, addCallFunc(CallFunc_MusicStopAll)); + addFuncDataACS0( 510, addCallFunc(CallFunc_MusicRemap)); } ACSVM::Thread *Environment::allocThread() From 3ccf668fdaf1e16beacf73d734fdae62a1d4ff4d Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 22 Aug 2023 23:03:46 +0100 Subject: [PATCH 21/36] Rename numspraycans to nummapspraycans - Consistency with nummaprings - Prevents confusion with incoming gamedata struct variable --- src/doomstat.h | 2 +- src/g_game.c | 2 +- src/p_mobj.c | 8 ++++---- src/p_setup.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index bd96e3a5d..c6bbbd997 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -713,7 +713,7 @@ extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins -extern UINT8 numspraycans; +extern UINT8 nummapspraycans; extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 redscore; ///< Red Team Scores diff --git a/src/g_game.c b/src/g_game.c index a692feefc..807f58814 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -219,7 +219,7 @@ UINT32 bluescore, redscore; // CTF and Team Match team scores // ring count... for PERFECT! INT32 nummaprings = 0; -UINT8 numspraycans = 0; +UINT8 nummapspraycans = 0; // Elminates unnecessary searching. boolean CheckForBustableBlocks; diff --git a/src/p_mobj.c b/src/p_mobj.c index 5d0ec3224..a5ae4b881 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12914,10 +12914,10 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) } case MT_SPRAYCAN: { - if (numspraycans) + if (nummapspraycans) { - if (numspraycans != UINT8_MAX) - numspraycans++; + if (nummapspraycans != UINT8_MAX) + nummapspraycans++; P_RemoveMobj(mobj); return false; @@ -12926,7 +12926,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) P_SetScale(mobj, mobj->destscale = 2*mobj->scale); P_SprayCanInit(mobj); - numspraycans++; + nummapspraycans++; break; } case MT_SKYBOX: diff --git a/src/p_setup.c b/src/p_setup.c index 23337787b..3cd7c5ef4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -816,7 +816,7 @@ static void P_SpawnMapThings(boolean spawnemblems) if (spawnemblems) { - if (numspraycans == 0) + if (nummapspraycans == 0) { UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; @@ -825,7 +825,7 @@ static void P_SpawnMapThings(boolean spawnemblems) CONS_Alert(CONS_WARNING, "SPRAY CANS: Map has assigned Spray Cans but no pickup placed!\n"); } } - else if (numspraycans > 1) + else if (nummapspraycans > 1) CONS_Alert(CONS_ERROR, "SPRAY CANS: Map has too many Spray Cans (%d)!", numspraycans); } } @@ -7425,7 +7425,7 @@ static void P_InitLevelSettings(void) maptargets = numtargets = 0; battleprisons = false; - numspraycans = 0; + nummapspraycans = 0; // emerald hunt hunt1 = hunt2 = hunt3 = NULL; From 6735868e267f5a057ddeeb5ab2251951c1d8f5b3 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 00:31:10 +0100 Subject: [PATCH 22/36] MT_EMBLEM: Adjut to not run anything if not local --- src/p_inter.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 3358af6fd..a3ef26de0 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -604,15 +604,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupEmblem(player, special->health - 1)) return; - if (P_IsLocalPlayer(player)) + if (!P_IsLocalPlayer(player)) { - if (!gamedata->collected[special->health-1]) - { - gamedata->collected[special->health-1] = true; - if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) - S_StartSound(NULL, sfx_ncitem); - gamedata->deferredsave = true; - } + // Must be party. + return; + } + + if (!gamedata->collected[special->health-1]) + { + gamedata->collected[special->health-1] = true; + if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) + S_StartSound(NULL, sfx_ncitem); + gamedata->deferredsave = true; } // Don't delete the object, just fade it. From e1b7cb66cb5f88c35f1c71414238bc8a2b7593c1 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 00:46:46 +0100 Subject: [PATCH 23/36] Rework the Spray Can system to ACTUALLY be what we want Instead of being specific to each level, Spray Cans are stored in a list on gamedata that will be stepped along each Spray Can you collect. They are only assigned to a level on collection - which prevents you from farming the same easy location for every colour in the game. In addition, this new system is one step short of dehardcoding them entirely. Now only Spray Cans specifically asked for by the existence of UC_SPRAYCAN will be put in the list, and if a secondary parameter is either "Yes" or "True", it will be put at the head of the list. This could technically support custom skincolours one day, but the author of this commit doesn't care to do the last bit of work necessary to make it happen. There's a slight extra overhead in that skincolor_t now also holds a `cache_spraycan` (renamed from `cachedcan`, which maps had previously) Currently, there's no safeguard against grabbing it on a custom course - you'll lose the Spray Can as soon as you load a fresh game again - but I consider that easy to fix (tomorrow) and necessary before merger, because the author of this commit does NOT want complaints on release because we forgot to protect users who keep on losing their skincolors. --- src/deh_lua.c | 1 + src/deh_soc.c | 8 +- src/deh_tables.c | 1 - src/doomdef.h | 5 +- src/doomstat.h | 2 +- src/g_game.c | 87 ++++++++---- src/info.c | 336 +++++++++++++++++++++++------------------------ src/k_menudraw.c | 13 +- src/m_cond.c | 230 ++++++++++++++------------------ src/m_cond.h | 7 +- src/p_inter.c | 49 ++++--- src/p_mobj.c | 27 ++-- src/p_setup.c | 23 ++-- 13 files changed, 413 insertions(+), 376 deletions(-) diff --git a/src/deh_lua.c b/src/deh_lua.c index 7b9555104..db3d4cbc5 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -142,6 +142,7 @@ static inline int lib_freeslot(lua_State *L) CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word); FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); strcpy(FREE_SKINCOLORS[i],word); + skincolors[i].cache_spraycan = UINT16_MAX; numskincolors++; lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT + i); r++; diff --git a/src/deh_soc.c b/src/deh_soc.c index a092384f6..027ff2d7a 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -216,10 +216,10 @@ void clear_levels(void) } } - if (gamedata) + if (gamedata && gamedata->spraycans) { UINT16 i; - for (i = 1; i < MAXCANCOLORS; i++) + for (i = 0; i < gamedata->numspraycans; i++) gamedata->spraycans[i].map = 0; } @@ -326,6 +326,7 @@ void readfreeslots(MYFILE *f) CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word); FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); strcpy(FREE_SKINCOLORS[i],word); + skincolors[i].cache_spraycan = UINT16_MAX; numskincolors++; break; } @@ -2625,6 +2626,9 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) PARAMCHECK(1); ty = UC_SPRAYCAN; re = get_skincolor(params[1]); + + // Force at head of the list? + x1 = (params[2] && (params[2][0] == 'Y' || params[2][0] == 'T')) ? 1 : 0; } else if ((offset=0) || fastcmp(params[0], "AND") || (++offset && fastcmp(params[0], "COMMA"))) diff --git a/src/deh_tables.c b/src/deh_tables.c index 0149e1ea3..f20d45ee7 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6521,7 +6521,6 @@ struct int_const_s const INT_CONST[] = { // SKINCOLOR_ doesn't include these..! {"MAXSKINCOLORS",MAXSKINCOLORS}, - {"MAXCANCOLORS",MAXCANCOLORS}, {"FIRSTSUPERCOLOR",FIRSTSUPERCOLOR}, {"NUMSUPERCOLORS",NUMSUPERCOLORS}, diff --git a/src/doomdef.h b/src/doomdef.h index c9f8b1702..9fffb78b4 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -232,6 +232,7 @@ struct skincolor_t UINT8 invshade; // Signpost color shade UINT16 chatcolor; // Chat color boolean accessible; // Accessible by the color command + setup menu + UINT16 cache_spraycan; // Cache for associated spraycan id }; #define FOLLOWERCOLOR_MATCH UINT16_MAX @@ -346,9 +347,7 @@ typedef enum SKINCOLOR_BLOSSOM, SKINCOLOR_TAFFY, - MAXCANCOLORS, - - FIRSTSUPERCOLOR = MAXCANCOLORS, + FIRSTSUPERCOLOR, // Super special awesome Super flashing colors! SKINCOLOR_SUPERSILVER1 = FIRSTSUPERCOLOR, diff --git a/src/doomstat.h b/src/doomstat.h index c6bbbd997..7d14f1d5a 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -532,7 +532,7 @@ struct mapheader_t UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. UINT32 _saveid; ///< Purely assistive in gamedata save processes - UINT16 cachedcan; ///< Cached Spraycan ID + UINT16 cache_spraycan; ///< Cached Spraycan ID // Lua information UINT8 numCustomOptions; ///< Internal. For Lua custom value support. diff --git a/src/g_game.c b/src/g_game.c index 807f58814..17d16b47a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4801,44 +4801,69 @@ void G_LoadGameData(void) if (versionMinor > 5) { - UINT16 numgamedatacans = READUINT32(save.p); + gamedata->gotspraycans = 0; + gamedata->numspraycans = READUINT16(save.p); + Z_Free(gamedata->spraycans); - if (numgamedatacans != MAXCANCOLORS - 1) + if (gamedata->numspraycans) { - save.p += (1 + 4) * numgamedatacans; - } - else - { - for (i = 1; i < MAXCANCOLORS; i++) + gamedata->spraycans = Z_Malloc( + (gamedata->numspraycans * sizeof(candata_t)), + PU_STATIC, NULL); + + for (i = 0; i < gamedata->numspraycans; i++) { - gamedata->spraycans[i].got = (boolean)READUINT8(save.p); - gamedata->spraycans[i].map = 0; + gamedata->spraycans[i].col = SKINCOLOR_NONE; + gamedata->spraycans[i].map = NEXTMAP_INVALID; + UINT16 col = READUINT16(save.p); UINT32 _saveid = READUINT32(save.p); + if (col < SKINCOLOR_FIRSTFREESLOT) + { + gamedata->spraycans[i].col = col; + skincolors[col].cache_spraycan = i; + } + if (_saveid >= numgamedatamapheaders) { - //CONS_Printf("LOAD - Color %s - id %u, map 0 (invalid id)\n", skincolors[i].name, _saveid); + // Can has not been grabbed on any map, this is intentional. continue; } UINT16 map = tempmapidreferences[_saveid]; if (map >= nummapheaders || !mapheaderinfo[map]) { - //CONS_Printf("LOAD - Color %s - id %u, map 0 (unloaded header)\n", skincolors[i].name, _saveid); + //CONS_Printf("LOAD - Can %u, color %s - id %u (unloaded header)\n", i, skincolors[col].name, _saveid); continue; } - //CONS_Printf("LOAD - Color %s - id %u, map %d\n", skincolors[i].name, _saveid, map+1); + //CONS_Printf("LOAD - Can %u, color %s - id %u, map %d\n", i, skincolors[col].name, _saveid, map); - gamedata->spraycans[i].map = map+1; - mapheaderinfo[map]->cachedcan = i; + gamedata->spraycans[i].map = map; - numgamedatacans--; // this one was successfully placed + if (gamedata->gotspraycans != i) + { + //CONS_Printf("LOAD - Swapping gotten can %u, color %s with prior ungotten can %u\n", i, skincolors[col].name, gamedata->gotspraycans); + + // All grabbed cans should be at the head of the list. + // Let's swap with the can the disjoint occoured at. + // This will prevent a gap from occouring on reload. + candata_t copycan = gamedata->spraycans[gamedata->gotspraycans]; + gamedata->spraycans[gamedata->gotspraycans] = gamedata->spraycans[i]; + gamedata->spraycans[i] = copycan; + + mapheaderinfo[copycan.map]->cache_spraycan = i; + } + + mapheaderinfo[map]->cache_spraycan = gamedata->gotspraycans; + + gamedata->gotspraycans++; } - - gamedata->allspraycansplaced = (numgamedatacans == 0); - //CONS_Printf("CCC - all spray cans placed? %c\n", gamedata->allspraycansplaced ? 'Y' : 'N'); + } + else + { + gamedata->spraycans = NULL; } } @@ -5093,7 +5118,7 @@ void G_SaveGameData(void) for (i = 0; i < nummapheaders; i++) { // No spraycan attached. - if (mapheaderinfo[i]->cachedcan == 0 + if (mapheaderinfo[i]->cache_spraycan >= gamedata->numspraycans // 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. && !(mapheaderinfo[i]->records.mapvisited & MV_MAX)) { @@ -5124,8 +5149,12 @@ void G_SaveGameData(void) length += 4 + (numgamedatamapheaders * (MAXMAPLUMPNAME+1+4+4)); + length += 2; - length += 4 + ((MAXCANCOLORS - 1) * (1 + 4)); + if (gamedata->numspraycans) + { + length += (gamedata->numspraycans * (2 + 4)); + } UINT32 numgamedatacups = 0; @@ -5322,7 +5351,7 @@ void G_SaveGameData(void) for (i = 0; i < nummapheaders; i++) { - if (mapheaderinfo[i]->cachedcan == 0 + if (mapheaderinfo[i]->cache_spraycan >= gamedata->numspraycans && !(mapheaderinfo[i]->records.mapvisited & MV_MAX)) continue; @@ -5363,25 +5392,25 @@ void G_SaveGameData(void) } } } + + WRITEUINT16(save.p, gamedata->numspraycans); // 2 - WRITEUINT32(save.p, MAXCANCOLORS - 1); // 4 + // gamedata->numspraycans * (2 + 4) - // (MAXCANCOLORS - 1) * (1 + 4) - - for (i = 1; i < MAXCANCOLORS; i++) + for (i = 0; i < gamedata->numspraycans; i++) { - WRITEUINT8(save.p, gamedata->spraycans[i].got); + WRITEUINT16(save.p, gamedata->spraycans[i].col); UINT32 _saveid = UINT32_MAX; UINT16 map = gamedata->spraycans[i].map; - if (map > 0 && map <= nummapheaders && mapheaderinfo[map - 1]) + if (map < nummapheaders && mapheaderinfo[map]) { - _saveid = mapheaderinfo[map - 1]->_saveid; + _saveid = mapheaderinfo[map]->_saveid; } - //CONS_Printf("SAVE - Color %s - id %u, map %d\n", skincolors[i].name, _saveid, map); + //CONS_Printf("SAVE - Can %u, color %s - id %u, map %d\n", i, skincolors[gamedata->spraycans[i].col].name, _saveid, map); WRITEUINT32(save.p, _saveid); } diff --git a/src/info.c b/src/info.c index b64175e49..509a84571 100644 --- a/src/info.c +++ b/src/info.c @@ -30314,189 +30314,189 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }; skincolor_t skincolors[MAXSKINCOLORS] = { - {"Default", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_NONE + {"Default", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_NONE - {"White", { 0, 0, 0, 0, 1, 2, 5, 8, 9, 11, 14, 17, 20, 22, 25, 28}, SKINCOLOR_BLACK, 8, 0, true}, // SKINCOLOR_WHITE - {"Silver", { 0, 1, 2, 3, 5, 7, 9, 12, 13, 15, 18, 20, 23, 25, 27, 30}, SKINCOLOR_NICKEL, 8, 0, true}, // SKINCOLOR_SILVER - {"Grey", { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, SKINCOLOR_GREY, 8, V_LAVENDERMAP, true}, // SKINCOLOR_GREY - {"Nickel", { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, SKINCOLOR_SILVER, 8, V_GRAYMAP, true}, // SKINCOLOR_NICKEL - {"Black", { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, SKINCOLOR_WHITE, 8, V_GRAYMAP, true}, // SKINCOLOR_BLACK - {"Skunk", { 0, 1, 2, 3, 4, 10, 16, 21, 23, 24, 25, 26, 27, 28, 29, 31}, SKINCOLOR_VOMIT, 8, V_GRAYMAP, true}, // SKINCOLOR_SKUNK - {"Fairy", { 0, 0, 252, 252, 200, 201, 211, 14, 16, 18, 20, 22, 24, 26, 28, 31}, SKINCOLOR_ARTICHOKE, 12, V_PINKMAP, true}, // SKINCOLOR_FAIRY - {"Popcorn", { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, SKINCOLOR_PIGEON, 12, V_TANMAP, true}, // SKINCOLOR_POPCORN - {"Artichoke", { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, SKINCOLOR_FAIRY, 12, V_GREENMAP, true}, // SKINCOLOR_ARTICHOKE - {"Pigeon", { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, SKINCOLOR_POPCORN, 12, V_SKYMAP, true}, // SKINCOLOR_PIGEON - {"Sepia", { 0, 1, 3, 5, 7, 9, 241, 242, 243, 245, 247, 249, 236, 237, 238, 239}, SKINCOLOR_LEATHER, 6, V_TANMAP, true}, // SKINCOLOR_SEPIA - {"Beige", { 0, 208, 216, 217, 240, 241, 242, 243, 245, 247, 249, 250, 251, 237, 238, 239}, SKINCOLOR_BROWN, 2, V_BROWNMAP, true}, // SKINCOLOR_BEIGE - {"Caramel", {208, 48, 216, 217, 218, 220, 221, 223, 224, 226, 228, 230, 232, 234, 236, 239}, SKINCOLOR_CERULEAN, 8, V_TANMAP, true}, // SKINCOLOR_CARAMEL - {"Peach", { 0, 208, 48, 216, 218, 221, 212, 213, 214, 215, 206, 207, 197, 198, 199, 254}, SKINCOLOR_CYAN, 8, V_TANMAP, true}, // SKINCOLOR_PEACH - {"Brown", {216, 217, 219, 221, 224, 225, 227, 229, 230, 232, 234, 235, 237, 239, 29, 30}, SKINCOLOR_BEIGE, 8, V_BROWNMAP, true}, // SKINCOLOR_BROWN - {"Leather", {218, 221, 224, 227, 229, 231, 233, 235, 237, 239, 28, 28, 29, 29, 30, 31}, SKINCOLOR_SEPIA, 8, V_BROWNMAP, true}, // SKINCOLOR_LEATHER - {"Pink", { 0, 208, 208, 209, 209, 210, 211, 211, 212, 213, 214, 215, 41, 43, 45, 46}, SKINCOLOR_PISTACHIO, 8, V_PINKMAP, true}, // SKINCOLOR_PINK - {"Rose", {209, 210, 211, 211, 212, 213, 214, 215, 41, 42, 43, 44, 45, 71, 46, 47}, SKINCOLOR_MOSS, 8, V_PINKMAP, true}, // SKINCOLOR_ROSE - {"Cinnamon", {216, 221, 224, 226, 228, 60, 61, 43, 44, 45, 71, 46, 47, 29, 30, 31}, SKINCOLOR_WRISTWATCH, 6, V_REDMAP, true}, // SKINCOLOR_CINNAMON - {"Ruby", { 0, 208, 209, 210, 211, 213, 39, 40, 41, 43, 186, 186, 169, 169, 253, 254}, SKINCOLOR_SAPPHIRE, 8, V_REDMAP, true}, // SKINCOLOR_RUBY - {"Raspberry", { 0, 208, 209, 210, 32, 33, 34, 35, 37, 39, 41, 43, 44, 45, 46, 47}, SKINCOLOR_MINT, 8, V_REDMAP, true}, // SKINCOLOR_RASPBERRY - {"Red", {209, 210, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44 , 45, 71, 46, 47}, SKINCOLOR_GREEN, 6, V_REDMAP, true}, // SKINCOLOR_RED - {"Crimson", {210, 33, 35, 38, 40, 42, 43, 45, 71, 71, 46, 46, 47, 47, 30, 31}, SKINCOLOR_PINETREE, 6, V_REDMAP, true}, // SKINCOLOR_CRIMSON - {"Maroon", { 32, 33, 35, 37, 39, 41, 43, 237, 26, 26, 27, 27, 28, 29, 30, 31}, SKINCOLOR_TOXIC, 8, V_REDMAP, true}, // SKINCOLOR_MAROON - {"Lemonade", { 0, 80, 81, 82, 83, 216, 210, 211, 212, 213, 214, 215, 43, 44, 71, 47}, SKINCOLOR_THUNDER, 8, V_PINKMAP, true}, // SKINCOLOR_LEMONADE - {"Scarlet", { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, SKINCOLOR_ALGAE, 10, V_REDMAP, true}, // SKINCOLOR_SCARLET - {"Ketchup", { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, SKINCOLOR_MUSTARD, 10, V_REDMAP, true}, // SKINCOLOR_KETCHUP - {"Dawn", { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, SKINCOLOR_DUSK, 8, V_ORANGEMAP, true}, // SKINCOLOR_DAWN - {"Sunslam", { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, SKINCOLOR_MOONSET, 8, V_ORANGEMAP, true}, // SKINCOLOR_SUNSLAM - {"Creamsicle", { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, SKINCOLOR_PERIWINKLE, 8, V_ORANGEMAP, true}, // SKINCOLOR_CREAMSICLE - {"Orange", {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, SKINCOLOR_BLUE, 8, V_ORANGEMAP, true}, // SKINCOLOR_ORANGE - {"Rosewood", { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, SKINCOLOR_MIDNIGHT, 6, V_ORANGEMAP, true}, // SKINCOLOR_ROSEWOOD - {"Tangerine", { 80, 81, 82, 83, 64, 51, 52, 54, 55, 57, 58, 60, 61, 63, 71, 47}, SKINCOLOR_LIME, 8, V_ORANGEMAP, true}, // SKINCOLOR_TANGERINE - {"Tan", { 0, 80, 81, 82, 83, 84, 85, 86, 87, 245, 246, 248, 249, 251, 237, 239}, SKINCOLOR_RUST, 8, V_TANMAP, true}, // SKINCOLOR_TAN - {"Cream", { 0, 80, 80, 81, 81, 49, 51, 222, 224, 227, 230, 233, 236, 239, 29, 31}, SKINCOLOR_COPPER, 10, V_TANMAP, true}, // SKINCOLOR_CREAM - {"Gold", { 0, 80, 81, 83, 64, 65, 66, 67, 68, 215, 69, 70, 44, 71, 46, 47}, SKINCOLOR_SLATE, 8, V_GOLDMAP, true}, // SKINCOLOR_GOLD - {"Royal", { 80, 81, 83, 64, 65, 223, 229, 196, 196, 197, 197, 198, 199, 29, 30, 31}, SKINCOLOR_PLATINUM, 6, V_GOLDMAP, true}, // SKINCOLOR_ROYAL - {"Bronze", { 83, 64, 65, 66, 67, 215, 69, 70, 44, 44, 45, 71, 46, 47, 29, 31}, SKINCOLOR_STEEL, 8, V_GOLDMAP, true}, // SKINCOLOR_BRONZE - {"Copper", { 0, 82, 64, 65, 67, 68, 70, 237, 239, 28, 28, 29, 29, 30, 30, 31}, SKINCOLOR_CREAM, 6, V_GOLDMAP, true}, // SKINCOLOR_COPPER - {"Yellow", { 0, 80, 81, 82, 83, 73, 84, 74, 64, 65, 66, 67, 68, 69, 70, 71}, SKINCOLOR_AQUAMARINE, 8, V_YELLOWMAP, true}, // SKINCOLOR_YELLOW - {"Mustard", { 80, 81, 82, 83, 64, 65, 65, 76, 76, 77, 77, 78, 79, 237, 239, 29}, SKINCOLOR_KETCHUP, 8, V_YELLOWMAP, true}, // SKINCOLOR_MUSTARD - {"Banana", { 80, 81, 83, 72, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 30}, SKINCOLOR_EMERALD, 8, V_YELLOWMAP, true}, // SKINCOLOR_BANANA - {"Olive", { 80, 82, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 28, 29, 31}, SKINCOLOR_TEAL, 8, V_YELLOWMAP, true}, // SKINCOLOR_OLIVE - {"Crocodile", { 0, 80, 81, 88, 88, 188, 189, 76, 76, 77, 78, 79, 236, 237, 238, 239}, SKINCOLOR_VIOLET, 8, V_YELLOWMAP, true}, // SKINCOLOR_CROCODILE - {"Peridot", { 0, 80, 81, 88, 188, 189, 190, 191, 94, 94, 95, 95, 109, 110, 111, 31}, SKINCOLOR_NAVY, 6, V_GREENMAP, true}, // SKINCOLOR_PERIDOT - {"Vomit", { 0, 208, 216, 209, 218, 51, 65, 76, 191, 191, 126, 143, 138, 175, 169, 254}, SKINCOLOR_SKUNK, 8, V_GREENMAP, true}, // SKINCOLOR_VOMIT - {"Garden", { 81, 82, 83, 73, 64, 65, 66, 92, 92, 93, 93, 94, 95, 109, 110, 111}, SKINCOLOR_LAVENDER, 6, V_GREENMAP, true}, // SKINCOLOR_GARDEN - {"Lime", { 0, 80, 81, 88, 188, 189, 114, 114, 115, 115, 116, 116, 117, 118, 119, 111}, SKINCOLOR_TANGERINE, 8, V_GREENMAP, true}, // SKINCOLOR_LIME - {"Handheld", { 83, 72, 73, 74, 75, 76, 102, 104, 105, 106, 107, 108, 109, 110, 111, 31}, SKINCOLOR_ULTRAMARINE, 8, V_GREENMAP, true}, // SKINCOLOR_HANDHELD - {"Tea", { 0, 80, 80, 81, 88, 89, 90, 91, 92, 93, 94, 95, 109, 110, 111, 31}, SKINCOLOR_BLOSSOM, 8, V_GREENMAP, true}, // SKINCOLOR_TEA - {"Pistachio", { 0, 80, 88, 88, 89, 90, 91, 102, 103, 104, 105, 106, 107, 108, 109, 110}, SKINCOLOR_PINK, 6, V_GREENMAP, true}, // SKINCOLOR_PISTACHIO - {"Moss", { 88, 89, 90, 91, 91, 92, 93, 94, 107, 107, 108, 108, 109, 109, 110, 111}, SKINCOLOR_ROSE, 8, V_GREENMAP, true}, // SKINCOLOR_MOSS - {"Camouflage", {208, 84, 85, 240, 241, 243, 245, 94, 107, 108, 108, 109, 109, 110, 110, 111}, SKINCOLOR_CAMOUFLAGE, 8, V_GREENMAP, true}, // SKINCOLOR_CAMOUFLAGE - {"Mint", { 0, 88, 88, 89, 89, 100, 101, 102, 125, 126, 143, 143, 138, 175, 169, 254}, SKINCOLOR_RASPBERRY, 8, V_GREENMAP, true}, // SKINCOLOR_MINT - {"Green", { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}, SKINCOLOR_RED, 8, V_GREENMAP, true}, // SKINCOLOR_GREEN - {"Pinetree", { 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 30, 30, 31}, SKINCOLOR_CRIMSON, 8, V_GREENMAP, true}, // SKINCOLOR_PINETREE - {"Turtle", { 96, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 119, 111}, SKINCOLOR_MAGENTA, 8, V_GREENMAP, true}, // SKINCOLOR_TURTLE - {"Swamp", { 96, 112, 113, 114, 115, 116, 117, 118, 119, 119, 29, 29, 30, 30, 31, 31}, SKINCOLOR_BYZANTIUM, 8, V_GREENMAP, true}, // SKINCOLOR_SWAMP - {"Dream", { 0, 0, 208, 208, 48, 89, 98, 100, 148, 148, 172, 172, 173, 173, 174, 175}, SKINCOLOR_POMEGRANATE, 8, V_GREENMAP, true}, // SKINCOLOR_DREAM - {"Plague", { 80, 88, 96, 112, 113, 124, 142, 149, 149, 173, 174, 175, 169, 253, 254, 31}, SKINCOLOR_NOVA, 8, V_GREENMAP, true}, // SKINCOLOR_PLAGUE - {"Emerald", { 0, 120, 121, 112, 113, 114, 115, 125, 125, 126, 126, 127, 138, 175, 253, 254}, SKINCOLOR_BANANA, 8, V_GREENMAP, true}, // SKINCOLOR_EMERALD - {"Algae", {128, 129, 130, 131, 132, 133, 134, 115, 115, 116, 116, 117, 118, 119, 110, 111}, SKINCOLOR_SCARLET, 10, V_GREENMAP, true}, // SKINCOLOR_ALGAE - {"Aquamarine", { 0, 128, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 118, 118, 119, 111}, SKINCOLOR_YELLOW, 8, V_AQUAMAP, true}, // SKINCOLOR_AQUAMARINE - {"Turquoise", {128, 120, 121, 122, 123, 141, 141, 142, 142, 143, 143, 138, 138, 139, 139, 31}, SKINCOLOR_MAUVE, 10, V_AQUAMAP, true}, // SKINCOLOR_TURQUOISE - {"Teal", { 0, 120, 120, 121, 140, 141, 142, 143, 143, 138, 138, 139, 139, 254, 254, 31}, SKINCOLOR_OLIVE, 8, V_AQUAMAP, true}, // SKINCOLOR_TEAL - {"Robin", { 0, 80, 81, 82, 83, 88, 121, 140, 133, 133, 134, 135, 136, 137, 138, 139}, SKINCOLOR_THISTLE, 8, V_SKYMAP, true}, // SKINCOLOR_ROBIN - {"Cyan", { 0, 0, 128, 128, 255, 131, 132, 134, 142, 142, 143, 127, 118, 119, 110, 111}, SKINCOLOR_PEACH, 8, V_SKYMAP, true}, // SKINCOLOR_CYAN - {"Jawz", { 0, 0, 128, 128, 129, 146, 133, 134, 135, 149, 149, 173, 173, 174, 175, 31}, SKINCOLOR_LILAC, 10, V_SKYMAP, true}, // SKINCOLOR_JAWZ - {"Cerulean", { 0, 128, 129, 130, 131, 132, 133, 135, 136, 136, 137, 137, 138, 138, 139, 31}, SKINCOLOR_CARAMEL, 8, V_SKYMAP, true}, // SKINCOLOR_CERULEAN - {"Navy", {128, 129, 130, 132, 134, 135, 136, 137, 137, 138, 138, 139, 139, 29, 30, 31}, SKINCOLOR_PERIDOT, 8, V_SKYMAP, true}, // SKINCOLOR_NAVY - {"Platinum", { 0, 0, 0, 144, 144, 145, 9, 11, 14, 142, 136, 137, 138, 138, 139, 31}, SKINCOLOR_ROYAL, 8, V_GRAYMAP, true}, // SKINCOLOR_PLATINUM - {"Slate", { 0, 0, 144, 144, 144, 145, 145, 145, 170, 170, 171, 171, 172, 173, 174, 175}, SKINCOLOR_GOLD, 10, 0, true}, // SKINCOLOR_SLATE - {"Steel", { 0, 144, 144, 145, 145, 170, 170, 171, 171, 172, 172, 173, 173, 174, 175, 31}, SKINCOLOR_BRONZE, 10, V_GRAYMAP, true}, // SKINCOLOR_STEEL - {"Thunder", { 80, 81, 82, 83, 64, 65, 11, 171, 172, 173, 173, 157, 158, 159, 254, 31}, SKINCOLOR_LEMONADE, 8, V_GOLDMAP, true}, // SKINCOLOR_THUNDER - {"Nova", { 0, 83, 49, 50, 51, 32, 192, 148, 148, 172, 173, 174, 175, 29, 30, 31}, SKINCOLOR_PLAGUE, 10, V_BLUEMAP, true}, // SKINCOLOR_NOVA - {"Rust", {208, 48, 216, 217, 240, 241, 242, 171, 172, 173, 24, 25, 26, 28, 29, 31}, SKINCOLOR_TAN, 8, V_BROWNMAP, true}, // SKINCOLOR_RUST - {"Wristwatch", { 48, 218, 221, 224, 227, 231, 196, 173, 173, 174, 159, 159, 253, 253, 254, 31}, SKINCOLOR_CINNAMON, 8, V_BROWNMAP, true}, // SKINCOLOR_WRISTWATCH - {"Jet", {145, 146, 147, 148, 149, 173, 173, 174, 175, 175, 28, 28, 29, 29, 30, 31}, SKINCOLOR_TAFFY, 8, V_GRAYMAP, true}, // SKINCOLOR_JET - {"Sapphire", { 0, 128, 129, 131, 133, 135, 149, 150, 152, 154, 156, 158, 159, 253, 254, 31}, SKINCOLOR_RUBY, 6, V_SKYMAP, true}, // SKINCOLOR_SAPPHIRE - {"Ultramarine", { 0, 0, 120, 120, 121, 133, 135, 149, 149, 166, 166, 167, 168, 169, 254, 31}, SKINCOLOR_HANDHELD, 10, V_SKYMAP, true}, // SKINCOLOR_ULTRAMARINE - {"Periwinkle", { 0, 0, 144, 144, 145, 146, 147, 149, 150, 152, 154, 155, 157, 159, 253, 254}, SKINCOLOR_CREAMSICLE, 8, V_BLUEMAP, true}, // SKINCOLOR_PERIWINKLE - {"Blue", {144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 156, 158, 253, 254, 31}, SKINCOLOR_ORANGE, 8, V_BLUEMAP, true}, // SKINCOLOR_BLUE - {"Midnight", {146, 148, 149, 150, 152, 153, 155, 157, 159, 253, 253, 254, 254, 31, 31, 31}, SKINCOLOR_ROSEWOOD, 8, V_BLUEMAP, true}, // SKINCOLOR_MIDNIGHT - {"Blueberry", { 0, 144, 145, 146, 147, 171, 172, 166, 166, 167, 167, 168, 168, 175, 169, 253}, SKINCOLOR_PURPLE, 8, V_BLUEMAP, true}, // SKINCOLOR_BLUEBERRY - {"Thistle", { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, SKINCOLOR_ROBIN, 8, V_PURPLEMAP, true}, // SKINCOLOR_THISTLE - {"Purple", { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, SKINCOLOR_BLUEBERRY, 10, V_PURPLEMAP, true}, // SKINCOLOR_PURPLE - {"Pastel", { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, SKINCOLOR_FUCHSIA, 11, V_PURPLEMAP, true}, // SKINCOLOR_PASTEL - {"Moonset", { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, SKINCOLOR_SUNSLAM, 10, V_MAGENTAMAP, true}, // SKINCOLOR_MOONSET - {"Dusk", {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, SKINCOLOR_DAWN, 6, V_MAGENTAMAP, true}, // SKINCOLOR_DUSK - {"Violet", {176, 177, 178, 179, 180, 181, 182, 183, 184, 165, 165, 166, 167, 168, 169, 254}, SKINCOLOR_CROCODILE, 8, V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET - {"Magenta", {252, 200, 177, 177, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 31}, SKINCOLOR_TURTLE, 8, V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA - {"Fuchsia", {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, SKINCOLOR_PASTEL, 11, V_MAGENTAMAP, true}, // SKINCOLOR_FUCHSIA - {"Toxic", { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, SKINCOLOR_MAROON, 8, V_LAVENDERMAP, true}, // SKINCOLOR_TOXIC - {"Mauve", { 80, 81, 82, 83, 64, 50, 201, 192, 193, 194, 195, 173, 174, 175, 253, 254}, SKINCOLOR_TURQUOISE, 8, V_LAVENDERMAP, true}, // SKINCOLOR_MAUVE - {"Lavender", {252, 177, 179, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, SKINCOLOR_GARDEN, 6, V_LAVENDERMAP, true}, // SKINCOLOR_LAVENDER - {"Byzantium", {145, 192, 193, 194, 195, 196, 197, 198, 199, 199, 29, 29, 30, 30, 31, 31}, SKINCOLOR_SWAMP, 8, V_LAVENDERMAP, true}, // SKINCOLOR_BYZANTIUM - {"Pomegranate", {208, 209, 210, 211, 212, 213, 214, 195, 195, 196, 196, 197, 198, 199, 29, 30}, SKINCOLOR_DREAM, 8, V_LAVENDERMAP, true}, // SKINCOLOR_POMEGRANATE - {"Lilac", { 0, 0, 0, 252, 252, 176, 200, 201, 179, 192, 193, 194, 195, 196, 197, 198}, SKINCOLOR_JAWZ, 6, V_PINKMAP, true}, // SKINCOLOR_LILAC - {"Blossom", { 0, 252, 252, 176, 200, 177, 201, 202, 202, 34, 36, 38, 40, 42, 45, 46}, SKINCOLOR_TEA, 8, V_PINKMAP, true}, // SKINCOLOR_BLOSSOM - {"Taffy", { 0, 252, 252, 200, 200, 201, 202, 203, 204, 204, 205, 206, 207, 43, 45, 47}, SKINCOLOR_JET, 8, V_PINKMAP, true}, // SKINCOLOR_TAFFY + {"White", { 0, 0, 0, 0, 1, 2, 5, 8, 9, 11, 14, 17, 20, 22, 25, 28}, SKINCOLOR_BLACK, 8, 0, true, UINT16_MAX}, // SKINCOLOR_WHITE + {"Silver", { 0, 1, 2, 3, 5, 7, 9, 12, 13, 15, 18, 20, 23, 25, 27, 30}, SKINCOLOR_NICKEL, 8, 0, true, UINT16_MAX}, // SKINCOLOR_SILVER + {"Grey", { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, SKINCOLOR_GREY, 8, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_GREY + {"Nickel", { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, SKINCOLOR_SILVER, 8, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_NICKEL + {"Black", { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, SKINCOLOR_WHITE, 8, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_BLACK + {"Skunk", { 0, 1, 2, 3, 4, 10, 16, 21, 23, 24, 25, 26, 27, 28, 29, 31}, SKINCOLOR_VOMIT, 8, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_SKUNK + {"Fairy", { 0, 0, 252, 252, 200, 201, 211, 14, 16, 18, 20, 22, 24, 26, 28, 31}, SKINCOLOR_ARTICHOKE, 12, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_FAIRY + {"Popcorn", { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, SKINCOLOR_PIGEON, 12, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_POPCORN + {"Artichoke", { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, SKINCOLOR_FAIRY, 12, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_ARTICHOKE + {"Pigeon", { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, SKINCOLOR_POPCORN, 12, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_PIGEON + {"Sepia", { 0, 1, 3, 5, 7, 9, 241, 242, 243, 245, 247, 249, 236, 237, 238, 239}, SKINCOLOR_LEATHER, 6, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_SEPIA + {"Beige", { 0, 208, 216, 217, 240, 241, 242, 243, 245, 247, 249, 250, 251, 237, 238, 239}, SKINCOLOR_BROWN, 2, V_BROWNMAP, true, UINT16_MAX}, // SKINCOLOR_BEIGE + {"Caramel", {208, 48, 216, 217, 218, 220, 221, 223, 224, 226, 228, 230, 232, 234, 236, 239}, SKINCOLOR_CERULEAN, 8, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_CARAMEL + {"Peach", { 0, 208, 48, 216, 218, 221, 212, 213, 214, 215, 206, 207, 197, 198, 199, 254}, SKINCOLOR_CYAN, 8, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_PEACH + {"Brown", {216, 217, 219, 221, 224, 225, 227, 229, 230, 232, 234, 235, 237, 239, 29, 30}, SKINCOLOR_BEIGE, 8, V_BROWNMAP, true, UINT16_MAX}, // SKINCOLOR_BROWN + {"Leather", {218, 221, 224, 227, 229, 231, 233, 235, 237, 239, 28, 28, 29, 29, 30, 31}, SKINCOLOR_SEPIA, 8, V_BROWNMAP, true, UINT16_MAX}, // SKINCOLOR_LEATHER + {"Pink", { 0, 208, 208, 209, 209, 210, 211, 211, 212, 213, 214, 215, 41, 43, 45, 46}, SKINCOLOR_PISTACHIO, 8, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_PINK + {"Rose", {209, 210, 211, 211, 212, 213, 214, 215, 41, 42, 43, 44, 45, 71, 46, 47}, SKINCOLOR_MOSS, 8, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_ROSE + {"Cinnamon", {216, 221, 224, 226, 228, 60, 61, 43, 44, 45, 71, 46, 47, 29, 30, 31}, SKINCOLOR_WRISTWATCH, 6, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_CINNAMON + {"Ruby", { 0, 208, 209, 210, 211, 213, 39, 40, 41, 43, 186, 186, 169, 169, 253, 254}, SKINCOLOR_SAPPHIRE, 8, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_RUBY + {"Raspberry", { 0, 208, 209, 210, 32, 33, 34, 35, 37, 39, 41, 43, 44, 45, 46, 47}, SKINCOLOR_MINT, 8, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_RASPBERRY + {"Red", {209, 210, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44 , 45, 71, 46, 47}, SKINCOLOR_GREEN, 6, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_RED + {"Crimson", {210, 33, 35, 38, 40, 42, 43, 45, 71, 71, 46, 46, 47, 47, 30, 31}, SKINCOLOR_PINETREE, 6, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_CRIMSON + {"Maroon", { 32, 33, 35, 37, 39, 41, 43, 237, 26, 26, 27, 27, 28, 29, 30, 31}, SKINCOLOR_TOXIC, 8, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_MAROON + {"Lemonade", { 0, 80, 81, 82, 83, 216, 210, 211, 212, 213, 214, 215, 43, 44, 71, 47}, SKINCOLOR_THUNDER, 8, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_LEMONADE + {"Scarlet", { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, SKINCOLOR_ALGAE, 10, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_SCARLET + {"Ketchup", { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, SKINCOLOR_MUSTARD, 10, V_REDMAP, true, UINT16_MAX}, // SKINCOLOR_KETCHUP + {"Dawn", { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, SKINCOLOR_DUSK, 8, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_DAWN + {"Sunslam", { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, SKINCOLOR_MOONSET, 8, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_SUNSLAM + {"Creamsicle", { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, SKINCOLOR_PERIWINKLE, 8, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_CREAMSICLE + {"Orange", {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, SKINCOLOR_BLUE, 8, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_ORANGE + {"Rosewood", { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, SKINCOLOR_MIDNIGHT, 6, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_ROSEWOOD + {"Tangerine", { 80, 81, 82, 83, 64, 51, 52, 54, 55, 57, 58, 60, 61, 63, 71, 47}, SKINCOLOR_LIME, 8, V_ORANGEMAP, true, UINT16_MAX}, // SKINCOLOR_TANGERINE + {"Tan", { 0, 80, 81, 82, 83, 84, 85, 86, 87, 245, 246, 248, 249, 251, 237, 239}, SKINCOLOR_RUST, 8, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_TAN + {"Cream", { 0, 80, 80, 81, 81, 49, 51, 222, 224, 227, 230, 233, 236, 239, 29, 31}, SKINCOLOR_COPPER, 10, V_TANMAP, true, UINT16_MAX}, // SKINCOLOR_CREAM + {"Gold", { 0, 80, 81, 83, 64, 65, 66, 67, 68, 215, 69, 70, 44, 71, 46, 47}, SKINCOLOR_SLATE, 8, V_GOLDMAP, true, UINT16_MAX}, // SKINCOLOR_GOLD + {"Royal", { 80, 81, 83, 64, 65, 223, 229, 196, 196, 197, 197, 198, 199, 29, 30, 31}, SKINCOLOR_PLATINUM, 6, V_GOLDMAP, true, UINT16_MAX}, // SKINCOLOR_ROYAL + {"Bronze", { 83, 64, 65, 66, 67, 215, 69, 70, 44, 44, 45, 71, 46, 47, 29, 31}, SKINCOLOR_STEEL, 8, V_GOLDMAP, true, UINT16_MAX}, // SKINCOLOR_BRONZE + {"Copper", { 0, 82, 64, 65, 67, 68, 70, 237, 239, 28, 28, 29, 29, 30, 30, 31}, SKINCOLOR_CREAM, 6, V_GOLDMAP, true, UINT16_MAX}, // SKINCOLOR_COPPER + {"Yellow", { 0, 80, 81, 82, 83, 73, 84, 74, 64, 65, 66, 67, 68, 69, 70, 71}, SKINCOLOR_AQUAMARINE, 8, V_YELLOWMAP, true, UINT16_MAX}, // SKINCOLOR_YELLOW + {"Mustard", { 80, 81, 82, 83, 64, 65, 65, 76, 76, 77, 77, 78, 79, 237, 239, 29}, SKINCOLOR_KETCHUP, 8, V_YELLOWMAP, true, UINT16_MAX}, // SKINCOLOR_MUSTARD + {"Banana", { 80, 81, 83, 72, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 30}, SKINCOLOR_EMERALD, 8, V_YELLOWMAP, true, UINT16_MAX}, // SKINCOLOR_BANANA + {"Olive", { 80, 82, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 28, 29, 31}, SKINCOLOR_TEAL, 8, V_YELLOWMAP, true, UINT16_MAX}, // SKINCOLOR_OLIVE + {"Crocodile", { 0, 80, 81, 88, 88, 188, 189, 76, 76, 77, 78, 79, 236, 237, 238, 239}, SKINCOLOR_VIOLET, 8, V_YELLOWMAP, true, UINT16_MAX}, // SKINCOLOR_CROCODILE + {"Peridot", { 0, 80, 81, 88, 188, 189, 190, 191, 94, 94, 95, 95, 109, 110, 111, 31}, SKINCOLOR_NAVY, 6, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_PERIDOT + {"Vomit", { 0, 208, 216, 209, 218, 51, 65, 76, 191, 191, 126, 143, 138, 175, 169, 254}, SKINCOLOR_SKUNK, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_VOMIT + {"Garden", { 81, 82, 83, 73, 64, 65, 66, 92, 92, 93, 93, 94, 95, 109, 110, 111}, SKINCOLOR_LAVENDER, 6, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_GARDEN + {"Lime", { 0, 80, 81, 88, 188, 189, 114, 114, 115, 115, 116, 116, 117, 118, 119, 111}, SKINCOLOR_TANGERINE, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_LIME + {"Handheld", { 83, 72, 73, 74, 75, 76, 102, 104, 105, 106, 107, 108, 109, 110, 111, 31}, SKINCOLOR_ULTRAMARINE, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_HANDHELD + {"Tea", { 0, 80, 80, 81, 88, 89, 90, 91, 92, 93, 94, 95, 109, 110, 111, 31}, SKINCOLOR_BLOSSOM, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_TEA + {"Pistachio", { 0, 80, 88, 88, 89, 90, 91, 102, 103, 104, 105, 106, 107, 108, 109, 110}, SKINCOLOR_PINK, 6, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_PISTACHIO + {"Moss", { 88, 89, 90, 91, 91, 92, 93, 94, 107, 107, 108, 108, 109, 109, 110, 111}, SKINCOLOR_ROSE, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_MOSS + {"Camouflage", {208, 84, 85, 240, 241, 243, 245, 94, 107, 108, 108, 109, 109, 110, 110, 111}, SKINCOLOR_CAMOUFLAGE, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_CAMOUFLAGE + {"Mint", { 0, 88, 88, 89, 89, 100, 101, 102, 125, 126, 143, 143, 138, 175, 169, 254}, SKINCOLOR_RASPBERRY, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_MINT + {"Green", { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}, SKINCOLOR_RED, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_GREEN + {"Pinetree", { 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 30, 30, 31}, SKINCOLOR_CRIMSON, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_PINETREE + {"Turtle", { 96, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 119, 111}, SKINCOLOR_MAGENTA, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_TURTLE + {"Swamp", { 96, 112, 113, 114, 115, 116, 117, 118, 119, 119, 29, 29, 30, 30, 31, 31}, SKINCOLOR_BYZANTIUM, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_SWAMP + {"Dream", { 0, 0, 208, 208, 48, 89, 98, 100, 148, 148, 172, 172, 173, 173, 174, 175}, SKINCOLOR_POMEGRANATE, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_DREAM + {"Plague", { 80, 88, 96, 112, 113, 124, 142, 149, 149, 173, 174, 175, 169, 253, 254, 31}, SKINCOLOR_NOVA, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_PLAGUE + {"Emerald", { 0, 120, 121, 112, 113, 114, 115, 125, 125, 126, 126, 127, 138, 175, 253, 254}, SKINCOLOR_BANANA, 8, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_EMERALD + {"Algae", {128, 129, 130, 131, 132, 133, 134, 115, 115, 116, 116, 117, 118, 119, 110, 111}, SKINCOLOR_SCARLET, 10, V_GREENMAP, true, UINT16_MAX}, // SKINCOLOR_ALGAE + {"Aquamarine", { 0, 128, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 118, 118, 119, 111}, SKINCOLOR_YELLOW, 8, V_AQUAMAP, true, UINT16_MAX}, // SKINCOLOR_AQUAMARINE + {"Turquoise", {128, 120, 121, 122, 123, 141, 141, 142, 142, 143, 143, 138, 138, 139, 139, 31}, SKINCOLOR_MAUVE, 10, V_AQUAMAP, true, UINT16_MAX}, // SKINCOLOR_TURQUOISE + {"Teal", { 0, 120, 120, 121, 140, 141, 142, 143, 143, 138, 138, 139, 139, 254, 254, 31}, SKINCOLOR_OLIVE, 8, V_AQUAMAP, true, UINT16_MAX}, // SKINCOLOR_TEAL + {"Robin", { 0, 80, 81, 82, 83, 88, 121, 140, 133, 133, 134, 135, 136, 137, 138, 139}, SKINCOLOR_THISTLE, 8, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_ROBIN + {"Cyan", { 0, 0, 128, 128, 255, 131, 132, 134, 142, 142, 143, 127, 118, 119, 110, 111}, SKINCOLOR_PEACH, 8, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_CYAN + {"Jawz", { 0, 0, 128, 128, 129, 146, 133, 134, 135, 149, 149, 173, 173, 174, 175, 31}, SKINCOLOR_LILAC, 10, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_JAWZ + {"Cerulean", { 0, 128, 129, 130, 131, 132, 133, 135, 136, 136, 137, 137, 138, 138, 139, 31}, SKINCOLOR_CARAMEL, 8, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_CERULEAN + {"Navy", {128, 129, 130, 132, 134, 135, 136, 137, 137, 138, 138, 139, 139, 29, 30, 31}, SKINCOLOR_PERIDOT, 8, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_NAVY + {"Platinum", { 0, 0, 0, 144, 144, 145, 9, 11, 14, 142, 136, 137, 138, 138, 139, 31}, SKINCOLOR_ROYAL, 8, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_PLATINUM + {"Slate", { 0, 0, 144, 144, 144, 145, 145, 145, 170, 170, 171, 171, 172, 173, 174, 175}, SKINCOLOR_GOLD, 10, 0, true, UINT16_MAX}, // SKINCOLOR_SLATE + {"Steel", { 0, 144, 144, 145, 145, 170, 170, 171, 171, 172, 172, 173, 173, 174, 175, 31}, SKINCOLOR_BRONZE, 10, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_STEEL + {"Thunder", { 80, 81, 82, 83, 64, 65, 11, 171, 172, 173, 173, 157, 158, 159, 254, 31}, SKINCOLOR_LEMONADE, 8, V_GOLDMAP, true, UINT16_MAX}, // SKINCOLOR_THUNDER + {"Nova", { 0, 83, 49, 50, 51, 32, 192, 148, 148, 172, 173, 174, 175, 29, 30, 31}, SKINCOLOR_PLAGUE, 10, V_BLUEMAP, true, UINT16_MAX}, // SKINCOLOR_NOVA + {"Rust", {208, 48, 216, 217, 240, 241, 242, 171, 172, 173, 24, 25, 26, 28, 29, 31}, SKINCOLOR_TAN, 8, V_BROWNMAP, true, UINT16_MAX}, // SKINCOLOR_RUST + {"Wristwatch", { 48, 218, 221, 224, 227, 231, 196, 173, 173, 174, 159, 159, 253, 253, 254, 31}, SKINCOLOR_CINNAMON, 8, V_BROWNMAP, true, UINT16_MAX}, // SKINCOLOR_WRISTWATCH + {"Jet", {145, 146, 147, 148, 149, 173, 173, 174, 175, 175, 28, 28, 29, 29, 30, 31}, SKINCOLOR_TAFFY, 8, V_GRAYMAP, true, UINT16_MAX}, // SKINCOLOR_JET + {"Sapphire", { 0, 128, 129, 131, 133, 135, 149, 150, 152, 154, 156, 158, 159, 253, 254, 31}, SKINCOLOR_RUBY, 6, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_SAPPHIRE + {"Ultramarine", { 0, 0, 120, 120, 121, 133, 135, 149, 149, 166, 166, 167, 168, 169, 254, 31}, SKINCOLOR_HANDHELD, 10, V_SKYMAP, true, UINT16_MAX}, // SKINCOLOR_ULTRAMARINE + {"Periwinkle", { 0, 0, 144, 144, 145, 146, 147, 149, 150, 152, 154, 155, 157, 159, 253, 254}, SKINCOLOR_CREAMSICLE, 8, V_BLUEMAP, true, UINT16_MAX}, // SKINCOLOR_PERIWINKLE + {"Blue", {144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 156, 158, 253, 254, 31}, SKINCOLOR_ORANGE, 8, V_BLUEMAP, true, UINT16_MAX}, // SKINCOLOR_BLUE + {"Midnight", {146, 148, 149, 150, 152, 153, 155, 157, 159, 253, 253, 254, 254, 31, 31, 31}, SKINCOLOR_ROSEWOOD, 8, V_BLUEMAP, true, UINT16_MAX}, // SKINCOLOR_MIDNIGHT + {"Blueberry", { 0, 144, 145, 146, 147, 171, 172, 166, 166, 167, 167, 168, 168, 175, 169, 253}, SKINCOLOR_PURPLE, 8, V_BLUEMAP, true, UINT16_MAX}, // SKINCOLOR_BLUEBERRY + {"Thistle", { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, SKINCOLOR_ROBIN, 8, V_PURPLEMAP, true, UINT16_MAX}, // SKINCOLOR_THISTLE + {"Purple", { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, SKINCOLOR_BLUEBERRY, 10, V_PURPLEMAP, true, UINT16_MAX}, // SKINCOLOR_PURPLE + {"Pastel", { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, SKINCOLOR_FUCHSIA, 11, V_PURPLEMAP, true, UINT16_MAX}, // SKINCOLOR_PASTEL + {"Moonset", { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, SKINCOLOR_SUNSLAM, 10, V_MAGENTAMAP, true, UINT16_MAX}, // SKINCOLOR_MOONSET + {"Dusk", {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, SKINCOLOR_DAWN, 6, V_MAGENTAMAP, true, UINT16_MAX}, // SKINCOLOR_DUSK + {"Violet", {176, 177, 178, 179, 180, 181, 182, 183, 184, 165, 165, 166, 167, 168, 169, 254}, SKINCOLOR_CROCODILE, 8, V_MAGENTAMAP, true, UINT16_MAX}, // SKINCOLOR_VIOLET + {"Magenta", {252, 200, 177, 177, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 31}, SKINCOLOR_TURTLE, 8, V_MAGENTAMAP, true, UINT16_MAX}, // SKINCOLOR_MAGENTA + {"Fuchsia", {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, SKINCOLOR_PASTEL, 11, V_MAGENTAMAP, true, UINT16_MAX}, // SKINCOLOR_FUCHSIA + {"Toxic", { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, SKINCOLOR_MAROON, 8, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_TOXIC + {"Mauve", { 80, 81, 82, 83, 64, 50, 201, 192, 193, 194, 195, 173, 174, 175, 253, 254}, SKINCOLOR_TURQUOISE, 8, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_MAUVE + {"Lavender", {252, 177, 179, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, SKINCOLOR_GARDEN, 6, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_LAVENDER + {"Byzantium", {145, 192, 193, 194, 195, 196, 197, 198, 199, 199, 29, 29, 30, 30, 31, 31}, SKINCOLOR_SWAMP, 8, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_BYZANTIUM + {"Pomegranate", {208, 209, 210, 211, 212, 213, 214, 195, 195, 196, 196, 197, 198, 199, 29, 30}, SKINCOLOR_DREAM, 8, V_LAVENDERMAP, true, UINT16_MAX}, // SKINCOLOR_POMEGRANATE + {"Lilac", { 0, 0, 0, 252, 252, 176, 200, 201, 179, 192, 193, 194, 195, 196, 197, 198}, SKINCOLOR_JAWZ, 6, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_LILAC + {"Blossom", { 0, 252, 252, 176, 200, 177, 201, 202, 202, 34, 36, 38, 40, 42, 45, 46}, SKINCOLOR_TEA, 8, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_BLOSSOM + {"Taffy", { 0, 252, 252, 200, 200, 201, 202, 203, 204, 204, 205, 206, 207, 43, 45, 47}, SKINCOLOR_JET, 8, V_PINKMAP, true, UINT16_MAX}, // SKINCOLOR_TAFFY // super (todo: replace these with the kart ones) - {"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0, false}, // SKINCOLOR_SUPERSILVER1 - {"Super Silver 2", {0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07}, SKINCOLOR_BLACK, 6, 0, false}, // SKINCOLOR_SUPERSILVER2 - {"Super Silver 3", {0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b}, SKINCOLOR_BLACK, 5, 0, false}, // SKINCOLOR_SUPERSILVER3 - {"Super Silver 4", {0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false}, // SKINCOLOR_SUPERSILVER4 - {"Super Silver 5", {0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false}, // SKINCOLOR_SUPERSILVER5 + {"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERSILVER1 + {"Super Silver 2", {0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07}, SKINCOLOR_BLACK, 6, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERSILVER2 + {"Super Silver 3", {0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b}, SKINCOLOR_BLACK, 5, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERSILVER3 + {"Super Silver 4", {0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSILVER4 + {"Super Silver 5", {0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSILVER5 - {"Super Red 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2}, SKINCOLOR_CYAN, 15, 0, false}, // SKINCOLOR_SUPERRED1 - {"Super Red 2", {0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21}, SKINCOLOR_CYAN, 14, V_PINKMAP, false}, // SKINCOLOR_SUPERRED2 - {"Super Red 3", {0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23}, SKINCOLOR_CYAN, 13, V_REDMAP, false}, // SKINCOLOR_SUPERRED3 - {"Super Red 4", {0x00, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24}, SKINCOLOR_CYAN, 11, V_REDMAP, false}, // SKINCOLOR_SUPERRED4 - {"Super Red 5", {0xd0, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25}, SKINCOLOR_CYAN, 10, V_REDMAP, false}, // SKINCOLOR_SUPERRED5 + {"Super Red 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2}, SKINCOLOR_CYAN, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERRED1 + {"Super Red 2", {0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21}, SKINCOLOR_CYAN, 14, V_PINKMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRED2 + {"Super Red 3", {0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23}, SKINCOLOR_CYAN, 13, V_REDMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRED3 + {"Super Red 4", {0x00, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24}, SKINCOLOR_CYAN, 11, V_REDMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRED4 + {"Super Red 5", {0xd0, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25}, SKINCOLOR_CYAN, 10, V_REDMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRED5 - {"Super Orange 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34}, SKINCOLOR_SAPPHIRE, 15, 0, false}, // SKINCOLOR_SUPERORANGE1 - {"Super Orange 2", {0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34}, SKINCOLOR_SAPPHIRE, 12, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE2 - {"Super Orange 3", {0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35}, SKINCOLOR_SAPPHIRE, 9, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE3 - {"Super Orange 4", {0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46}, SKINCOLOR_SAPPHIRE, 4, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE4 - {"Super Orange 5", {0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_SAPPHIRE, 3, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE5 + {"Super Orange 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34}, SKINCOLOR_SAPPHIRE, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERORANGE1 + {"Super Orange 2", {0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34}, SKINCOLOR_SAPPHIRE, 12, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERORANGE2 + {"Super Orange 3", {0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35}, SKINCOLOR_SAPPHIRE, 9, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERORANGE3 + {"Super Orange 4", {0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46}, SKINCOLOR_SAPPHIRE, 4, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERORANGE4 + {"Super Orange 5", {0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_SAPPHIRE, 3, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERORANGE5 - {"Super Gold 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x52, 0x53, 0x48}, SKINCOLOR_PERIWINKLE, 15, 0, false}, // SKINCOLOR_SUPERGOLD1 - {"Super Gold 2", {0x00, 0x50, 0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41}, SKINCOLOR_PERIWINKLE, 9, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD2 - {"Super Gold 3", {0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD3 - {"Super Gold 4", {0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD4 - {"Super Gold 5", {0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD5 + {"Super Gold 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x52, 0x53, 0x48}, SKINCOLOR_PERIWINKLE, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERGOLD1 + {"Super Gold 2", {0x00, 0x50, 0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41}, SKINCOLOR_PERIWINKLE, 9, V_YELLOWMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERGOLD2 + {"Super Gold 3", {0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERGOLD3 + {"Super Gold 4", {0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERGOLD4 + {"Super Gold 5", {0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_PERIWINKLE, 8, V_YELLOWMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERGOLD5 - {"Super Peridot 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc}, SKINCOLOR_BLUEBERRY, 15, 0, false}, // SKINCOLOR_SUPERPERIDOT1 - {"Super Peridot 2", {0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe}, SKINCOLOR_BLUEBERRY, 4, V_GREENMAP, false}, // SKINCOLOR_SUPERPERIDOT2 - {"Super Peridot 3", {0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false}, // SKINCOLOR_SUPERPERIDOT3 - {"Super Peridot 4", {0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false}, // SKINCOLOR_SUPERPERIDOT4 - {"Super Peridot 5", {0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f, 0x77}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false}, // SKINCOLOR_SUPERPERIDOT5 + {"Super Peridot 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc}, SKINCOLOR_BLUEBERRY, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERPERIDOT1 + {"Super Peridot 2", {0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe}, SKINCOLOR_BLUEBERRY, 4, V_GREENMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPERIDOT2 + {"Super Peridot 3", {0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPERIDOT3 + {"Super Peridot 4", {0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPERIDOT4 + {"Super Peridot 5", {0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f, 0x77}, SKINCOLOR_BLUEBERRY, 3, V_GREENMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPERIDOT5 - {"Super Sky 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84}, SKINCOLOR_RUST, 15, 0, false}, // SKINCOLOR_SUPERSKY1 - {"Super Sky 2", {0x00, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86}, SKINCOLOR_RUST, 4, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY2 - {"Super Sky 3", {0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY3 - {"Super Sky 4", {0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY4 - {"Super Sky 5", {0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a, 0x8b}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY5 + {"Super Sky 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84}, SKINCOLOR_RUST, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERSKY1 + {"Super Sky 2", {0x00, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86}, SKINCOLOR_RUST, 4, V_SKYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSKY2 + {"Super Sky 3", {0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87}, SKINCOLOR_RUST, 3, V_SKYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSKY3 + {"Super Sky 4", {0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a}, SKINCOLOR_RUST, 3, V_SKYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSKY4 + {"Super Sky 5", {0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a, 0x8b}, SKINCOLOR_RUST, 3, V_SKYMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERSKY5 - {"Super Purple 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa2}, SKINCOLOR_EMERALD, 15, 0, false}, // SKINCOLOR_SUPERPURPLE1 - {"Super Purple 2", {0x00, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5}, SKINCOLOR_EMERALD, 4, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE2 - {"Super Purple 3", {0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE3 - {"Super Purple 4", {0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE4 - {"Super Purple 5", {0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xfd}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE5 + {"Super Purple 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa2}, SKINCOLOR_EMERALD, 15, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERPURPLE1 + {"Super Purple 2", {0x00, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5}, SKINCOLOR_EMERALD, 4, V_PURPLEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPURPLE2 + {"Super Purple 3", {0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPURPLE3 + {"Super Purple 4", {0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPURPLE4 + {"Super Purple 5", {0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xfd}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERPURPLE5 - {"Super Rust 1", {0x00, 0xd0, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x32, 0x33, 0x37, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x2e}, SKINCOLOR_CYAN, 14, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST1 - {"Super Rust 2", {0x30, 0x31, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x47, 0x2e}, SKINCOLOR_CYAN, 10, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST2 - {"Super Rust 3", {0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x3a, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x2e, 0x2e}, SKINCOLOR_CYAN, 9, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST3 - {"Super Rust 4", {0x48, 0x40, 0x41, 0x42, 0x43, 0x44, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x2e, 0x2e, 0x2e}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST4 - {"Super Rust 5", {0x41, 0x42, 0x43, 0x43, 0x44, 0x44, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST5 + {"Super Rust 1", {0x00, 0xd0, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x32, 0x33, 0x37, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x2e}, SKINCOLOR_CYAN, 14, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRUST1 + {"Super Rust 2", {0x30, 0x31, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x47, 0x2e}, SKINCOLOR_CYAN, 10, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRUST2 + {"Super Rust 3", {0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x3a, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x2e, 0x2e}, SKINCOLOR_CYAN, 9, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRUST3 + {"Super Rust 4", {0x48, 0x40, 0x41, 0x42, 0x43, 0x44, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x2e, 0x2e, 0x2e}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRUST4 + {"Super Rust 5", {0x41, 0x42, 0x43, 0x43, 0x44, 0x44, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERRUST5 - {"Super Tan 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52}, SKINCOLOR_BROWN, 14, 0, false}, // SKINCOLOR_SUPERTAN1 - {"Super Tan 2", {0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5}, SKINCOLOR_BROWN, 13, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN2 - {"Super Tan 3", {0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9}, SKINCOLOR_BROWN, 12, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN3 - {"Super Tan 4", {0x51, 0x52, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed}, SKINCOLOR_BROWN, 11, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN4 - {"Super Tan 5", {0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef}, SKINCOLOR_BROWN, 10, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN5 + {"Super Tan 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52}, SKINCOLOR_BROWN, 14, 0, false, UINT16_MAX}, // SKINCOLOR_SUPERTAN1 + {"Super Tan 2", {0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5}, SKINCOLOR_BROWN, 13, V_BROWNMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERTAN2 + {"Super Tan 3", {0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9}, SKINCOLOR_BROWN, 12, V_BROWNMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERTAN3 + {"Super Tan 4", {0x51, 0x52, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed}, SKINCOLOR_BROWN, 11, V_BROWNMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERTAN4 + {"Super Tan 5", {0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef}, SKINCOLOR_BROWN, 10, V_BROWNMAP, false, UINT16_MAX}, // SKINCOLOR_SUPERTAN5 - {"Chaos Emerald 1", { 0, 88, 188, 98, 114, 116, 117, 119, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD1 - {"Chaos Emerald 2", { 0, 80, 82, 74, 65, 52, 56, 60, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD2 - {"Chaos Emerald 3", { 0, 252, 201, 179, 182, 183, 185, 187, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD3 - {"Chaos Emerald 4", { 0, 144, 146, 147, 149, 165, 167, 169, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD4 - {"Chaos Emerald 5", { 0, 1, 144, 4, 9, 170, 14, 21, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD5 - {"Chaos Emerald 6", { 0, 208, 50, 32, 34, 37, 40, 44, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD6 - {"Chaos Emerald 7", { 0, 120, 121, 140, 133, 135, 149, 156, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_CHAOSEMERALD7 + {"Chaos Emerald 1", { 0, 88, 188, 98, 114, 116, 117, 119, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD1 + {"Chaos Emerald 2", { 0, 80, 82, 74, 65, 52, 56, 60, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD2 + {"Chaos Emerald 3", { 0, 252, 201, 179, 182, 183, 185, 187, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD3 + {"Chaos Emerald 4", { 0, 144, 146, 147, 149, 165, 167, 169, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD4 + {"Chaos Emerald 5", { 0, 1, 144, 4, 9, 170, 14, 21, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD5 + {"Chaos Emerald 6", { 0, 208, 50, 32, 34, 37, 40, 44, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD6 + {"Chaos Emerald 7", { 0, 120, 121, 140, 133, 135, 149, 156, 0, 0, 0, 0, 0, 0, 0, 0}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_CHAOSEMERALD7 - {"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_INVINCFLASH + {"Invinc Flash", { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_INVINCFLASH - {"Position", { 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM - {"Position Win 1", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN1 - {"Position Win 2", {134, 134, 135, 135, 135, 136, 136, 136, 137, 137, 138, 138, 139, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN2 - {"Position Win 3", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_WIN3 - {"Position Lose 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE1 - {"Position Lose 2", { 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 44, 45, 46, 47, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE2 - {"Position Lose 3", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_LOSE3 - {"Position Best 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST1 - {"Position Best 2", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST2 - {"Position Best 3", {112, 112, 113, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 110, 111, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST3 - {"Position Best 4", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST4 - {"Position Best 5", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST5 - {"Position Best 6", {181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 29, 30}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_POSNUM_BEST6 + {"Position", { 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM + {"Position Win 1", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_WIN1 + {"Position Win 2", {134, 134, 135, 135, 135, 136, 136, 136, 137, 137, 138, 138, 139, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_WIN2 + {"Position Win 3", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_WIN3 + {"Position Lose 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_LOSE1 + {"Position Lose 2", { 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 44, 45, 46, 47, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_LOSE2 + {"Position Lose 3", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_LOSE3 + {"Position Best 1", { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 71, 46, 47, 29, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST1 + {"Position Best 2", { 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 236, 237, 238, 239, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST2 + {"Position Best 3", {112, 112, 113, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 110, 111, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST3 + {"Position Best 4", {255, 255, 122, 122, 123, 123, 141, 141, 142, 142, 143, 143, 138, 139, 254, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST4 + {"Position Best 5", {152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 159, 253, 254, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST5 + {"Position Best 6", {181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 29, 30}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_POSNUM_BEST6 - {"Intermission", {0,80,80,81,81,81,84,85,86,87,246,248,251,26,28,31}, SKINCOLOR_NONE, 0, 0, false} // SKINCOLOR_INTERMISSION + {"Intermission", {0,80,80,81,81,81,84,85,86,87,246,248,251,26,28,31}, SKINCOLOR_NONE, 0, 0, false, UINT16_MAX}, // SKINCOLOR_INTERMISSION }; /** Patches the mobjinfo, state, and skincolor tables. diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f5b3ee3d9..3681aaeb0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6117,11 +6117,16 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) if (hasmedals) x -= 4; - if (mapheaderinfo[mapnum]->cachedcan != 0 && mapheaderinfo[mapnum]->cachedcan < MAXCANCOLORS && gamedata->spraycans[mapheaderinfo[mapnum]->cachedcan].got == true) + if (mapheaderinfo[mapnum]->cache_spraycan < gamedata->numspraycans) { - V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), - R_GetTranslationColormap(TC_RAINBOW, mapheaderinfo[mapnum]->cachedcan, GTC_MENUCACHE)); - //V_DrawRightAlignedThinString(x - 2, y, 0, skincolors[mapheaderinfo[mapnum]->cachedcan].name); + UINT16 col = gamedata->spraycans[mapheaderinfo[mapnum]->cache_spraycan].col; + + if (col < numskincolors) + { + V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), + R_GetTranslationColormap(TC_RAINBOW, col, GTC_MENUCACHE)); + //V_DrawRightAlignedThinString(x - 2, y, 0, skincolors[col].name); + } x -= 8; } } diff --git a/src/m_cond.c b/src/m_cond.c index 554cba4a6..31b852c24 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -628,15 +628,22 @@ void M_ClearSecrets(void) memset(netUnlocked, 0, sizeof(netUnlocked)); memset(gamedata->achieved, 0, sizeof(gamedata->achieved)); - gamedata->allspraycansplaced = false; - memset(gamedata->spraycans, 0, sizeof(gamedata->spraycans)); + Z_Free(gamedata->spraycans); + gamedata->spraycans = NULL; + gamedata->numspraycans = 0; + gamedata->gotspraycans = 0; - INT32 i; + UINT16 i; for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i]) continue; - mapheaderinfo[i]->cachedcan = 0; + mapheaderinfo[i]->cache_spraycan = UINT16_MAX; + } + + for (i = 0; i < numskincolors; i++) + { + skincolors[i].cache_spraycan = UINT16_MAX; } Z_Free(gamedata->challengegrid); @@ -669,146 +676,100 @@ static void M_AssignSpraycans(void) // the release date of "Bomb Rush Cyberfunk". // ~toast 180823 (committed a day later) - if (gamedata->allspraycansplaced) + if (gamedata->numspraycans != 0) return; // Init ordered list of skincolors - UINT16 tempcanlist[MAXCANCOLORS]; - size_t listlen = 0; + UINT16 tempcanlist[MAXSKINCOLORS]; + UINT16 listlen = 0, prependlen = 0; - // Todo one of these should be a freebie - UINT16 prependlist[] = + UINT32 i, j; + conditionset_t *c; + condition_t *cn; + + const UINT16 prependoffset = MAXSKINCOLORS-1; + + // None of the following accounts for cans being removed, only added... + for (i = 0; i < MAXCONDITIONSETS; ++i) { - SKINCOLOR_RED, - SKINCOLOR_ORANGE, - SKINCOLOR_YELLOW, - SKINCOLOR_GREEN, - SKINCOLOR_BLUE, - SKINCOLOR_PURPLE, - 0 - }; - - UINT16 i; - - for (i = 0; prependlist[i]; i++) - { - if (gamedata->spraycans[prependlist[i]].map > 0 - && gamedata->spraycans[prependlist[i]].map <= nummapheaders) + c = &conditionSets[i]; + if (!c->numconditions) continue; - //CONS_Printf("DDD - Prepending %d\n", prependlist[i]); - - tempcanlist[listlen] = prependlist[i]; - gamedata->spraycans[prependlist[i]].got = 2; // invalid set to detect in below loop, rather than having to iterate over prependlist again - listlen++; - } - - size_t prepend = listlen; - - for (i = 1; i < MAXCANCOLORS; i++) - { - if (gamedata->spraycans[i].map > 0 - && gamedata->spraycans[i].map <= nummapheaders) - continue; - - if (gamedata->spraycans[i].got == 2) + for (j = 0; j < c->numconditions; ++j) { - // re-make valid, reject duplicating prepended - gamedata->spraycans[i].got = false; - continue; - } - - //CONS_Printf("DDD - Adding %d\n", i); - - tempcanlist[listlen] = i; - listlen++; - } - - if (!listlen) - goto cansdone; - - if (prepend > 0) - { - // Swap the prepend for random order - M_Shuffle_UINT16(tempcanlist, prepend); - } - - if (listlen > prepend) - { - // Swap everything else for random order - M_Shuffle_UINT16(tempcanlist + prepend, listlen - prepend); - } - - i = 0; - - cupheader_t *cup; - - UINT16 level; - - for (cup = kartcupheaders; cup; cup = cup->next) - { - UINT8 j; - for (j = 0; j < cup->numlevels; j++) - { - level = cup->cachedlevels[j]; - - if (level > nummapheaders) + cn = &c->condition[j]; + if (cn->type != UC_SPRAYCAN) continue; - if (mapheaderinfo[level]->cachedcan != 0) + // G_LoadGamedata, G_SaveGameData doesn't support custom skincolors right now. + if (cn->requirement >= SKINCOLOR_FIRSTFREESLOT) //numskincolors) continue; - gamedata->spraycans[tempcanlist[i]].map = level + 1; - mapheaderinfo[level]->cachedcan = tempcanlist[i]; - - if (++i < listlen) + if (skincolors[cn->requirement].cache_spraycan != UINT16_MAX) continue; - goto cansdone; + // Still invalid, just in case it isn't assigned one later + skincolors[cn->requirement].cache_spraycan = UINT16_MAX-1; + + if (!cn->extrainfo1) + { + //CONS_Printf("DDD - Adding standard can color %d\n", cn->requirement); + + tempcanlist[listlen] = cn->requirement; + listlen++; + continue; + } + + //CONS_Printf("DDD - Prepending early can color %d\n", cn->requirement); + + tempcanlist[prependoffset - prependlen] = cn->requirement; + prependlen++; } } - for (level = 0; level < nummapheaders; level++) + if (listlen) { - if (!mapheaderinfo[level] - || !(mapheaderinfo[level]->typeoflevel & TOL_RACE) - || mapheaderinfo[level]->cachedcan != 0) - continue; - - gamedata->spraycans[tempcanlist[i]].map = level + 1; - mapheaderinfo[level]->cachedcan = tempcanlist[i]; - - if (++i < listlen) - continue; - - goto cansdone; + // Swap the standard colours for random order + M_Shuffle_UINT16(tempcanlist, listlen); + } + else if (!prependlen) + { + return; } -cansdone: - -#ifdef PARANOIA - for (i = 1; i < MAXCANCOLORS; i++) + if (prependlen) { - if (gamedata->spraycans[i].map == 0) - I_Error("CANPROBLEM - BAD MAP FOR CAN %d\n", i); - if (gamedata->spraycans[i].map > nummapheaders) - I_Error("CANPROBLEM - TOO BIG MAP FOR CAN %d\n", i); - if (mapheaderinfo[gamedata->spraycans[i].map-1]->cachedcan != i) - I_Error("CANPROBLEM - MAP AND CAN DISAGREE FOR %d (%d)\n", i, mapheaderinfo[gamedata->spraycans[i].map-1]->cachedcan); + // Swap the early colours for random order + M_Shuffle_UINT16(tempcanlist + prependoffset - prependlen, prependlen); + + // Put at the front of the main list + // (technically reverses the prepend order, but it + // was LITERALLY just shuffled so it doesn't matter) + while (prependlen) + { + prependlen--; + tempcanlist[listlen] = tempcanlist[prependlen]; + tempcanlist[prependlen] = tempcanlist[prependoffset - prependlen]; + listlen++; + } } - for (i = 0; i < nummapheaders; i++) - { - if (!mapheaderinfo[i] || mapheaderinfo[i]->cachedcan == 0) - continue; - if (mapheaderinfo[i]->cachedcan > MAXCANCOLORS) - I_Error("MAPPROBLEM - BAD CAN FOR MAP %d\n", i); - if (gamedata->spraycans[mapheaderinfo[i]->cachedcan].map-1 != i) - I_Error("MAPPROBLEM - CAN AND MAP DISAGREE FOR %d (%d)\n", i, gamedata->spraycans[mapheaderinfo[i]->cachedcan].map-1); - } -#endif + gamedata->spraycans = Z_Realloc( + gamedata->spraycans, + sizeof(candata_t) * (gamedata->numspraycans + listlen), + PU_STATIC, + NULL); - gamedata->allspraycansplaced = true; + for (i = 0; i < listlen; i++) + { + gamedata->spraycans[gamedata->numspraycans].map = NEXTMAP_INVALID; + gamedata->spraycans[gamedata->numspraycans].col = tempcanlist[i]; + + skincolors[tempcanlist[i]].cache_spraycan = gamedata->numspraycans; + + gamedata->numspraycans++; + } } void M_FinaliseGameData(void) @@ -1062,10 +1023,15 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_SPRAYCAN: { if (cn->requirement <= 0 - || cn->requirement >= MAXCANCOLORS) + || cn->requirement >= numskincolors) return false; - return gamedata->spraycans[cn->requirement].got; + UINT16 can_id = skincolors[cn->requirement].cache_spraycan; + + if (can_id >= gamedata->numspraycans) + return false; + + return (gamedata->spraycans[can_id].map < nummapheaders); } // Just for string building @@ -1522,20 +1488,24 @@ static const char *M_GetConditionString(condition_t *cn) case UC_SPRAYCAN: { if (cn->requirement <= 0 - || cn->requirement >= MAXCANCOLORS) + || cn->requirement >= numskincolors) return va("INVALID SPRAYCAN COLOR \"%d\"", cn->requirement); - INT32 checkLevel = gamedata->spraycans[cn->requirement].map - 1; + UINT16 can_id = skincolors[cn->requirement].cache_spraycan; - if (checkLevel < 0 || checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) - return va("INVALID SPRAYCAN MAP \"%d:%d\"", cn->requirement, checkLevel); + if (can_id >= gamedata->numspraycans) + return va("INVALID SPRAYCAN ID \"%d:%u\"", + cn->requirement, + skincolors[cn->requirement].cache_spraycan + ); - title = BUILDCONDITIONTITLE(checkLevel); + if (can_id == 0) + return "grab a Spray Can"; // Special case for the head of the list - work = va("%s: grab the Spray Can", title); + if (gamedata->spraycans[0].map >= nummapheaders) + return NULL; // Don't tease that there are many until you have one - Z_Free(title); - return work; + return va("grab %d Spray Cans", can_id + 1); } case UC_AND: diff --git a/src/m_cond.h b/src/m_cond.h index 09c850aa6..96cdf8c0d 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -255,8 +255,8 @@ typedef enum { struct candata_t { + UINT16 col; UINT16 map; - boolean got; }; // GAMEDATA STRUCTURE @@ -281,8 +281,9 @@ struct gamedata_t boolean unlockpending[MAXUNLOCKABLES]; // SPRAYCANS COLLECTED - boolean allspraycansplaced; - candata_t spraycans[MAXCANCOLORS]; + UINT16 numspraycans; + UINT16 gotspraycans; + candata_t* spraycans; // CHALLENGE GRID UINT16 challengegridwidth; diff --git a/src/p_inter.c b/src/p_inter.c index a3ef26de0..2b9542295 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -624,13 +624,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_SPRAYCAN: { - UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; - - if (col == 0 || col > MAXCANCOLORS) - { - return; - } - if (demo.playback) { // Never collect emblems in replays. @@ -643,15 +636,41 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } - if (P_IsLocalPlayer(player)) + if (!P_IsLocalPlayer(player)) { - if (!gamedata->spraycans[col].got) - { - gamedata->spraycans[col].got = true; - if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) - S_StartSound(NULL, sfx_ncitem); - gamedata->deferredsave = true; - } + // Must be party. + return; + } + + UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan; + + if (can_id < gamedata->numspraycans) + { + // Assigned to this level, has been grabbed + return; + } + //else + { + // Unassigned, get the next grabbable colour + can_id = gamedata->gotspraycans; + } + + if (can_id >= gamedata->numspraycans) + { + // We've exhausted all the spraycans to grab. + return; + } + + if (gamedata->spraycans[can_id].map >= nummapheaders) + { + gamedata->spraycans[can_id].map = gamemap-1; + mapheaderinfo[gamemap-1]->cache_spraycan = can_id; + + gamedata->gotspraycans++; + + if (!M_UpdateUnlockablesAndExtraEmblems(true, true)) + S_StartSound(NULL, sfx_ncitem); + gamedata->deferredsave = true; } // Don't delete the object, just fade it. diff --git a/src/p_mobj.c b/src/p_mobj.c index a5ae4b881..d4823700c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12437,19 +12437,28 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) void P_SprayCanInit(mobj_t* mobj) { - UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; + UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan; - if (col == 0 || col > MAXCANCOLORS) + if (can_id < gamedata->numspraycans) { - mobj->renderflags = RF_DONTDRAW; - return; + // Assigned to this level, has been grabbed + mobj->renderflags = (tr_trans50 << RF_TRANSSHIFT); + } + else + { + // Unassigned, get the next grabbable colour + can_id = gamedata->gotspraycans; + mobj->renderflags = 0; } - mobj->color = col; - - mobj->renderflags = (gamedata->spraycans[col].got) - ? (tr_trans50 << RF_TRANSSHIFT) - : 0; + if (can_id < gamedata->numspraycans) + { + mobj->color = gamedata->spraycans[can_id].col; + } + else + { + mobj->renderflags = RF_DONTDRAW; + } } static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj) diff --git a/src/p_setup.c b/src/p_setup.c index 3cd7c5ef4..f5461fa35 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -460,7 +460,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->justPlayed = 0; mapheaderinfo[num]->anger = 0; - mapheaderinfo[num]->cachedcan = 0; + mapheaderinfo[num]->cache_spraycan = UINT16_MAX; mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; @@ -816,17 +816,18 @@ static void P_SpawnMapThings(boolean spawnemblems) if (spawnemblems) { - if (nummapspraycans == 0) - { - UINT16 col = mapheaderinfo[gamemap-1]->cachedcan; + const UINT8 recommendedcans = +#ifdef DEVELOP + !(mapheaderinfo[gamemap-1]->typeoflevel & TOL_RACE) ? 0 : +#endif + 1; - if (col > 0 && col <= MAXCANCOLORS) - { - CONS_Alert(CONS_WARNING, "SPRAY CANS: Map has assigned Spray Cans but no pickup placed!\n"); - } - } - else if (nummapspraycans > 1) - CONS_Alert(CONS_ERROR, "SPRAY CANS: Map has too many Spray Cans (%d)!", numspraycans); + if (nummapspraycans > recommendedcans) + CONS_Alert(CONS_ERROR, "SPRAY CANS: Map has too many Spray Cans (%d)!", nummapspraycans); +#ifdef DEVELOP + else if (nummapspraycans != recommendedcans) + CONS_Alert(CONS_ERROR, "SPRAY CANS: Krew-made Race maps need a Spray Can placed!"); +#endif } } From 53549bfa2d3a87762752d29b471197866c4a35b8 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 17:08:06 +0100 Subject: [PATCH 24/36] M_Shuffle_UINT16: Fix to not rule out half of all possible Spray Can orders --- src/m_cond.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index 31b852c24..9335999ff 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -661,9 +661,14 @@ static void M_Shuffle_UINT16(UINT16 *list, size_t len) { size_t i; UINT16 temp; - while (--len > 1) // no need to swap on == + + while (len > 1) { i = M_RandomKey(len); + + if (i == --len) + continue; + temp = list[i]; list[i] = list[len]; list[len] = temp; From a3640110aee5ecb9468860865a99f2c7708e8bd4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 17:16:07 +0100 Subject: [PATCH 25/36] Only permit Spray Cans to be grabbed on base game levels I would not be so heavy handed against preventing players from grabbing Spray Cans on custom levels, but designing a system that permits unloaded headers to retain their cans is frankly overkill. There are plenty of other ways the same kind of level-scouring play can be experienced on custom levels. --- src/d_main.c | 5 +++-- src/dehacked.c | 3 +++ src/doomstat.h | 2 +- src/g_game.c | 1 + src/p_inter.c | 4 +++- src/p_mobj.c | 4 +++- src/p_setup.c | 8 ++++---- src/p_setup.h | 2 +- 8 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 9652f0d10..fd05bc3cd 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1514,7 +1514,8 @@ void D_SRB2Main(void) // // search for mainwad maps // - P_InitMapData(false); + P_InitMapData(); + basenummapheaders = nummapheaders; CON_SetLoadingProgress(LOADED_IWAD); @@ -1525,7 +1526,7 @@ void D_SRB2Main(void) // // search for pwad maps // - P_InitMapData(true); + P_InitMapData(); CON_SetLoadingProgress(LOADED_PWAD); diff --git a/src/dehacked.c b/src/dehacked.c index 6d44b87fb..66567fb05 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -605,7 +605,10 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) } // end while if (gamedataadded) + { + basenummapheaders = nummapheaders; G_LoadGameData(); + } if (gamestate == GS_MENU || gamestate == GS_TITLESCREEN) { diff --git a/src/doomstat.h b/src/doomstat.h index 7d14f1d5a..26ea96cdb 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -551,7 +551,7 @@ struct mapheader_t #define LF2_FINISHNEEDED (1<<3) ///< Not available in Time Attack modes until you beat the level extern mapheader_t** mapheaderinfo; -extern INT32 nummapheaders, mapallocsize; +extern INT32 nummapheaders, basenummapheaders, mapallocsize; struct unloaded_mapheader_t { diff --git a/src/g_game.c b/src/g_game.c index 17d16b47a..36de12b56 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -193,6 +193,7 @@ quake_t *g_quakes = NULL; // Map Header Information mapheader_t** mapheaderinfo = {NULL}; INT32 nummapheaders = 0; +INT32 basenummapheaders = 0; INT32 mapallocsize = 0; unloaded_mapheader_t *unloadedmapheaders = NULL; diff --git a/src/p_inter.c b/src/p_inter.c index 2b9542295..0d99598dc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -642,6 +642,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } + // See also P_SprayCanInit UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan; if (can_id < gamedata->numspraycans) @@ -649,7 +650,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Assigned to this level, has been grabbed return; } - //else + // Prevent footguns - these won't persist when custom levels are unloaded + else if (gamemap-1 < basenummapheaders) { // Unassigned, get the next grabbable colour can_id = gamedata->gotspraycans; diff --git a/src/p_mobj.c b/src/p_mobj.c index d4823700c..76ae716a3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12437,6 +12437,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) void P_SprayCanInit(mobj_t* mobj) { + // See also P_TouchSpecialThing UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan; if (can_id < gamedata->numspraycans) @@ -12444,7 +12445,8 @@ void P_SprayCanInit(mobj_t* mobj) // Assigned to this level, has been grabbed mobj->renderflags = (tr_trans50 << RF_TRANSSHIFT); } - else + // Prevent footguns - these won't persist when custom levels are unloaded + else if (gamemap-1 < basenummapheaders) { // Unassigned, get the next grabbable colour can_id = gamedata->gotspraycans; diff --git a/src/p_setup.c b/src/p_setup.c index f5461fa35..f55c13b09 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8564,7 +8564,7 @@ lumpnum_t wadnamelump = LUMPERROR; INT16 wadnamemap = 0; // gamemap based // Initialising map data (and catching replacements)... -UINT8 P_InitMapData(boolean existingmapheaders) +UINT8 P_InitMapData(void) { UINT8 ret = 0; INT32 i, j; @@ -8616,7 +8616,7 @@ UINT8 P_InitMapData(boolean existingmapheaders) if (maplump == LUMPERROR) { #ifndef DEVELOP - if (!existingmapheaders) + if (!basenummapheaders) { I_Error("P_InitMapData: Base map %s has a header but no level\n", name); } @@ -8633,7 +8633,7 @@ UINT8 P_InitMapData(boolean existingmapheaders) ret |= MAPRET_ADDED; CONS_Printf("%s\n", name); - if (existingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR) + if (basenummapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR) { G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you @@ -8950,7 +8950,7 @@ boolean P_MultiSetupWadFiles(boolean fullsetup) if (partadd_stage == 2) { - UINT8 mapsadded = P_InitMapData(true); + UINT8 mapsadded = P_InitMapData(); if (!mapsadded) CONS_Printf(M_GetText("No maps added\n")); diff --git a/src/p_setup.h b/src/p_setup.h index 1d474dd10..bcffd85b2 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -114,7 +114,7 @@ boolean P_AddWadFile(const char *wadfilename); #define MAPRET_ADDED (1) #define MAPRET_CURRENTREPLACED (1<<1) -UINT8 P_InitMapData(boolean existingmapheaders); +UINT8 P_InitMapData(void); extern lumpnum_t wadnamelump; extern INT16 wadnamemap; #define WADNAMECHECK(name) (!strncmp(name, "WADNAME", 7)) From 6d2e120e77dffb050cdff8e1241a8bd2091a4cc0 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 17:54:09 +0100 Subject: [PATCH 26/36] M_DrawCharSelectCircle: Fix offset colour id when number of colors available is odd VS even --- src/k_menudraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 3681aaeb0..23c989505 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1299,7 +1299,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (i == 0) { - n = l = r = M_GetColorBefore(&p->colors, p->color, (numoptions/2) - 1); + n = l = r = M_GetColorBefore(&p->colors, p->color, (numoptions/2) - (numoptions & 1)); } else if (subtract) { @@ -1428,7 +1428,7 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y) if (i == 0) { - n = l = r = M_GetColorBefore(&p->colors, p->followercolor, (numoptions/2) - 1); + n = l = r = M_GetColorBefore(&p->colors, p->followercolor, (numoptions/2) - (numoptions & 1)); } else if (subtract) { From e7dc2cda0c5bb982185b864c5d2be9f2044cb2ce Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 17:59:01 +0100 Subject: [PATCH 27/36] d_netcmd.c: Fix default values for color cvars (and associated lastgoodcolor) to not error when we make basically all of the colours unlockables. --- src/d_netcmd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 582f3d98d..b5a987635 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -287,12 +287,12 @@ consvar_t cv_playername[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("name4", "Knuckles", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name4_OnChange) }; // player colors -UINT16 lastgoodcolor[MAXSPLITSCREENPLAYERS] = {SKINCOLOR_BLUE, SKINCOLOR_BLUE, SKINCOLOR_BLUE, SKINCOLOR_BLUE}; +UINT16 lastgoodcolor[MAXSPLITSCREENPLAYERS] = {SKINCOLOR_NONE, SKINCOLOR_NONE, SKINCOLOR_NONE, SKINCOLOR_NONE}; consvar_t cv_playercolor[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("color", "Red", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color1_OnChange), - CVAR_INIT ("color2", "Orange", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange), - CVAR_INIT ("color3", "Blue", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color3_OnChange), - CVAR_INIT ("color4", "Red", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color4_OnChange) + CVAR_INIT ("color", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color1_OnChange), + CVAR_INIT ("color2", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange), + CVAR_INIT ("color3", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color3_OnChange), + CVAR_INIT ("color4", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color4_OnChange) }; // player's skin, saved for commodity, when using a favorite skins wad.. consvar_t cv_skin[MAXSPLITSCREENPLAYERS] = { @@ -1516,7 +1516,7 @@ static void SendNameAndColor(const UINT8 n) CV_StealthSetValue(&cv_playercolor[n], SKINCOLOR_NONE); } - sendColor = cv_playercolor[n].value; + lastgoodcolor[playernum] = sendColor = cv_playercolor[n].value; } // ditto for follower colour: From b0ee900422b43c28ab4213a74c8fec8e881765de Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 18:24:45 +0100 Subject: [PATCH 28/36] d_netcmd.c: Fix errors with multiple _OnChange functions - Skin_OnChange - Now always checks for cheats for any machine-local player skin change in non-netgame, non-K_CanChangeRules contexts - Previously applied only to consoleplayer in Time Trial - Color_OnChange - Fixes straight up incorrect condition that prevented honest players from changing color mid-game via the developer console --- src/d_netcmd.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b5a987635..80eea8703 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -6833,14 +6833,11 @@ static void Skin_OnChange(const UINT8 p) return; } - if (p == 0) + if (!CV_CheatsEnabled() && !(netgame || K_CanChangeRules(false)) + && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - if (!CV_CheatsEnabled() && !(multiplayer || netgame) // In single player. - && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y - { - CV_StealthSet(&cv_skin[p], skins[players[g_localplayers[p]].skin].name); - return; - } + CV_StealthSet(&cv_skin[p], skins[players[g_localplayers[p]].skin].name); + return; } if (CanChangeSkin(g_localplayers[p])) @@ -6890,7 +6887,7 @@ static void Color_OnChange(const UINT8 p) UINT16 color = cv_playercolor[p].value; boolean colorisgood = (color == SKINCOLOR_NONE || K_ColorUsable(color, false, true) == true); - if (Playing() && splitscreen < p) + if (Playing() && p <= splitscreen) { if (P_PlayerMoving(g_localplayers[p]) == true) { From b9a6f7362ba011baba8b65033565f85544ab9e79 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 18:54:28 +0100 Subject: [PATCH 29/36] M_UpdateConditionSetsPending: Fix invalid character names returning the entire loop early --- src/m_cond.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index 9335999ff..2b4aba4de 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -826,7 +826,7 @@ void M_UpdateConditionSetsPending(void) if (cn->requirement < 0) { CONS_Alert(CONS_WARNING, "UCRP_ISCHARACTER: Invalid character %s for condition ID %d", cn->stringvar, cn->id+1); - return; + continue; } Z_Free(cn->stringvar); From 595b3d67d53f24e524591f1928efdd827cf8da0e Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 23 Aug 2023 18:58:10 +0100 Subject: [PATCH 30/36] Add UC_CHARACTERWINS Just in case I don't get to do a major pass on adding a ton of extra unlockable conditions later, this is an easy, quick add featuring a stat gamedata and the statistics menu has been tracking for a while. --- src/deh_soc.c | 26 ++++++++++---------------- src/m_cond.c | 22 +++++++++++++++++++++- src/m_cond.h | 2 ++ 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 027ff2d7a..bdff40b1c 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2542,6 +2542,14 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) return; } } + else if (fastcmp(params[0], "CHARACTERWINS")) + { + PARAMCHECK(2); + ty = UC_CHARACTERWINS; + stringvar = Z_StrDup(params[1]); + re = -1; + x1 = atoi(params[2]); + } else if ((offset=0) || fastcmp(params[0], "ALLCHAOS") || (++offset && fastcmp(params[0], "ALLSUPER")) || (++offset && fastcmp(params[0], "ALLEMERALDS"))) @@ -2662,22 +2670,8 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) { PARAMCHECK(1); ty = UCRP_ISCHARACTER; -#if 0 - { - re = R_SkinAvailable(params[1]); - - if (re < 0) - { - deh_warning("Invalid character %s for condition ID %d", params[1], id+1); - return; - } - } -#else - { - stringvar = Z_StrDup(params[1]); - re = -1; - } -#endif + stringvar = Z_StrDup(params[1]); + re = -1; } else if (fastcmp(params[0], "ISENGINECLASS")) { diff --git a/src/m_cond.c b/src/m_cond.c index 2b4aba4de..e0bf2d9cb 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -819,13 +819,14 @@ void M_UpdateConditionSetsPending(void) switch (cn->type) { + case UC_CHARACTERWINS: case UCRP_ISCHARACTER: { cn->requirement = R_SkinAvailable(cn->stringvar); if (cn->requirement < 0) { - CONS_Alert(CONS_WARNING, "UCRP_ISCHARACTER: Invalid character %s for condition ID %d", cn->stringvar, cn->id+1); + CONS_Alert(CONS_WARNING, "UC TYPE %u: Invalid character %s for condition ID %d", cn->type, cn->stringvar, cn->id+1); continue; } @@ -981,6 +982,12 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_MAPTIME: // Requires time on map <= x return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement); + case UC_CHARACTERWINS: + if (cn->requirement < 0) + return false; + + return (skins[cn->requirement].records.wins >= (UINT32)cn->extrainfo1); + case UC_ALLCHAOS: case UC_ALLSUPER: case UC_ALLEMERALDS: @@ -1353,6 +1360,19 @@ static const char *M_GetConditionString(condition_t *cn) return work; } + case UC_CHARACTERWINS: + { + if (cn->requirement < 0 || !skins[cn->requirement].realname[0]) + return va("INVALID CHAR CONDITION \"%d:%d:%d\"", cn->type, cn->requirement, cn->extrainfo1); + work = (R_SkinUsable(-1, cn->requirement, false)) + ? skins[cn->requirement].realname + : "???"; + return va("win %d Round%s as %s", + cn->extrainfo1, + cn->extrainfo1 == 1 ? "" : "s", + work); + } + case UC_ALLCHAOS: case UC_ALLSUPER: case UC_ALLEMERALDS: diff --git a/src/m_cond.h b/src/m_cond.h index 96cdf8c0d..b330b942e 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -41,6 +41,8 @@ typedef enum UC_MAPSPBATTACK, // MAPSPBATTACK [map] UC_MAPTIME, // MAPTIME [map] [time to beat, tics] + UC_CHARACTERWINS, // CHARACTERWINS [character] [x rounds] + UC_ALLCHAOS, // ALLCHAOS [minimum difficulty] UC_ALLSUPER, // ALLSUPER [minimum difficulty] UC_ALLEMERALDS, // ALLEMERALDS [minimum difficulty] From 9076b3c99be085d93d1ee838cd69175e9c87c1d8 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 24 Aug 2023 01:16:14 +0100 Subject: [PATCH 31/36] Self-review: Correctly initialised gamedata spraycan map id in clear_Levels --- src/deh_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index bdff40b1c..76488462e 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -220,7 +220,7 @@ void clear_levels(void) { UINT16 i; for (i = 0; i < gamedata->numspraycans; i++) - gamedata->spraycans[i].map = 0; + gamedata->spraycans[i].map = NEXTMAP_INVALID; } // Exit the current gamemap as a safeguard From 0a9d15ceb02a0201d8960330b16f62881ed1b60c Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Aug 2023 04:34:19 -0700 Subject: [PATCH 32/36] Remove remnants of LJOYSTICK --- src/d_netcmd.c | 26 -------------------------- src/d_netcmd.h | 3 --- 2 files changed, 29 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 582f3d98d..cd523b0ca 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -242,11 +242,6 @@ static void Command_Eval(void); static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; -#ifdef LJOYSTICK -static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"}, - {4, "/dev/js3"}, {0, NULL}}; -#endif - static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}}; static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; @@ -344,7 +339,6 @@ consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); -#if (defined (LJOYSTICK) || defined (HAVE_SDL)) consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale), CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2), @@ -352,23 +346,6 @@ consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("padscale4", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale4) }; -#ifdef LJOYSTICK -consvar_t cv_joyport[MAXSPLITSCREENPLAYERS] = { //Alam: for later - CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL), - CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL), - CVAR_INIT ("padport3", "/dev/js0", CV_SAVE, joyport_cons_t, NULL), - CVAR_INIT ("padport4", "/dev/js0", CV_SAVE, joyport_cons_t, NULL) -}; -#endif -#else -consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { //Alam: Dummy for save - CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL), - CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL), - CVAR_INIT ("padscale3", "1", CV_SAVE|CV_HIDEN, NULL, NULL), - CVAR_INIT ("padscale4", "1", CV_SAVE|CV_HIDEN, NULL, NULL) -}; -#endif - // SRB2kart consvar_t cv_items[NUMKARTRESULTS-1] = { CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL), @@ -1079,9 +1056,6 @@ void D_RegisterClientCommands(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { CV_RegisterVar(&cv_joyscale[i]); -#ifdef LJOYSTICK - CV_RegisterVar(&cv_joyport[i]); -#endif } // s_sound.c diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 1206bf7dc..acf6b8336 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -48,9 +48,6 @@ extern consvar_t cv_splitplayers; extern consvar_t cv_seenames; extern consvar_t cv_usemouse; extern consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS]; -#ifdef LJOYSTICK -extern consvar_t cv_joyport[MAXSPLITSCREENPLAYERS]; -#endif // normally in p_mobj but the .h is not read extern consvar_t cv_itemrespawntime; From c5e341769bff2389b1f75ff4aa4828c0ecdc3450 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Aug 2023 04:52:41 -0700 Subject: [PATCH 33/36] Remove unused cvars - growmusicfade - invincmusicfade - music_resync_powerups_only - music_resync_threshold - renderstats (wasn't even registered in the console!) - resetspecialmusic - resume - samplerate - stretch - tailspickup --- src/d_netcmd.c | 6 ------ src/g_game.c | 7 ------- src/g_game.h | 7 ------- src/r_main.c | 4 ---- src/r_main.h | 1 - src/s_sound.c | 15 --------------- src/s_sound.h | 3 --- src/sdl/i_video.cpp | 2 -- 8 files changed, 45 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cd523b0ca..bd33113a3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1076,12 +1076,6 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_soundtest); - CV_RegisterVar(&cv_invincmusicfade); - CV_RegisterVar(&cv_growmusicfade); - - CV_RegisterVar(&cv_resetspecialmusic); - - CV_RegisterVar(&cv_resume); CV_RegisterVar(&cv_perfstats); // ingame object placing diff --git a/src/g_game.c b/src/g_game.c index 5e6546699..060dd84f1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -395,13 +395,6 @@ consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, C // Display song credits consvar_t cv_songcredits = CVAR_INIT ("songcredits", "On", CV_SAVE, CV_OnOff, NULL); -consvar_t cv_invincmusicfade = CVAR_INIT ("invincmusicfade", "300", CV_SAVE, CV_Unsigned, NULL); -consvar_t cv_growmusicfade = CVAR_INIT ("growmusicfade", "500", CV_SAVE, CV_Unsigned, NULL); - -consvar_t cv_resetspecialmusic = CVAR_INIT ("resetspecialmusic", "Yes", CV_SAVE, CV_YesNo, NULL); - -consvar_t cv_resume = CVAR_INIT ("resume", "Yes", CV_SAVE, CV_YesNo, NULL); - consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("kickstartaccel", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange), CVAR_INIT ("kickstartaccel2", "Off", CV_SAVE|CV_CALL, CV_OnOff, weaponPrefChange2), diff --git a/src/g_game.h b/src/g_game.h index 0b922b4da..e781f17ee 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -101,13 +101,6 @@ extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; -extern consvar_t cv_invincmusicfade; -extern consvar_t cv_growmusicfade; - -extern consvar_t cv_resetspecialmusic; - -extern consvar_t cv_resume; - // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) #define MAXPLMOVE (50) diff --git a/src/r_main.c b/src/r_main.c index d50c43644..50fee5b58 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -151,7 +151,6 @@ static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); -consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_chasecam[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange), CVAR_INIT ("chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange), @@ -181,8 +180,6 @@ consvar_t cv_homremoval = CVAR_INIT ("homremoval", "Yes", CV_SAVE, homremoval_co consvar_t cv_maxportals = CVAR_INIT ("maxportals", "2", CV_SAVE, maxportals_cons_t, NULL); -consvar_t cv_renderstats = CVAR_INIT ("renderstats", "Off", 0, CV_OnOff, NULL); - consvar_t cv_drawpickups = CVAR_INIT ("drawpickups", "Yes", CV_CHEAT, CV_YesNo, NULL); consvar_t cv_debugfinishline = CVAR_INIT ("debugfinishline", "Off", CV_CHEAT, CV_OnOff, NULL); @@ -1693,7 +1690,6 @@ void R_RegisterEngineStuff(void) UINT8 i; CV_RegisterVar(&cv_gravity); - CV_RegisterVar(&cv_tailspickup); CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_homremoval); diff --git a/src/r_main.h b/src/r_main.h index 0f5560308..c201a50be 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -133,7 +133,6 @@ extern consvar_t cv_ffloorclip; extern consvar_t cv_drawdist, cv_drawdist_precip; extern consvar_t cv_fov[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_skybox; -extern consvar_t cv_tailspickup; extern consvar_t cv_drawpickups; extern consvar_t cv_debugfinishline; diff --git a/src/s_sound.c b/src/s_sound.c index eb6c9a2e1..931040814 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -63,8 +63,6 @@ static void PlaySoundIfUnfocused_OnChange(void); static void ModFilter_OnChange(void); #endif -consvar_t cv_samplerate = CVAR_INIT ("samplerate", "22050", 0, CV_Unsigned, NULL); //Alam: For easy hacking? - // stereo reverse consvar_t stereoreverse = CVAR_INIT ("stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL); @@ -93,15 +91,6 @@ consvar_t cv_closedcaptioning = CVAR_INIT ("closedcaptioning", "Off", CV_SAVE|CV consvar_t cv_gamedigimusic = CVAR_INIT ("music", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange); consvar_t cv_gamesounds = CVAR_INIT ("sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange); -static CV_PossibleValue_t music_resync_threshold_cons_t[] = { - {0, "MIN"}, - {1000, "MAX"}, - {0, NULL} -}; - -consvar_t cv_music_resync_threshold = CVAR_INIT ("music_resync_threshold", "100", CV_SAVE|CV_CALL, music_resync_threshold_cons_t, I_UpdateSongLagThreshold); -consvar_t cv_music_resync_powerups_only = CVAR_INIT ("music_resync_powerups_only", "No", CV_SAVE|CV_CALL, CV_YesNo, I_UpdateSongLagConditions); - // Window focus sound sytem toggles consvar_t cv_playmusicifunfocused = CVAR_INIT ("playmusicifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlayMusicIfUnfocused_OnChange); consvar_t cv_playsoundifunfocused = CVAR_INIT ("playsoundsifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlaySoundIfUnfocused_OnChange); @@ -249,15 +238,11 @@ void S_RegisterSoundStuff(void) CV_RegisterVar(&precachesound); CV_RegisterVar(&surround); - CV_RegisterVar(&cv_samplerate); CV_RegisterVar(&cv_playsoundifunfocused); CV_RegisterVar(&cv_playmusicifunfocused); CV_RegisterVar(&cv_gamesounds); CV_RegisterVar(&cv_gamedigimusic); - CV_RegisterVar(&cv_music_resync_threshold); - CV_RegisterVar(&cv_music_resync_powerups_only); - #ifdef HAVE_OPENMPT CV_RegisterVar(&cv_modfilter); #endif diff --git a/src/s_sound.h b/src/s_sound.h index 805413604..fd6e62e29 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -52,9 +52,6 @@ extern consvar_t cv_gamesounds; extern consvar_t cv_playmusicifunfocused; extern consvar_t cv_playsoundifunfocused; -extern consvar_t cv_music_resync_threshold; -extern consvar_t cv_music_resync_powerups_only; - #ifdef HAVE_OPENMPT extern consvar_t cv_modfilter; #endif diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 88eb183e5..7a2a09d23 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -115,7 +115,6 @@ rendermode_t chosenrendermode = render_none; // set by command line arguments // synchronize page flipping with screen refresh consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "Off", CV_SAVE, CV_OnOff, NULL); -static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL); static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL); UINT8 graphics_started = 0; // Is used in console.c and screen.c @@ -1606,7 +1605,6 @@ void I_StartupGraphics(void) COM_AddCommand ("vid_modelist", VID_Command_ModeList_f); COM_AddCommand ("vid_mode", VID_Command_Mode_f); CV_RegisterVar (&cv_vidwait); - CV_RegisterVar (&cv_stretch); CV_RegisterVar (&cv_alwaysgrabmouse); disable_mouse = static_cast(M_CheckParm("-nomouse")); disable_fullscreen = M_CheckParm("-win") ? SDL_TRUE : SDL_FALSE; From f4ca6001d8e2fecbe48493c933883ca1093eefbf Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 24 Aug 2023 04:56:02 -0700 Subject: [PATCH 34/36] s_sound: remove unused OpenMPT stuff --- src/s_sound.c | 23 ----------------------- src/s_sound.h | 12 ------------ 2 files changed, 35 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 931040814..e6abc1791 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -59,10 +59,6 @@ static void GameDigiMusic_OnChange(void); static void PlayMusicIfUnfocused_OnChange(void); static void PlaySoundIfUnfocused_OnChange(void); -#ifdef HAVE_OPENMPT -static void ModFilter_OnChange(void); -#endif - // stereo reverse consvar_t stereoreverse = CVAR_INIT ("stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL); @@ -95,13 +91,6 @@ consvar_t cv_gamesounds = CVAR_INIT ("sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, consvar_t cv_playmusicifunfocused = CVAR_INIT ("playmusicifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlayMusicIfUnfocused_OnChange); consvar_t cv_playsoundifunfocused = CVAR_INIT ("playsoundsifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlaySoundIfUnfocused_OnChange); -#ifdef HAVE_OPENMPT -openmpt_module *openmpt_mhandle = NULL; - -static CV_PossibleValue_t interpolationfilter_cons_t[] = {{0, "Default"}, {1, "None"}, {2, "Linear"}, {4, "Cubic"}, {8, "Windowed sinc"}, {0, NULL}}; -consvar_t cv_modfilter = CVAR_INIT ("modfilter", "0", CV_SAVE|CV_CALL, interpolationfilter_cons_t, ModFilter_OnChange); -#endif - #define S_MAX_VOLUME 127 // when to clip out sounds @@ -243,10 +232,6 @@ void S_RegisterSoundStuff(void) CV_RegisterVar(&cv_gamesounds); CV_RegisterVar(&cv_gamedigimusic); -#ifdef HAVE_OPENMPT - CV_RegisterVar(&cv_modfilter); -#endif - COM_AddCommand("tunes", Command_Tunes_f); COM_AddCommand("restartaudio", Command_RestartAudio_f); COM_AddCommand("playsound", Command_PlaySound); @@ -2494,11 +2479,3 @@ static void PlaySoundIfUnfocused_OnChange(void) if (window_notinfocus && !cv_playsoundifunfocused.value) S_StopSounds(); } - -#ifdef HAVE_OPENMPT -void ModFilter_OnChange(void) -{ - if (openmpt_mhandle) - openmpt_module_set_render_param(openmpt_mhandle, OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH, cv_modfilter.value); -} -#endif diff --git a/src/s_sound.h b/src/s_sound.h index fd6e62e29..d101f307c 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -19,18 +19,10 @@ #include "command.h" #include "tables.h" // angle_t -#ifdef HAVE_OPENMPT -#include "libopenmpt/libopenmpt.h" -#endif - #ifdef __cplusplus extern "C" { #endif -#ifdef HAVE_OPENMPT -extern openmpt_module *openmpt_mhandle; -#endif - // mask used to indicate sound origin is player item pickup #define PICKUP_SOUND 0x8000 @@ -52,10 +44,6 @@ extern consvar_t cv_gamesounds; extern consvar_t cv_playmusicifunfocused; extern consvar_t cv_playsoundifunfocused; -#ifdef HAVE_OPENMPT -extern consvar_t cv_modfilter; -#endif - extern CV_PossibleValue_t soundvolume_cons_t[]; typedef enum From 6d5770ff5a47eca0b48d6c38c73b5969b72a23d6 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 24 Aug 2023 21:32:41 +0100 Subject: [PATCH 35/36] M_AssignSpraycans: Fix off-by-one in shuffled prepended colours causing crashes The prepended colour list passed to M_Shuffle_UINT16 was off by one, leading to possible pollution of the region with an uninitialised value. For some reason, some people's machines guarantee clean stack memory on game startup, while other people's do not. This is why some people were crashing on Spray Can list generation and some weren't. The stack memory was DEFINITELY not clean by the time you could navigate to the gamedata clear menu, which is why that was crashing without fail. --- src/m_cond.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index e0bf2d9cb..5d3e07a59 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -746,7 +746,7 @@ static void M_AssignSpraycans(void) if (prependlen) { // Swap the early colours for random order - M_Shuffle_UINT16(tempcanlist + prependoffset - prependlen, prependlen); + M_Shuffle_UINT16(tempcanlist + prependoffset - (prependlen - 1), prependlen); // Put at the front of the main list // (technically reverses the prepend order, but it From 29c39433669d99c6db9735602894deb9a2450c08 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 24 Aug 2023 21:38:17 +0100 Subject: [PATCH 36/36] M_AssignSpraycans: Forgot to remove an irrelevant check, now should update if new Spray Can conditions are added --- src/m_cond.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index 5d3e07a59..eecc73ae6 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -681,9 +681,6 @@ static void M_AssignSpraycans(void) // the release date of "Bomb Rush Cyberfunk". // ~toast 180823 (committed a day later) - if (gamedata->numspraycans != 0) - return; - // Init ordered list of skincolors UINT16 tempcanlist[MAXSKINCOLORS]; UINT16 listlen = 0, prependlen = 0;