mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-19 12:01:36 +00:00
Add bonus pickups in conditions you couldn't grab Spray Cans before
- If you've gotten every Spray Can, or you're on a custom course...
- Only one of these spawns per map
- Correctly save and load these
- Statistics menu counts base-game bonuses
- If there are gaps in the list, or new Spray Cans are added later, these base-game bonuses are converted into the new Spray Cans
- New graphics required so far:
- SBONA0 to SBONP0 - 16-frame prerendered circling sprite animation
- GOTBON - 8x8 representation of the SBON object
This commit is contained in:
parent
a6bf7f46a7
commit
ecb4ffeeca
12 changed files with 165 additions and 45 deletions
|
|
@ -1915,6 +1915,7 @@ bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV,
|
|||
// See also P_SprayCanInit
|
||||
UINT16 can_id = mapheaderinfo[gamemap-1]->records.spraycan;
|
||||
|
||||
// Intentionally not affected by MCAN_BONUS
|
||||
if (can_id < gamedata->numspraycans)
|
||||
{
|
||||
UINT16 col = gamedata->spraycans[can_id].col;
|
||||
|
|
|
|||
|
|
@ -156,6 +156,9 @@ struct skinreference_t
|
|||
#define MV_MYSTICMELODY (1<<4)
|
||||
#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE|MV_SPBATTACK|MV_MYSTICMELODY)
|
||||
|
||||
#define MCAN_INVALID (UINT16_MAX)
|
||||
#define MCAN_BONUS (UINT16_MAX-1)
|
||||
|
||||
struct recordtimes_t
|
||||
{
|
||||
tic_t time; ///< Time in which the level was finished.
|
||||
|
|
|
|||
|
|
@ -607,7 +607,7 @@ void srb2::load_ng_gamedata()
|
|||
|
||||
for (auto& mappair : js.maps)
|
||||
{
|
||||
UINT16 mapnum = G_MapNumber(mappair.first.c_str());
|
||||
uint16_t mapnum = G_MapNumber(mappair.first.c_str());
|
||||
recorddata_t dummyrecord {};
|
||||
dummyrecord.mapvisited |= mappair.second.visited.visited ? MV_VISITED : 0;
|
||||
dummyrecord.mapvisited |= mappair.second.visited.beaten ? MV_BEATEN : 0;
|
||||
|
|
@ -630,24 +630,35 @@ void srb2::load_ng_gamedata()
|
|||
|
||||
dummyrecord.spraycan = (minorversion >= GD_MINIMUM_SPRAYCANSV2)
|
||||
? mappair.second.spraycan
|
||||
: UINT16_MAX;
|
||||
: MCAN_INVALID;
|
||||
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum])
|
||||
{
|
||||
// Valid mapheader, time to populate with record data.
|
||||
|
||||
// Infill Spray Can info
|
||||
if (dummyrecord.spraycan < tempcans.size())
|
||||
if (
|
||||
dummyrecord.spraycan < tempcans.size()
|
||||
&& (mapnum < basenummapheaders)
|
||||
&& (tempcans[dummyrecord.spraycan].map >= basenummapheaders)
|
||||
)
|
||||
{
|
||||
// Assign map ID.
|
||||
tempcans[dummyrecord.spraycan].map = mapnum;
|
||||
}
|
||||
dummyrecord.spraycan = UINT16_MAX; // We repopulate this later.
|
||||
|
||||
if (dummyrecord.spraycan != MCAN_INVALID)
|
||||
{
|
||||
// Yes, even if it's valid. We reassign later.
|
||||
dummyrecord.spraycan = MCAN_BONUS;
|
||||
}
|
||||
|
||||
mapheaderinfo[mapnum]->records = dummyrecord;
|
||||
}
|
||||
else if (dummyrecord.mapvisited & MV_BEATEN
|
||||
|| dummyrecord.timeattack.time != 0 || dummyrecord.timeattack.lap != 0
|
||||
|| dummyrecord.spbattack.time != 0 || dummyrecord.spbattack.lap != 0)
|
||||
|| dummyrecord.spbattack.time != 0 || dummyrecord.spbattack.lap != 0
|
||||
|| dummyrecord.spraycan != MCAN_INVALID)
|
||||
{
|
||||
// Invalid, but we don't want to lose all the juicy statistics.
|
||||
// Instead, update a FILO linked list of "unloaded mapheaders".
|
||||
|
|
@ -666,8 +677,11 @@ void srb2::load_ng_gamedata()
|
|||
unloadedmap->next = unloadedmapheaders;
|
||||
unloadedmapheaders = unloadedmap;
|
||||
|
||||
// Invalidate can.
|
||||
dummyrecord.spraycan = UINT16_MAX;
|
||||
if (dummyrecord.spraycan != MCAN_INVALID)
|
||||
{
|
||||
// Invalidate non-bonus spraycans.
|
||||
dummyrecord.spraycan = MCAN_BONUS;
|
||||
}
|
||||
|
||||
// Finally, copy into.
|
||||
unloadedmap->records = dummyrecord;
|
||||
|
|
@ -744,7 +758,7 @@ void srb2::load_ng_gamedata()
|
|||
|
||||
skincolors[tempcan.col].cache_spraycan = i;
|
||||
|
||||
if (tempcan.map < nummapheaders)
|
||||
if (tempcan.map < basenummapheaders)
|
||||
mapheaderinfo[tempcan.map]->records.spraycan = i;
|
||||
|
||||
gamedata->spraycans[i] = tempcan;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"BSPH", // Sphere
|
||||
"EMBM",
|
||||
"SPCN", // Spray Can
|
||||
"SBON", // Spray Can replacement bonus
|
||||
"MMSH", // Ancient Shrine
|
||||
"MORB", // One Morbillion
|
||||
"EMRC", // Chaos Emeralds
|
||||
|
|
|
|||
|
|
@ -589,6 +589,7 @@ typedef enum sprite
|
|||
SPR_BSPH, // Sphere
|
||||
SPR_EMBM,
|
||||
SPR_SPCN, // Spray Can
|
||||
SPR_SBON, // Spray Can replacement bonus
|
||||
SPR_MMSH, // Ancient Shrine
|
||||
SPR_MORB, // One Morbillion
|
||||
SPR_EMRC, // Chaos Emeralds
|
||||
|
|
|
|||
|
|
@ -1486,6 +1486,7 @@ extern struct statisticsmenu_s {
|
|||
INT32 gotmedals;
|
||||
INT32 nummedals;
|
||||
INT32 numextramedals;
|
||||
INT32 numcanbonus;
|
||||
UINT32 statgridplayed[9][9];
|
||||
INT32 maxscroll;
|
||||
UINT16 *maplist;
|
||||
|
|
|
|||
|
|
@ -3315,7 +3315,7 @@ void M_DrawCupSelect(void)
|
|||
incj = false;
|
||||
|
||||
work_array[j].medal = NULL;
|
||||
work_array[j].col = work_array[j].dotcol = UINT16_MAX;
|
||||
work_array[j].col = work_array[j].dotcol = MCAN_INVALID;
|
||||
|
||||
if (templevelsearch.timeattack)
|
||||
{
|
||||
|
|
@ -3354,11 +3354,15 @@ void M_DrawCupSelect(void)
|
|||
emblem = M_GetLevelEmblems(-1);
|
||||
}
|
||||
}
|
||||
else if ((gamedata->gotspraycans > 0) && (mapheaderinfo[map]->typeoflevel & TOL_RACE))
|
||||
else if (mapheaderinfo[map]->typeoflevel & TOL_RACE)
|
||||
{
|
||||
incj = true;
|
||||
|
||||
if (mapheaderinfo[map]->records.spraycan < gamedata->numspraycans)
|
||||
if (mapheaderinfo[map]->records.spraycan == MCAN_BONUS)
|
||||
{
|
||||
work_array[j].col = MCAN_BONUS;
|
||||
}
|
||||
else if (mapheaderinfo[map]->records.spraycan < gamedata->numspraycans)
|
||||
{
|
||||
work_array[j].col = gamedata->spraycans[mapheaderinfo[map]->records.spraycan].col;
|
||||
}
|
||||
|
|
@ -3409,7 +3413,13 @@ void M_DrawCupSelect(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (work_array[i].col < numskincolors)
|
||||
if (work_array[i].col == MCAN_BONUS)
|
||||
{
|
||||
// Bonus in place of Spray Can
|
||||
|
||||
V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTBON", PU_CACHE));
|
||||
}
|
||||
else if (work_array[i].col < numskincolors)
|
||||
{
|
||||
// Spray Can
|
||||
|
||||
|
|
@ -8324,7 +8334,14 @@ static INT32 M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime,
|
|||
if (hasmedals)
|
||||
x -= 4;
|
||||
|
||||
if (mapheaderinfo[mapnum]->records.spraycan < gamedata->numspraycans)
|
||||
if (mapheaderinfo[mapnum]->records.spraycan == MCAN_BONUS)
|
||||
{
|
||||
if (draw)
|
||||
V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTBON", PU_CACHE));
|
||||
|
||||
x -= 8;
|
||||
}
|
||||
else if (mapheaderinfo[mapnum]->records.spraycan < gamedata->numspraycans)
|
||||
{
|
||||
UINT16 col = gamedata->spraycans[mapheaderinfo[mapnum]->records.spraycan].col;
|
||||
|
||||
|
|
@ -8374,11 +8391,18 @@ static void M_DrawStatsMaps(void)
|
|||
if (gamedata->numspraycans)
|
||||
{
|
||||
medalspos = 30 + V_ThinStringWidth(medalcountstr, 0);
|
||||
medalcountstr = va("x %d/%d", gamedata->gotspraycans, gamedata->numspraycans);
|
||||
medalcountstr = va("x %d/%d", gamedata->gotspraycans + statisticsmenu.numcanbonus, gamedata->numspraycans);
|
||||
V_DrawThinString(20 + medalspos, 60, 0, medalcountstr);
|
||||
V_DrawMappedPatch(10 + medalspos, 60, 0, W_CachePatchName("GOTCAN", PU_CACHE),
|
||||
R_GetTranslationColormap(TC_DEFAULT, gamedata->spraycans[0].col, GTC_MENUCACHE));
|
||||
}
|
||||
else if (statisticsmenu.numcanbonus)
|
||||
{
|
||||
medalspos = 30 + V_ThinStringWidth(medalcountstr, 0);
|
||||
medalcountstr = va("x %d", statisticsmenu.numcanbonus);
|
||||
V_DrawThinString(20 + medalspos, 60, 0, medalcountstr);
|
||||
V_DrawScaledPatch(10 + medalspos, 60, 0, W_CachePatchName("GOTBON", PU_CACHE));
|
||||
}
|
||||
|
||||
medalspos = BASEVIDWIDTH - 20;
|
||||
|
||||
|
|
|
|||
47
src/m_cond.c
47
src/m_cond.c
|
|
@ -743,7 +743,7 @@ void M_ClearSecrets(void)
|
|||
continue;
|
||||
|
||||
mapheaderinfo[i]->records.mapvisited = 0;
|
||||
mapheaderinfo[i]->records.spraycan = UINT16_MAX;
|
||||
mapheaderinfo[i]->records.spraycan = MCAN_INVALID;
|
||||
|
||||
mapheaderinfo[i]->cache_maplock = MAXUNLOCKABLES;
|
||||
|
||||
|
|
@ -813,6 +813,30 @@ static void M_AssignSpraycans(void)
|
|||
conditionset_t *c;
|
||||
condition_t *cn;
|
||||
|
||||
UINT16 bonustocanmap = 0;
|
||||
|
||||
// First, turn outstanding bonuses into existing uncollected Spray Cans.
|
||||
while (gamedata->gotspraycans < gamedata->numspraycans)
|
||||
{
|
||||
while (bonustocanmap < basenummapheaders)
|
||||
{
|
||||
if (mapheaderinfo[bonustocanmap]->records.spraycan != MCAN_BONUS)
|
||||
{
|
||||
bonustocanmap++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (bonustocanmap == basenummapheaders)
|
||||
break;
|
||||
|
||||
mapheaderinfo[bonustocanmap]->records.spraycan = gamedata->gotspraycans;
|
||||
gamedata->spraycans[gamedata->gotspraycans].map = bonustocanmap;
|
||||
gamedata->gotspraycans++;
|
||||
}
|
||||
|
||||
const UINT16 prependoffset = MAXSKINCOLORS-1;
|
||||
|
||||
// None of the following accounts for cans being removed, only added...
|
||||
|
|
@ -828,7 +852,7 @@ static void M_AssignSpraycans(void)
|
|||
if (cn->type != UC_SPRAYCAN)
|
||||
continue;
|
||||
|
||||
// G_LoadGamedata, G_SaveGameData doesn't support custom skincolors right now.
|
||||
// This will likely never support custom skincolors.
|
||||
if (cn->requirement >= SKINCOLOR_FIRSTFREESLOT) //numskincolors)
|
||||
continue;
|
||||
|
||||
|
|
@ -890,7 +914,24 @@ static void M_AssignSpraycans(void)
|
|||
|
||||
for (i = 0; i < listlen; i++)
|
||||
{
|
||||
gamedata->spraycans[gamedata->numspraycans].map = NEXTMAP_INVALID;
|
||||
// Convert bonus pickups into Spray Cans if new ones have been added.
|
||||
while (bonustocanmap < basenummapheaders)
|
||||
{
|
||||
if (mapheaderinfo[bonustocanmap]->records.spraycan != MCAN_BONUS)
|
||||
{
|
||||
bonustocanmap++;
|
||||
continue;
|
||||
}
|
||||
|
||||
gamedata->gotspraycans++;
|
||||
mapheaderinfo[bonustocanmap]->records.spraycan = gamedata->numspraycans;
|
||||
break;
|
||||
}
|
||||
gamedata->spraycans[gamedata->numspraycans].map = (
|
||||
(bonustocanmap == basenummapheaders)
|
||||
? NEXTMAP_INVALID
|
||||
: bonustocanmap
|
||||
);
|
||||
gamedata->spraycans[gamedata->numspraycans].col = tempcanlist[i];
|
||||
|
||||
skincolors[tempcanlist[i]].cache_spraycan = gamedata->numspraycans;
|
||||
|
|
|
|||
|
|
@ -290,10 +290,22 @@ void M_Statistics(INT32 choice)
|
|||
{
|
||||
(void)choice;
|
||||
|
||||
UINT16 i;
|
||||
|
||||
statisticsmenu.gotmedals = M_CountMedals(false, false);
|
||||
statisticsmenu.nummedals = M_CountMedals(true, false);
|
||||
statisticsmenu.numextramedals = M_CountMedals(true, true);
|
||||
|
||||
statisticsmenu.numcanbonus = 0;
|
||||
for (i = 0; i < basenummapheaders; i++)
|
||||
{
|
||||
if (!mapheaderinfo[i])
|
||||
continue;
|
||||
if (mapheaderinfo[i]->records.spraycan != MCAN_BONUS)
|
||||
continue;
|
||||
statisticsmenu.numcanbonus++;
|
||||
}
|
||||
|
||||
M_StatisticsPageInit();
|
||||
|
||||
MISC_StatisticsDef.prevMenu = currentMenu;
|
||||
|
|
|
|||
|
|
@ -781,13 +781,25 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// See also P_SprayCanInit
|
||||
UINT16 can_id = mapheaderinfo[gamemap-1]->records.spraycan;
|
||||
|
||||
if (can_id < gamedata->numspraycans)
|
||||
if (can_id < gamedata->numspraycans || can_id == MCAN_BONUS)
|
||||
{
|
||||
// Assigned to this level, has been grabbed
|
||||
return;
|
||||
}
|
||||
// Prevent footguns - these won't persist when custom levels are unloaded
|
||||
else if (gamemap-1 < basenummapheaders)
|
||||
|
||||
if (
|
||||
(gamemap-1 >= basenummapheaders)
|
||||
|| (gamedata->gotspraycans >= gamedata->numspraycans)
|
||||
)
|
||||
{
|
||||
// Custom course OR we ran out of assignables.
|
||||
|
||||
if (special->threshold != 0)
|
||||
return;
|
||||
|
||||
can_id = MCAN_BONUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unassigned, get the next grabbable colour
|
||||
can_id = gamedata->gotspraycans;
|
||||
|
|
@ -810,18 +822,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
skincolors[swapcol].cache_spraycan = can_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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]->records.spraycan = can_id;
|
||||
|
||||
if (gamedata->gotspraycans == 0
|
||||
&& gametype == GT_TUTORIAL
|
||||
|
|
@ -837,12 +838,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
|
||||
gamedata->gotspraycans++;
|
||||
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
||||
mapheaderinfo[gamemap-1]->records.spraycan = can_id;
|
||||
|
||||
if (!M_UpdateUnlockablesAndExtraEmblems(true, true))
|
||||
S_StartSound(NULL, sfx_ncitem);
|
||||
gamedata->deferredsave = true;
|
||||
|
||||
{
|
||||
mobj_t *canmo = NULL;
|
||||
mobj_t *next = NULL;
|
||||
|
|
|
|||
37
src/p_mobj.c
37
src/p_mobj.c
|
|
@ -12917,7 +12917,7 @@ void P_SprayCanInit(mobj_t* mobj)
|
|||
// See also P_TouchSpecialThing
|
||||
UINT16 can_id = mapheaderinfo[gamemap-1]->records.spraycan;
|
||||
|
||||
if (can_id < gamedata->numspraycans)
|
||||
if (can_id < gamedata->numspraycans || can_id == MCAN_BONUS)
|
||||
{
|
||||
// Assigned to this level, has been grabbed
|
||||
mobj->renderflags = (tr_trans50 << RF_TRANSSHIFT);
|
||||
|
|
@ -12925,19 +12925,38 @@ void P_SprayCanInit(mobj_t* mobj)
|
|||
// Prevent footguns - these won't persist when custom levels are unloaded
|
||||
else if (gamemap-1 < basenummapheaders)
|
||||
{
|
||||
// Unassigned, get the next grabbable colour (offset by threshold)
|
||||
can_id = gamedata->gotspraycans;
|
||||
if (gamedata->gotspraycans >= gamedata->numspraycans)
|
||||
{
|
||||
can_id = MCAN_BONUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unassigned, get the next grabbable colour (offset by threshold)
|
||||
can_id = gamedata->gotspraycans;
|
||||
|
||||
// It's ok if this goes over gamedata->numspraycans, as they're
|
||||
// capped below in this func... but NEVER let this go backwards!!
|
||||
if (mobj->threshold != 0)
|
||||
can_id += (mobj->threshold & UINT8_MAX);
|
||||
// It's ok if this goes over gamedata->numspraycans, as they're
|
||||
// capped below in this func... but NEVER let this go backwards!!
|
||||
if (mobj->threshold != 0)
|
||||
can_id += (mobj->threshold & UINT8_MAX);
|
||||
}
|
||||
|
||||
mobj->renderflags = 0;
|
||||
}
|
||||
|
||||
if (can_id < gamedata->numspraycans)
|
||||
else
|
||||
{
|
||||
// Custom course, bonus only
|
||||
can_id = MCAN_BONUS;
|
||||
}
|
||||
|
||||
if (can_id == MCAN_BONUS && mobj->threshold == 0)
|
||||
{
|
||||
// Only one bonus possible
|
||||
// We modify sprite instead of state for netsync reasons
|
||||
mobj->sprite = SPR_SBON;
|
||||
}
|
||||
else if (can_id < gamedata->numspraycans)
|
||||
{
|
||||
mobj->sprite = mobj->state->sprite;
|
||||
mobj->color = gamedata->spraycans[can_id].col;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -498,7 +498,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
|
|||
#endif
|
||||
|
||||
memset(&mapheaderinfo[num]->records, 0, sizeof(recorddata_t));
|
||||
mapheaderinfo[num]->records.spraycan = UINT16_MAX;
|
||||
mapheaderinfo[num]->records.spraycan = MCAN_INVALID;
|
||||
|
||||
mapheaderinfo[num]->justPlayed = 0;
|
||||
mapheaderinfo[num]->anger = 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue