This avoids an issue where the skins array takes up a fixed, but large
amount of memory at runtime. On x86_64 linux, that array is roughly 80
megabytes in memory, most of which is unused when the game is unmodded.
Instead, we treat `skins` as a dynamically resizing array, and it is an
array-of-pointers into separate allocated `skin_t`.
This is based on Lactozilla's skin limit MR for SRB2, but I've rewritten
it because RR has diverged quite a bit.
This was verified to check every access of `skins` by using clangd's
find-all-references function. However, I have only tested plain skins,
not Lua addons, so that could afford some extra checking.
- Selection between Tails' Way (existing Tutorial) and Eggman's Way (Playground)
- Semi-passable UI
- Characterful descriptions
- Add "PlaygroundRoute" condition to Challenges
- Fires if you select Eggman's Way
- 0 Chao Keys unless you go back to Goner for the outro (which Playground skips)
- We keep medals, but times are invalid due to the massive overhaul!
- To avoid double increment of minorversion, this will not fire on current internal, only 2.3 gamedatas
- Misjudged the boundary conditions as `numskins == MAXSKINS` is valid
- Internal version of public MR !113, credit Alu Folie for bringing attention to the area of error
- 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
- For programmers:
- Deprecate GamedataSprayCanJson
- Previously stored colour name and map name together.
- Was swapped in place to move invalid entries to the back.
- If the old info exists, we convert it.
- Instead:
- Store list of colour names
- Index into that list in GamedataMapJson to write map ID
- Stable-sort the list as collected then uncollected
- Write only valid entries into gamedata_t
- Use the map ID reference to link map back to final order
- Sounds more complicated, and it kind of is - but the code is WAY more readable, elegant IMO, allows for expansions to be added later and takes advantage of CPP features it didn't originally
- For testers:
- Ideally, nothing should change. Just be careful and remember to keep backups of your gamedata
# Conflicts:
# src/g_gamedata.cpp
# src/g_gamedata.h
Prevents loaded VS unloaded record data output from falling out of sync.
Specifically resolvesKartKrew/RingRacers#130 by making unloaded skins handled properly
The hypothesis for this patch is that the operating system has not actually
finished writing the file to disk when moving the tmp file into place. The
move operation is atomic, but the write is not, even when flushed or using
unbuffered IO. So we reorder these operations, make the old save .bak
atomically and write the new save in place.
I doubt saving this backup will actually be useful given the frequency of
saves in the game, but at the very least it leaves _some_ backup in place in
the event of failure.
- Most R_SkinAvailable calls should be returning index into demo.skinlist (same numerical value as when demo was recorded), for demo sync
- A handful of general things permit exception for this
- Expose `replaynumskins` (calculated as `(demo.playback ? demo.numskins : numskins)`) to Lua
- There's *always* more that can be done for this, but this is the minimum spec that can at least be somewhat stable
- If emerald not yet collected on that cup, pick the first uncollected emerald, then get the cup's CUPCACHE_SPECIAL with that ID to pick the stage
- Already collected emeralds retain their swappage across gamedata saves
- Returns to normal order if you get all 7 OR Special Mode is unlocked (chao key? debug? password in modded games? sky's the limit)
- Pops up a Message from the Stars telling you the gems have been returned to their natural place
- Add-ons will always use their dedicated sealed star, since it's unordered material
If it weren't so last minute I could have a better solution for GP Backups, but right now what I've gone for is it always trusts whatever G_GPCupIntoRoundQueue does AS LONG AS THE COURSE ISN'T THE ONE YOU'RE RELOADING INTO. If it IS, then it checks to see if it's exactly what's been saved, and complains (with the generic error message, unfortunately) if it isn't.