It used to take a single int: the area number returned by
mapclass::area(roomx, roomy). All uses of currentarea() were called
with an extra area() call as its argument. Additionally, there's a
good reason why currentarea() should have the room coordinates: in one
of the cases that it's called, there's a special case for the ship's
coordinates. This results in the SAVE screen in the map menu being able
to show "The Ship", while the continue screen shows "Dimension VVVVVV"
instead. Therefore, why not put that exception inside currentarea()
instead, and remove a few callsite map.area() wrappers by making
currentarea() take the room x and y coordinates?
It'll start working in the next commit... See the description there.
(This commit does not add the new strings to the language files, since
Terry now added them separately in his own branch)
I really thought I was going to need to block changing the language
in-game altogether, but activity zone prompts are now fixed and the
only obvious problem I can think of right now is having a dialogue
open, so I just disable the language option if a textbox is displayed.
(like how the map menu only has the save option if a script is running)
This commit removes the `NO_EDITOR` and `NO_CUSTOM_LEVELS` defines,
which cleans up the code a lot, and they weren't really needed anyways.
This commit also disables the editor on the Steam Deck, and adds a
program argument to re-enable the editor, `-enable-editor`.
For some reason, the credits button was always specifically removed from
M&P builds. After some discussion with Terry Cavanagh on the VVVVVV
Discord server, we agreed that there was no reason this should be
removed. So, it's getting put back in.
- ERROR/WARNING screen title was overlapping with message
- Crewmate screen names and rescued statuses were overlapping with each
other
- Textboxes on Level Complete screen were overlapping with each other
and the crewmate was not vertically centered in the box
- Some strings were running into each other in flip mode, instead of
being moved out of each other (PR_CJK_HIGH and PR_CJK_LOW worked the
wrong way around because of FLIP macros being applied to Y coords)
- In-game esc menu was "bouncy" with selected menu options because of a
hardcoded 16 pixel offset
- Bindings in the gamepad menu were overlapping with each other
- Some Super Gravitron "Best Time" labels and values were a little too
close
Two changes:
- The labels on the Game Complete! screen for number of trinkets/deaths
/time etc have been moved two pixels to the right, and had their
limits increased by 1 character
- The inaccuate limit for "quit to main menu" has been increased
If you provided any one of -playx, -playy, -playrx, -playry, -playgc, or
-playmusic in command-line arguments for command-line playtesting, then
the game would always try to play music, even if you passed a negative
-playmusic. This wouldn't do anything in that case, unless you had
MMMMMM installed, in which case it would play MMMMMM track 15
(Predestined Fate Final Level) due to the legacy wraparound bug.
To fix this, only play music if the track provided is greater than -1.
Additionally, to prevent it from playing Path Complete by default if you
specify any of the other save position arguments but n ot -playmusic,
it's now initialized to -1 instead of 0.
This fixes a regression where main game teleporter icons (which would be
target icons if flag 12 was on) would be rendered on the minimap after
loading from a custom quicksave.
This is because this was always enabled when loading from a custom
quicksave, but the game didn't start rendering them until PR #898, which
de-duplicated the minimap rendering code.
The best fix here is to just not enable the teleporters when loading
from custom quicksaves.
This adds an anonymous enum for time trial indexes (e.g. the bestrank,
bestlives, etc. arrays and timetriallevel), and replaces all integer
literals with them.
Just like the unlock arrays, these are indexes to an array in XML save
files, so the numbers matter, and therefore should not use strict
typechecking.
This adds an anonymous enum for the unlock and unlocknotify arrays and
unlocknum function, and replaces all integer literals with them.
This is not named and thus cannot be used for strict typechecking
because these are actually indexes into an array in XML save files, so
the numbers themselves matter a lot.
This replaces the swngame int variable with a named enum and enforces
strict typechecking on it.
Strict typechecking is okay here as the swngame variable is not part of
the API surface of the game in any way and is completely internal.
And just to make things clear, I've added a SWN_NONE enum to use for
initialization, because previously it was being initialized to 0, even
though 0 was the Gravitron.
This adds an anonymous enum for sound effects and replaces all calls to
music.playef that use integer literals.
This is not a named enum (that can be used for strict typechecking)
because sound effect IDs are essentially part of the API of the game -
many custom levels use these numbers. This is just to make the source
code more readable without needing a comment to denote what number is
what sound.
This adds an anonymous enum for music tracks and replaces all calls to
music.play and music.niceplay that use integer literals. Additionally,
this is also done for integer literals for cl.levmusic (except 0) and
music.currentsong where appropriate, but _not_ the music areamap because
that would not make it look very aesthetically pleasing in the code.
This is not a named enum (that can be used for strict typechecking)
because music track IDs are essentially part of the API of the game -
almost every custom level uses these numbers. This is just to make the
source code more readable without needing a comment to denote what
number is what track.
This adds a "- Press {button} to skip -" prompt to both the credits and
ending picture sequences.
It was always possible to skip them by pressing Enter, but not many
people knew this. In fact, even I didn't know this until I saw Elomavi
do it a year or so ago. So it's not really intuitive that this is
possible.
The prompt only shows up if you've completed the game before, and
disappears after two seconds similar to the "[Press {button} to return
to editor]" text.
Unfortunately, given how the game works, game completion is detected
based on if you have unlocked Flip Mode or not. At this point, the
unlock for the game being completed (unlock 5) will already be set to
true no matter what during the Plenary fanfare, but the Flip Mode unlock
(unlock 18) won't be until the player hits "play" on the main menu. As a
special case, the prompt will always show up in M&P (because Flip Mode
is always unlocked in M&P).
This prevents deleting telesaves and quicksaves in special modes and
custom levels.
Otherwise, rolling credits in a custom level would still delete the main
game quicksave.
This fixes a long-standing bug where it's possible to play music during
the Game Over screen in No Death Mode. All you have to do is die while
music is fading out from one area to the next.
The easiest way to do this is in the entrance to Space Station 2, since
there's a music change to Passion for Exploring in Outer Hull (you will
need to go into the zone far enough to activate Pushing Onwards first),
which also contains spikes to die on.
Basically, it's a simple oversight because the nicefade system relies on
music fading out to start playing the next track, but in this case, No
Death Mode fades the music out without accounting for that. It's best to
just disable nicefade entirely when dying in No Death Mode.
Thanks to KSS for reporting this bug.
This calls Game::savestatsandsettings() after unlocking Master of the
Universe (the achievement for completing No Death Mode), instead of
before.
This is not a big deal since Game::savestatsandsettings() is called
anyway whenever the game is gracefully closed since 2.3, but it helps to
make sure people won't lose their achievement data.
2.3 already made it so that if you ran the `rollcredits` command during
in-editor playtesting, you wouldn't be returned to the title screen
while losing unsaved level changes. But there are plenty of other ways
to go back to the title screen from in-editor playtesting too. Namely,
gamestate 1015 (the gamestate after completing a level) and 82 (time
trial complete).
So just add the appropriate checks to those gamestates, and add a
catch-all check in Game::quittomenu(). Additionally,
Game::updatecustomlevelstats() should not update custom level stats
during in-editor playtesting (otherwise it would still happen even if
the game didn't bring you back to the title screen).
Editor notes will also be shown if the game prevents you from going to
the title screen.
Also, just to make things clear, I also added a level note for when the
level is completed during in-editor playtesting. This is just to make it
clear in cases where it might not be obvious that the game returned you
to the editor for this reason. E.g. you have a terminal that calls
gamestate(1013) in a level with 0 custom crewmates, but when you
activate it, it looks like the terminal didn't work for some reason and
just brought you back to the editor. But that's just only because you
literally just completed the level.
This fixes a bug where the wrong music can play on the title screen, as
reported by AllyTally on Discord.
The bug can be triggered by triggering a room transition right as
game.quittomenu() is called (which is easiest to achieve by placing the
player on an oscillating/"out of bounds" room border in a custom level
so they go back and forth between two rooms every frame, and triggering
gamestate 1013, which starts a fadeout to menu if all custom crewmates
are rescued).
When this happens, game.quittomenu() calls script.hardreset(), but the
rest of the frame still executes, even though we set game.gamestate to
TITLEMODE too (because game.quittomenu() was called by
game.updatestate() which was called by gamelogic(), and game.gamestate
is only checked at the start of the frame). This ends up triggering a
room transition, and since map.custommode is guaranteed to now be off
(because of script.hardreset()), the main game music area code kicks in,
and plays something that isn't Presenting VVVVVV.
The bug here is that we're resetting too early when we still have the
rest of an in-game frame to execute. So, instead, we should only reset
at the end of the frame, and this can be achieved with a defer callback.
I noticed that in 2.3, the game would launch with default controller
binds upon first launch (e.g. no pre-existing unlock.vvv or
settings.vvv), but in 2.4, this wasn't the case, and the default binds
would only be set the next time the game was launched. This would result
in you being essentially unable to use the controller on first launch
save for the analogue stick and D-pad to move between menu selections
and move the player.
Bisecting pointed to commit 3ef5248db9 as
the cause of the regression. It turns out returning early upon error or
a file not existing didn't set the default controller binds, because
they were done at the end of Game::deserializesettings(). But the binds
would be set on the next launch because if the file didn't exist, a new
file would be written, not with the default binds, but then the next
launch would read the file, see there were no binds, and then set the
default binds accordingly.
To fix this, I made it so that the default controller binds are set when
Game is initialized. That way, it covers all cases where the game can't
read a settings file.
For some reason, the accessibility option that was meant to disable
flashes doesn't disable ALL flashes, only screen flashes and screen
shaking. This commit disables a lot more, most importantly randomness
in colors, the player flashing on death/respawn, and teleporters
flashing.
There's a few places where textboxes are constructed through code, but
they pass in the color's RGB values in manually. This commit
unhardcodes most of them them, replacing them with a color lookup.
The ones that weren't changed are special cases, like `175, 174, 174`.
I thought 2.2 already had separate map and interact gamepad bindings,
and they simply got neglected and broken with 2.3's split Enter/E key
option. But actually, the new split Enter/E option also applied to
gamepad buttons, and a separate interact binding was added, without
really indicating anything if Enter and E are not split. And I guess
using the same button for map and interact by default also makes sense
for simplicity...
This commit makes sure the button glyph displayed in-game is at least
the correct button. The gamepad bindings menu is also slightly modified
to darken the interact option - the button glyphs code now
automatically causes them to show equal buttons anyway, so it wasn't
too big of a change to also darken the line and disable the binding
option. To me this says: "the interact key is fixed to be the same as
enter right now, but there is a way to change it."
It's still not ideal of course, and I know a similar change to the
gamepad menu to hide the interact option was rejected a year ago
because action sets would already fix it, but it's a year later now,
and showing misleading button glyphs should be fixed in 2.4, whether it
will already have action sets or not (And at this point I think the
plan already is to keep the existing input system for 2.4)
And it's a 3 line diff to darken and disable the option, compared to
fully hiding the option.
This adds a function that converts an action (such as interacting
in-game) to the corresponding button text ("ENTER", "E") or button
glyph (PlayStation triangle, Steam Deck Y, etc). This function
currently only gives the existing ENTERs or Es, because I don't know
how best to detect controller usage, or whether the game is running on
a Steam Deck, or what buttons need to be displayed there. Still, it
should now be really easy to adapt the rendering of keyboard keys to
consoles, controllers, or rebound keys.
To identify the actions that currently need to be displayed, this
commit also adds the initial enums for action sets as described by
Ethan in a comment in #834 (Jan 18, 2022).
This uses DDA (https://w.wiki/6RSQ) to draw a line between the previous
frame's mouse position, and the current frame's mouse position. This
means that there will no longer be gaps in lines of tiles if you move
your mouse fast enough (which is actually rather slow, so it gets
annoying quickly).
The editor's timestep is no longer hardcoded to 24, as I assume that
was only done so there would be less gaps in lines of tiles drawn.
With interpolation, that is no longer an issue, so I've removed the
editor's special case for the timestep.
This commit moves everything left out of the previous commit to the
state system. This means a bunch of new functions were added as well,
to avoid the code in each function becoming too huge. A lot of cleanup
was done as well, simplifying logic, merging duplicated code, etc.
This commit does NOT touch "script hooks", script editor logic and
autotiling, as those seem to be their own separate beasts.
Tools were a mess, spread all over the code with hundreds of `else-if`
statements. Instead of magic numbers denoting tools, an enum has been
created, and logic has been extracted into simple switch/cases, shared
logic being deduplicated.
The base of a state system for the editor has been created as well,
laying a good path for further organization improvements. Because of
this, the entire editor no longer gets drawn underneath the menus,
except for a few pieces which I haven't extracted yet. Either way,
this should be good for performance, if that was a concern.
I have this annoying issue where the game will open on the wrong monitor
in fullscreen mode, because that monitor is considered to be display 0,
whereas the primary monitor I want is display 1.
To mitigate this somewhat, the game now stores the display index that it
was closed on, and will save it to settings. Then the next time the game
opens, it will open on that display index. This should work pretty well
as long as you aren't changing your monitor setup constantly.
Of course, none of this applies if your window manager is busted. For
example, on GNOME Wayland, which is what I use, in windowed mode the
game will always open on the monitor the cursor is on, and it won't even
be centered in the monitor. But it works fine if I use XWayland via
SDL_VIDEODRIVER=x11.
This fixes a bug where the trinket collection text boxes, along with the
cutscene bars, would stay on-screen if the player warped to the ship
while they were up.
This only happens during the gamestate 0 anti-softlock checks, and only
if completestop is active in the first place, so text boxes aren't
cleared if the player is doing something that wouldn't lead to a
softlock otherise.
Fixes#921.
The style we have here is that functions with no arguments are to have
explicit `void` arguments, even though this doesn't apply in C++,
because it does apply in C.
Unfortunately, some have slipped through the cracks. This commit fixes
them.
`hardestroom` currently stores the current roomname, but it was missing
a call to get the translated string. This has been added, fixing the
hardest room appearing as untranslated.
This commit replaces the old system with the new one, making it much
easier to edit the transforming and glitchy roomnames. Additionally,
this syncs flag 72 to finalstretch.
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
This commit adds a better system for animated roomnames.
The old system, like many other systems, were very hardcoded, and can be
described as mostly else-if chains, with some fun string comparisons.
The new system uses lists of text for transformations and glitchy names,
making it much easier to add new cases if needeed.
This commit implements the system but does not replace the old system,
where that is done in the next commit.
The settings for special roomnames can be read from level XML, and
`setroomname()` can be used from commands to set a new, static name.
The `-addresses` command-line option added in 64be99d4 helps
autosplitters on platforms where VVVVVV is not built as a
position-independent executable. macOS has made it increasingly
difficult, or impossible, to build binaries without PIE.
Adding an obvious string to search for will help tools that need to deal
with versions of VVVVVV built with PIE. The bytestring to search for is
`[vVvVvV]game`, followed by four null bytes (to avoid finding it in the
program code section). This identifies the beginning of the game object;
addresses to other objects can be figured out by relative offsets
printed by `-addresses`, since ASLR can only change where the globals
begin.
Partially fixes#928; it may still be advisable to figure out how to
explicitly disable PIE on Windows/Linux.
After discussing with Ally and Dav, we came to the agreement that this
is basically useless since the prompt will always be centered and take
up most of the horizontal space of the screen.
And the x-position was only added as an offset because at some point,
there was a missing space from the side of the "- Press ENTER to
Teleport -" prompt, and the offset was there so people could mimic the
prompt accordingly. But that was fixed at some point, so it's useless
now.
By default, when you open the level editor to start a new level, the
level font will now match your VVVVVV language; so if you're, say,
Japanese, then you can make Japanese levels from the get-go. If you
want to make levels for a different target audience, you can change the
font via a new menu (map settings > change description > change font).
The game will remember this choice and it will become the new initial
level font.
If a custom level doesn't specify a font, it should be the 8x8 font.
But the main game can't specify a font, it's just the interface font
because that's for the language that the game is in.
They need to know how wide the text is going to be in a particular
font, so font::string_wordwrap and font::string_wordwrap_balanced now
take a flags argument like all the printing and dimensions-getting
functions. next_wrap and next_wrap_s take a Font* now, they're internal
to Font.cpp so they can take a Font and avoid double flag-parsing. But
if any non-Font.cpp code needs next_wrap/next_wrap_s in the future, I'd
just make a public wrapper that takes a uint32_t flags and passes the
Font* to the internal functions.
- If the level font is higher than 10 pixels, the third description
line (Desc3) is disabled and unavailable. CJK languages require less
characters to convey the same message (140 characters caused people
to cram tweets in all languages except CJK) and this gives us enough
room in the levels list without having to cram the metadata even more
than it already was or showing less levels per page.
- The "Untitled Level" and "by Unknown" now selectively show up in the
interface font instead of the level font.
I especially focused on graphics.len and the print calls around them,
because graphics.len calls appear a bit less often, might be overlooked
when migrating print calls (thus possibly using different fonts by
accident) and are often used for some kind of right-alignment or
centering which can be changed into PR_RIGHT or PR_CEN with a different
X anyway.
Notably, I also added a new function to generate these kinds of
sliders: ....[]............
Different languages means that the slider for analogue stick
sensitivity needs to be longer to fit possibly long words for
Low/Medium/High, and then different font sizes means that the longer
slider won't fit onscreen in a language that needs a 12-wide font. So
slider_get() can take a "target width", which dynamically changes the
number of characters depending on the width of them in the interface
font.
I kinda forgot that I could force the 8x8 font instead of adapting the
characters in the slider to the font, and other ideas (like using
different characters or a more graphical progress bar) have been
brought up on Discord, so this might all change again sooner or later.