SDL just got an API to toggle VSync without having to tear down the
renderer ( libsdl-org/SDL#4157 ). We can remove the workaround and use
that instead. For now, we are putting it behind an ifdef until SDL
2.0.18 officially releases in November.
Fixes#831.
Constants.h will house constants like the screen size and others. But
basically only the screen size for now.
Now we don't have to type that "4 bytes per 40 chars (whole screen)"
comment everywhere...
Since those are all downstream recipients of either static storage or
memory that doesn't move for the duration of the custom level, it's okay
to make these be `const char*`s without having to redo any of the RAII
memory management.
mapclass::currentarea() is included in this as well. I also cleaned up
Tower.cpp's headers to fix some transitive includes because I was
removing UtilityClass.h includes from all other level files too.
The "Untitled room" names no longer show any coordinates, because doing
so would require complicated memory management that's completely
unneeded. No one will ever see them, and if they do they already know
they have a problem anyway. The only time they might be able to see them
is if they corrupted the areamap, but this was only possible in 2.2 and
previous by dying outside the room deaths array in Outside Dimension
VVVVVV, which has since been patched out. Besides, sometimes the
"Untitled room" gets overwritten by something else anyway (especially in
Finalclass.cpp), so it really, really doesn't matter.
Turns out, the r, g, and b arguments don't actually do anything!
There was a call to RGBf() in the function. RGBf() is just getRGB() but
first adds 128 and then divides by 3 to each of the color channels
beforehand. Unfortunately, RGBf() does not have any side effects, and
the function threw away the return value. Bravo.
This also reveals that the website images drawn in the credits in the
main menu are only recolored because of a stale `ct` set by the previous
graphics.bigprint(), and not because any color values were passed in to
drawimagecol()... What fun surprises the game has in store for me every
day.
I'm honestly not too sure why drawcustompixeltextbox ever existed? All
it seemed to do was draw even more horizontal/vertical tiles to finish
any gaps in the tiling... which was all completely unnecessary and
wasteful, because even the previous drawpixeltextbox implementation
covered all gaps in all custom level map sizes that I tried.
Anyway, that at least gets rid of one copy-pasted function.
This is a pretty hefty commit! But essentially, I made a new editorclass
object, and moved all functions and variables that only get used in the
in-game level editor to that class. This cleanly demarcates which things
are in the editor and which things are just general custom level stuff.
Then I fixed up all the callers. I also fixed up some NO_CUSTOM_LEVELS
and NO_EDITOR ifdefs, too, in several places.
This accompanies the editor.cpp -> CustomLevels.cpp change; I'll be
splitting out the editor functions in the next commit. The name of the
include guard has been changed as well, but not anything else.
This is because the y-position of the graphics.onscreen() check was a
little too high. Then their name (under Beta Testing) would suddenly
disappear too early. You'd have to look real close to spot it, but it
does happen. It's cuz the credits are all kinda hardcoded, which is
probably bad, but fixing that would have to come later...
I talked with Ethan earlier about this. For 2.3, he wanted me in GitHub
contributors (well, still separate from the rest), to really highlight
the source-code-release community-driven nature of 2.3, but he said it'd
be fine to put me in C++ credits in 2.4.
To match the option to nuke all main game save data, there is also now
an option to nuke all custom level save data separately (which is just
all custom level quicksaves, along with stars for level completion). It
has its own confirmation menu too. It does not delete any levels from
the levels folder.
Custom level quicksaves are NOT affected by the clear data menu, so the
player should be able to delete quicksaves this way. The quicksave
confirmation menu now has an extra option to delete the save (and that
option also has its own confirmation menu before deleting).
This error case can happen, but if it does, non-console users get an
ERROR page with no further information. So use setLevelDirError if this
failure mode happens. And Menu::errorloadinglevel needs to be changed to
accomodate that.
It's possible to get one page of levels by removing all the built-ins,
either by removing them directly from data.zip or by putting files with
the same filenames as them in your level folder that don't contain
nothing.
And hey, there's already a check for if no levels exist at all, so why
not check for this too?
If a zip file is improperly structured, a message will be displayed when
the player loads the level list.
This will only display the last-displayed improper zip, because there
only needs to be one displayed at a time. Also because doing anything
more would most likely require heap allocation, and I don't want to do
that.
Some people prefer the 2.2 behavior where unfocusing pauses the game,
but the music still plays. One such person is Trinket9 on the VVVVVV
Discord server, who wanted it that way.
The reason audio pausing was added in the first place was to prevent
desyncing music in levels with cutscenes that synced to music. Rather
than reverting it, let's add this option instead.
This is because the fade delay did not last long enough.
I was under the mistaken impression that the fade animation lasts for 15
frames. However, this does not account for the fact that the offset of
each fade bar is dependent on RNG, and the worst case scenario is that
they have an offset of 96 pixels (in the opposite direction of the
fade).
The actual fade animation timer accounts for the worst case scenario, so
the fade animation actually lasts for (320 pixels plus 96 pixels is 416
pixels, 416 pixels divided by 24 pixels per frame equals 17.333...
frames, but since the actual timer keeps adding/subtracting 24 pixels
per frame until it passes the 416-pixel threshold, that gets rounded up
to...) 18 frames.
And an extra frame to make it so deltaframe interpolation doesn't
suddenly stop on the last deltaframes before the screen is completely
black.
I also need to draw the screen black on the map screen when glitchrunner
mode is off, if there's a fadeout going on. Else that would introduce
yet another frame flicker.
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
In order to help players spot the difference between outlined text and
non-outlined text, we now outline the text outline text itself (if text
outline is enabled, of course). But drawing the outline alone doesn't
stand out enough, so we have to draw a solid backing against the text as
well, in order to properly show the contrast.
This makes the text much more readable against certain backgrounds (if
you have text outline enabled), especially against the Warp Zone
background (when you start in "This is how it is").
The text box drawn at the bottom of the map screen isn't wide enough, so
it's possible to see the corners on the right side of the text box if
you have custom graphics like I do.
The solution is to increase the width of the text box by one tile.
This is a lot of copy-pasted code, but a little bit of copy-pasting
never hurt anyone...
The keybind to interact with activity zones and teleporters is now
separate from the keybind to open the map, or return to the editor from
in-editor playtesting, or restart a time trial. The keybind is now E,
and the default controller bind is X. No controller button prompts, but
the game didn't have controller button prompts anyways, so whatever.
Doing this now because if people's muscle memory are going to be broken
by not being able to spam the map keybind anymore, at least we can help
a bit by changing the keybind so they can keep spamming it - their
muscle memory is going to be broken anyways.
This option has to be enabled by going to the speedrunner menu options
and selecting "interact button". It is disabled by default.
All prompt text needs to be string-interpolated every time they are
drawn, because it is possible for people to change which interact button
they use in the middle of gameplay, via the in-game options.
Closes#736.
This fixes the finalstretch tile shifting persisting if you return to
the main dimension and final_colormode isn't reset properly.
It's possible to do so in the main game by using a teleporter in
finalmode while having the Intermission 1 or 2 companion active.
For custom levels, level makers can make a setup that automatically
turns on finalstretch, goes to finalmode, and then returns to the main
dimension. The only thing being... as a level maker myself, this tile
shifting REALLY doesn't seem useful (and no one has ever used it because
the setup to do so hadn't really been found or documented until this
year). For one, the exact shift is randomized every time (there's an
fRandom() call to cycle the colors). For two, it goes away after the
player saves and reloads the level. And for three, it doesn't animate
like it does in finalmode (this is the biggest reason IMO).
Nevertheless, I've decided to keep support for this in custom levels, in
case someone in the future does want to use it and is okay with the
limitations.
This factors out the slowdown and invincibility conditionals to a
function. This means less copy-pasted code, and it also conveys intent
(that we don't want to allow competitive options if we have either of
these cheats enabled).
This function isn't implemented in the header because then we would have
to include Map.h for map.invincibility, and transitive includes are
evil. Although, map.invincibility ought to be on Game instead (it was
only mapclass due to 2.2-and-previous argument passing), but that's a
bunch of variable reshuffling that can be done later.
The game dereferences graphics.screenbuffer without checking it first...
it's unlikely to happen, but the least we can to do be safe is to add a
check and assert here.
This is a simple change - we draw minimap.png, instead of the generated
custom map, if it is a per-level mounted custom asset.
Custom levels have already been able to utilize minimap.png, but it was
limited - they could do gamemode(teleporter) in a script, and that would
show their customized minimap.png, but it's not like the player could
look at it during gameplay.
I would have done this earlier if I had figured out how to check if a
specific asset was mounted or not.
The config option has been removed. I'm going to implement something
that automatically shows and hides the mouse cursor whenever
appropriate, which is better than a config option.
In 2.2 and previous, the game would call resetgameclock() every frame
for the last 30 frames of the time trial countdown in order to make sure
it gets reset. This was in a render function, and didn't get brought out
in 2.3, so 2.3 resets the game clock *while rendering*, which is kinda
bad and is an oversight on my part for not noticing.
Instead of doing that, just add a conditional to the timer so that it
won't tick during the time trial countdown. This fixes#699 even further
by making it so the time trial par can't even be lost during the
countdown, because the timer won't tick up - so you can never get a sad
squeak to play by pausing the game or unfocus-pausing it during the
countdown.
This adds music and volume sliders to the audio options. To use the
sliders, you navigate to the given option, then press ACTION, and your
selection will be transferred to the slider. Pressing left or right will
move the slider accordingly. Then you can press ACTION to confirm the
volume is what you want and deselect it, or you can press Esc to cancel
the volume change, and it will revert to the previous volume; both
actions will write your settings to disk.
Most of this commit is just adding infrastructure to support having
sliders in menus (without copy-pasting code), which is a totally
completely new user interface that has never been used before in this
game. If we're going to be adding something new, I want to make sure
that it at least is done the RIGHT way.
Closes#706.
This is an option for speedrunners whose muscle memory is precisely
trained and used to the 1-frame input delay that existed in 2.2 and
below. It is located in Game Options -> Advanced Options, and is off by
default.
To re-add the 1-frame input delay, we simply move the key.Poll() to the
start of the frame, instead of before an input function gets ran -
undoing what #535 did.
There is a frame ordering-sensitive issue here, where toggling
game.inputdelay at the wrong time could cause double-polling. However,
we only toggle it in an input function, which regardless is always
guaranteed to be ran after key.Poll() (it either happened at the start
of the frame or just before the input function got ran), so this is not
an issue. But, in case we ever need to toggle this variable in the
future, we can just use the defer callbacks system to defer the toggle
to the end of the frame - also added by #535.
Added at the request of Habeechee on the VVVVVV speedrunning Discord
server.
This includes all text from the Gravitron and Super Gravitron.
This is to make the text more readable if they are placed in weird
situations - for example, in custom levels, where the background these
texts get placed on could be anything (custom level makers are crazy!).
This makes it easier to add bounds checks to all accesses of
map.explored. Also, all manually-written existing bounds checks have
been removed, because they're going to go into the new getters and
setters.
The getter is mapclass::isexplored() and the setter is
mapclass::setexplored().