mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-18 11:32:24 +00:00
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
This commit is contained in:
parent
be1d3e49e8
commit
1d06637a38
10 changed files with 315 additions and 13 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6538,6 +6538,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
|
||||
// SKINCOLOR_ doesn't include these..!
|
||||
{"MAXSKINCOLORS",MAXSKINCOLORS},
|
||||
{"MAXCANCOLORS",MAXCANCOLORS},
|
||||
{"FIRSTSUPERCOLOR",FIRSTSUPERCOLOR},
|
||||
{"NUMSUPERCOLORS",NUMSUPERCOLORS},
|
||||
|
||||
|
|
|
|||
|
|
@ -346,7 +346,9 @@ typedef enum
|
|||
SKINCOLOR_BLOSSOM,
|
||||
SKINCOLOR_TAFFY,
|
||||
|
||||
FIRSTSUPERCOLOR,
|
||||
MAXCANCOLORS,
|
||||
|
||||
FIRSTSUPERCOLOR = MAXCANCOLORS,
|
||||
|
||||
// Super special awesome Super flashing colors!
|
||||
SKINCOLOR_SUPERSILVER1 = FIRSTSUPERCOLOR,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
88
src/g_game.c
88
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
{
|
||||
|
|
|
|||
188
src/m_cond.c
188
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
|
||||
|
|
|
|||
10
src/m_cond.h
10
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue