Determining whether a map or cup is unlocked or not is now linear-time.
- Attempted to cache SECRET_SKIN et al as well, but skins are loaded after unlocks... oh well.
- Was staring down the barrel of a triple-nested loop for implementing SECRET_ALTMUSIC, so did this first to set good precedent.
- If a follower with the specific Mystic Melody sound effect (DSMELODY) is used on the Shrine by a local player...
- Activates the shrine after two seconds!
- This is clientside only.
- Stores on the mapheaderinfo's mapvisited record
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.
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.
- 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).
- 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
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
The linedef's behaviour was broken horribly by long map names, and it's not worth the effort to fix it for the following reasons.
- It was considered a security vulnerability to have free access to the console when it was written.
- The game literally had a cvar to disable running console scripts. That's "I am willingly distributing ActiveX Word Documents in 2023" levels of foolhardery.
- Anything GOOD it can do, both Lua and ACS can do better.
- Four evaluation modes.
- Perfect
- Currently no visual implementation
- All others have a cool set of visuals
- Multi-stage animation of a glowing threat and a Star that's Sealed
- If they're relevant, show the gems you HAVEN'T grabbed
- Three modes here
- No gems
- For Easy mode, asks you to brave a higher difficulty
- Chaos Emeralds
- Not all 7 chaos emeralds? Push your rank harder!
- Super Emeralds
- Not all 7 super emeralds? Further challenge awaits!
- `useBlackRock` to make evaluation context less specific for custom material is replaced with `useSeal` option
- M_CheckCupEmeralds(difficulty)
- Returns the Emeralds you have for that difficulty
- Obviously returns 0 for Easy
- Makes the method of checking collected Emeralds for cup contexts significantly easier
Hilariously broken due to the evaulation gamestate, but the first piece of the puzzle: gets the player into the Credits gamestate after the conclusion of a Podium, if the GP context's cup has this boolean set.
We're unlikely to utilise this, but permits creators of custom user-created cups to flex their own style without fighting to replace the default Podium.
- Re-saved every time you lose a life, to prevent scumming
- Force-deletion of your backup has changed
- Don't when saying NO to the GP Backup load prompt
- Don't when returning to titlescreen under non-DEVELOP
- DO when starting a new Grand Prix session of any cup
- Now called "Grand Prix Backup"
- Filename is now `gpringsav.bkp`
- Since available in standard contexts, do a little extra guarding against unsporting behaviour:
- In non-DEVELOP builds, delete Grand Prix Backup when returning to the titlescreen/menus.
- "undo your extra work and make it more generic" - Tyron
Associated menu stuff is rough and unpolished, but it's 1am for the author of this commit.
- Activated for this session by either of the following Passwords.
- `savetheframes`
- `savetheanimals`
- The idea is that if Ring Racers were ever ran at a GDQ, runners would swear their alliegance to a particular Metroid routing at the startup stage, because I think it's funny and cute.
- A live event backup is created/overwritten on non-cheated grandprix level change.
- It's wiped on reaching the podium.
- When hitting PLAY on the main menu, if a live event backup exists, make a Menu Message.
- If you hit A, turn live event backups on for this session, and load the backup.
- If you hit B/X, delete the backup and proceed to character select.
- Done this way to avoid cheating a different character to the end of GP.
- Unlike the (maybe a little over-)engineering of ringdata.dat, liveringbak.bkp is streamlined as all hells and has very little recovery for differing file lists.
- Sprites have directional lighting, like walls
- For normal sprites: contrast is much stronger than walls
- Papersprites look the same as walls
- SpriteBacklight option in level header weakens the contrast for sprites only
- SpriteBacklight subtracts from LightContrast
- E.g. SpriteBacklight = 0 would let it match LightContrast
- E.g. SpriteBacklight = 60 would make the contrast much weaker
- Negative values make the contrast stronger
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
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)
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.
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.
- Quake epicenter + radius now work.
- Since quake epicenter works, removed the display player check on all of the quakes, so other players can feel them.
- Multiple quake effects are stored in a linked list and dynamically created/freed, so they can stack together.
- Made in china
- Made the quake effects only work on the z axis.
- Quakes now alternate their intensity and dampen it over time, instead of randomly picking values.
- Added quake effects for offroad and stair-jank
- Disabled quakes in reducevfx
- Removed actionmovie and windowquake (sorry jartha)
A system for level completion jingles.
- G_Ticker
- Call P_EndingMusic after a certain amount of time has elapsed to begin a jingle
- Play O_RACENT after the jingle's completion instead of baking it or an equivalent into the music
- P_EndingMusic
- Change the function signature to not take a player
- Pick from a series of const char* jingles, instead of sprintfing into a buffer
- _first
- _win
- _lose
- RETIRE
- Simplifying logic to handle intermission music
Replaces Flicky List system from 2.2.
Specify a list of comma-seperated Followers.
Not used by any systems in this commit, but important to store the data for later.
A general purpose system that permits cacheing of GP progression in one place, but which permits future expansion and brings Online GP a little closer to reality.
- Stores a bunch of levels, gametypes, encore state, and restricted-by-rank-ness in sequence.
- Initialised on GP cup select.
- FUTURE WORK: Open to being initialised by other methods
- Digests its way through that sequence as maps are completed.
- Stores round number instead of `grandprixinfo`.
- Map commands as sent over the wire have been adjusted.
- Sends round number and size of/position in roundqueue.
- Now figures out GP Event Type from gametype.
- Can be swung in the direction of a Special Stage with a hint flag.
- This hint flag replaces "fromlevelselect", which was functionally vestigal.
- Maps build anger every time a map isn't selected by anyone.
- If a map is ignored for 4 votes in a row, then on the 5th vote it shows up it will be angry enough to vote for itself when everyone else finishes voting.
- Once it gives its funny vote, or it gets played, it will calm down again.
- 13P+ vote icons are implemented; it's just a basic circle though cuz lazy.
- Made the roulette finish even faster.
- Bots can vote again but now behind a debug cvar.
Each map now just has a countdown for when they'll reappear (stored in mapheader), which gets decremented each time a new map is played. This means it's now compatible across gametype switches, is a lot less complex, and is easy to retrieve the value for a specific map without needing to iterate constantly.
Lots of the old unused code surrounding this function was also removed. Lastly, added a PARANOIA check for callAgainSoon being mishandled.
- 4th map is now a regular option instead of dice.
- Add function to draw a maintained Combi Catcher object on screen.
- Put all vote static variables into either a "vote" struct or a "vote_draw" struct, if it's logic or drawing code.
- Prefix netcode vote globals with _g.
- Add enums/defines for vote magic numbers.
- On level load for GT_TUTORIAL:
- Forces to requested skin (or Eggman if not specified).
- Named `relevantskin` and not `tutorialskin` in case it's useful to use the same apparatus later for Adventure.
- Also sets default skincolor and disables follower.
- Replaces `tutorialmode`.
- Forces gamespeed to Easy, with no POSITION.
- Laps are currently disabled as well, but this can be changed if necessary.
- Hides Free Play.
- Does not count as a played round (except for Chao Keys).
`tutorialmap` has also been removed. This will be replaced in a later commit with something that plays nicer with Ring Racers' existing systems.