- New Rank icons
- Use Minimap-sized Emerald instead of Battle HUD
- Shows associated character you last updated (or matched) the position with
- Also removes the temporary printout display on G_LoadGameData.
Please note this is incompatible with gamedata from previous commits on this branch. As always, keep a backup of your last stable gamedata.
A system for record data to point to either loaded or unloaded skins.
- When writing to gamedata, stores a funny internal reference id on skinrecord_t.
- This ID is then used when writing out subsequent references ala mobjnum.
- As an example, has been attached to cup windata.
- Also assigned based on profile skin when gamedata importprofilewins event is occouring.
- Set to your current skin if you complete a Cup, OR if you get equal or better on any existing Cup.
- Successfully reassigned alongside unloadedskin records when a skin is added.
- TEMPORARY: Character ownership of Cup wins are displayed in your latest-log.txt, in lieu of an update to the cupgrid
Track wins per character.
- If you have an existing gamedata of minor version 2 or earlier, attempt to import your last-used profile's wins onto the character you last used on that profile, so you're not starting from nothing. It's not quite accurate, but it's something.
- Much like map headers and cups, these are also tracked in an unloaded_skins_t linked list. That work was done in this commit because gamedata is actually loaded before even base-game characters, which is annoying but at least confirmed it was working quicker.
- TEMPORARY: Your per-character wins are displayed in your latest-log.txt, in lieu of an update to the in-game statistics menu
- Use WRITESTRINGL and READSTRINGL instead of their N equivalents. These are mostly the same, but the macro length is inclusive of the null terminator, so this prevents one-byte overruns.
- Make sure datatype for iteration over nummapheaders is of sufficient unsigned region
- Make sure hashes are calculated based on final field for guaranteed consistency, not initial buffer
- Additional newlines for spacing
More opportunities for early rejection in table-wide searches, in anticipation of future work.
- Many circumstances independently implemented string name comparisons. Most of these have been converted to use R_SkinAvailable, with one exception.
- M_CharacterSelectInit already needs to iterate over the characters, so produce the skin hash independently.
- Previously duplicated across R_AddSkins and R_PatchSkins, now handled in R_ProcessPatchableFields.
- Increase the size of the buffer to SKINNAMESIZE+1, to prevent silent failure for long character names.
IMPORTANT NOTE - increments gamedata minor version to 3, as it is necessary to backfill windata to prevent losing existing cup records.
This is essentially commit 1482fd5 but for cups.
- All cups currently loaded with nonzero windata are written on gamedata save.
- This reduces gamedata size for a player who has not unlocked every cup!
- On gamedata load, if a cup is not loaded, store the extra windata on a linked list.
- On cup header creation, check the linked list to see if an associated unloaded cupheader windata exists.
- If it does, write the record onto the cup structure directly, and delete the "unloaded cupheader" storage struct!
- Then on the NEXT gamedata save, in addition to all loaded cupheaders, it writes the extra windata kept in long term storage in exactly the same format.
Only persist the full data while unloaded if player has explicitly achieved something on that level, rather than merely visited (such as in a netgame).
Achieving something counts as:
- Getting a best time
- Getting a best lap time (Race only)
- Getting MV_SPBATTACK (completing an SPB run)
- Completing the level IF the level has LF2_FINISHNEEDED (finish level in GP/MP before you see it in Time Attack)
You will run into memory limits before this happens, but... if you have major quantities of unloaded mapheader record data, avoid a rollover in the counter of records to write.
In the previous entry in the series, due to level header records existing in a NUMMAPS-sized table always saved and loaded in full, Time Attack times persisted even across game loads without the relevant custom levels added.
However, this was changed with the long map name system. Map records were assigned to level headers, which were only created on level load.
This new system brings Ring Racers up to parity (or better, due to the reduced incidence of header conflicts!)
- All levels currently loaded with records attached are written on gamedata save.
- This reduces gamedata size for a player who has not unlocked every level!
- On gamedata load, if a level is not loaded, store those extra records on a linked list.
- On level header creation, check the linked list to see if an associated unloaded mapheader record exists.
- If it does, write the record onto the map structure directly, and delete the "unloaded mapheader" storage struct!
- Then on the NEXT gamedata save, in addition to all loaded mapheaders, it writes the extra records kept in long term storage in exactly the same format.
Preperatory work for the next feature on my agenda.
- No longer independently allocated.
- This was a byproduct of the previous NUMMAPS-based implementation. It's just cleaner to have it live directly on the mapheader_t, no caveats about it.
- Now contains mapvisited bitflag array.
- Now all to-gamedata properties on a mapheader's struct are grouped together.
- I was of two minds about it, but decided that this would have cleaner guarantees for compartmentalisation, saving, and loading.
- They can still be wiped independently (G_ClearRecords for time/lap and M_ClearSecrets for mapvisited).
Foundational assistive work.
Also guarantees a consistent cup name length in memory, as some places read/wrote 15 bytes, and some places read/wrote 16 bytes. We settle on the latter (including null terminator) for the broadest backwards compatibility.
The problem with bots randomly going towards the same spot in podium was...
- If you failed a Sealed Star, K_UpdateGrandPrixBots wouldn't make the puppies non-spectators
- P_SpawnPlayer catches the spectator status...
- ...but it's in a loop that calls K_GetPodiumPosition...
- ...which ignores spectators...
- ...which means this is too late to catch it, so we simply have to do it earlier.
Also short circuits P_SpawnPlayer's and K_UpdateGrandPrixBot's attempts to set so the author of this commit can be certain what is being done is correct
Map things are writeable in Lua, which I am pretty certain is a mistake because mapthings are not sent over the network at all. I considered making them net-synced (it would be relatively easy), but it also aligns with another, more "philosophical" issue: Doom generally copies over properties from mapthing_t into mobj_t, and then only refers to it again when needing to respawn an object -- mapthing_t is not really intended to be referred to very often at runtime. At best it's slightly annoying since some objects rely on a spawnpoint for behavior changes, at worst it may make ACS more confusing in the future since Thing and Mobj tags are mixed together or less useful since they wouldn't be able to modify behaviors of objects that are based on args.
So I decided to solve these two issues at the same time; just treat mapthing_t as something to copy values from, like OG Doom does it. This basically just means that special and args are also part of the mobj now instead of the mapthing, which should fill any desire to edit this stuff from Lua, and reduces the number of instances where objects need to check for their spawnpoint to function properly.