Fixes#1057.
Based on Ethan's hunch, I simply removed the format comparison that
decides whether to halt and restart, or reuse the voice. Voices are
now always restarted when playing a new track.
This also simplifies the code somewhat: `MusicTrack::musicVoiceFormat`
was now no longer used, and an `if (!IsHalted())` was no longer
necessary because `Halt()` already does that. So those are now removed
as well.
At first my CJK changes also misaligned this sprite, and my solution
that time was to position the textbox higher depending on the height
of the textbox, so it would be centered around the crewmate sprite
(which stayed at a hardcoded place onscreen). Recently, #987 changed
these sprites to be relative to the position of the textbox instead of
relative to the screen, which is much more logical, but it stopped
centering these sprites again. But it's an easy fix: simply account for
the extra-added height when adding the sprite in.
This fixes a regression caused by PR #923 (the PR that moved rendering
to be GPU-based) where the interpolation of the horizontal and vertical
warp backgrounds (in over-30-FPS mode) was in the wrong direction, which
makes them look blurry.
This happens because the arguments to the `lerp` function were in the
wrong, reverse order.
On the VVVVVV Discord server, Ally raised the argument that they were in
the same order before she made the changes; therefore the previous code
was also incorrect and it wasn't her fault. However, this argument is
incorrect, because in that case, the reverse order _is_ the correct
order.
The reason that it's now the wrong order is because the output of `lerp`
is now being used as the argument to a source rectangle. Previously, the
output of `lerp` was being used as the offset argument to
`ScrollSurface`, which is analogous to being a destination rectangle.
Fixes#1038.
The main issue was mostly that we have to build C files as C++ in some
cases, and extern "C" wasn't being used everywhere, so linker errors
popped up. The rest is the usual tedious VS2010 stuff like casting void*
to other stuff, so this commit as a whole is pretty boring!
This commit adds new debug lines while you're NOT hovering over an
entity or a block. Additionally, coordinates are now displayed smaller,
to not take up as much vertical space.
The level debugger is toggleable in playtesting mode by pressing Y.
You can toggle whether or not the game is paused inside of the debugger
by pressing TAB. The debugger screen allows you to see entity and block
properties, and allows you to move them around.
The hardest room used to be stored as a room name in whatever language
it was in when you last died enough times to break the record (before
localization, that was always English). Even after localization became
a thing we could get away with this since we only had a single font,
but now we might have actual question marks appearing when the new font
doesn't support characters from the old language.
Therefore, this commit adds more info about the hardest room to save
files - everything that is needed to know in order to do the
translation at display time. These are hardestroom_x and hardestroom_y
for the room coordinates, as well as hardestroom_specialname to mark
special names, in addition to changing the stored room name back to
English. I've also added hardestroom_finalstretch in case we later
decide to drop the English name as a key and rely on just the
coordinates (even though I think that change itself would be more
complicated than any simplification it would accomplish, and I don't
think it's necessary, but better to have it if we do need it later)
As described in #1016, there used to be a bug that inflated
levelstats.vvv in 2.3, which was fixed in 2.4, but there was no way
for inflated files to get smaller yet.
This commit changes the storage of levelstats from a std::vector of
structs to a std::map, so that uniqueness is guaranteed and thus the
stats can be optimized automatically. And it also simplifies *and*
optimizes the code that handles the levelstats - no more big loops that
iterated over every element to find the matching level.
(Farewell to the "life optimisation and all that" comment, too)
I tested this with both my own levelstats.vvv, as well as some inflated
ones (including Balneor's 93 MB one) and saw this code correctly reduce
the filesize and speed up the levels list.
Fixes#1016.
The declarations of `std::vector<std::string> customlevelnames` and
`std::vector<int> customlevelscores` are made quite early in the
function, commented with "Old system", but the place where the old
system is processed is after a big chunk of code that processes the new
system (and indeed never uses these vectors). So for readability,
they're now closer to where they're used.
`levelcomplete` and `gamecomplete` were hardcoded using textbox colors
which were offset by 1. This PR fixes that, no longer requiring
slightly-off colors, and instead adding a new property to textboxes
which tell the game to display either level complete or game complete.
This commit adds a system for displaying sprites in textboxes, meant to
replace the hardcoded system in the main game. This does not support
levelcomplete.png and gamecomplete.png yet, which will most likely just
be special cases.
This ensures loading a 2.4 save in the English-only 2.3 or earlier
doesn't result in missing characters because a translated area name
appears in the save file. We are not reading from <summary> anymore
in 2.4.
The way this is done is by not translating the area names inside
mapclass::currentarea(), but at the callsites other than the one which
saves the <summary>.
For both `tele` and `quick`, I removed these attributes of class Game:
- std::string *_gametime
- int *_trinkets
- std::string *_currentarea
- bool *_crewstats[numcrew]
All this info can now be gotten from members of Game::last_telesave and
Game::last_telesave. I've also cleaned up the continue menu to not have
all the display code appear twice (once for telesave and once for
quicksave).
RIP "Error! Error!" though lol
This is what got saved to the area part of the <summary> tags, and it
was specifically set upon pressing ACTION to save in the map menu.
Which meant tsave.vvv may not get an accurate area name (notably
"nowhere" if you hadn't quicksaved before in that session) even though
it's not displayed anywhere so it didn't really matter. But this
variable can be removed - there's only one place where <summary> is
written for both quicksaves and telesaves, so that now gets the area
at saving time.
Fun fact: custom level quicksaves also have a <summary> tag, and it's
even less functional than the one in tsave.vvv, because it stores
whatever main-game area name applies to your current coordinates.
So I simply filled in the level's name instead (just like what the
actual save box says).
Game::telesummary and Game::quicksummary stored the summary string for
the save files - which is the <summary> tag that says something like
"Space Station, 10:30:59". The game only ever displays the quicksave
variant of these two, for "Last Save:" on the map menu's SAVE tab.
So the telesave has a <summary> too, but it's never displayed anywhere.
(In fact, the area is often set to "nowhere"...)
However, the summary strings have another function: detect that both
the telesave and quicksave exist. If a summary string for a save is
empty, then that save is considered not to exist.
I'm refactoring the summary string system, by making the new variables
Game::last_telesave and Game::last_quicksave of type struct
Game::Summary. This struct should have all data necessary to display
the summary string at runtime, and thus translate it at runtime (so
we don't store a summary in a certain language and then display it in
the wrong font later - the summary can always be in the current
language). It also has an `exists` member, to replace the need to
check for empty strings.
The <summary> tag is now completely unused, but is still written to
for older versions of the game to read.
(This commit does not add the new string to the language files, since
Terry now added it separately in his own branch)
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?
Language folders can now have a graphics folder, with these files:
- sprites.png and flipsprites.png: spritesheets which contain
translated versions of the word enemies and checkpoints
- spritesmask.xml: an XML file containing all the sprites that should
be copied from the translated sprites and flipsprites images to
the original sprites/flipsprites.
This means that the translated spritesheets don't have to contain ALL
sprites - they only have to contain the translated ones. When loading
them, the game assembles a combined spritesheet with translated sprites
replacing English ones as needed, and this sheet is used to visually
substitute the normal sprites at rendering time.
It's important to note that even if 32x32 enemies have pixel-perfect
hitboxes, this is only a visual change. This has been discussed several
times on Discord - basically we don't want to give people unfair
advantages or disadvantages because of their language setting, or
change existing gameplay and speedruns tactics, which may depend on the
exact pixel arrangements of the enemies. Therefore, the hitboxes are
still based on the English sprites. This should be basically
unnoticeable for casual players, especially with some thought from
translators and artists, but there will be an option in the speedrunner
menu to display the original sprites all the time.
I removed the `VVV_freefunc(SDL_FreeSurface, *tilesheet)` in
make_array() in Graphics.cpp, which frees grphx.im_sprites_surf and
grphx.im_flipsprites_surf. Since GraphicsResources::destroy() already
frees these, it looks like the only purpose the one in make_array()
serves is to do it earlier. But now we need them again later (when
switching languages) so let's just not free them early.
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)
The intention of the recent refactor was to make it so that the
temporary surfaces would be allocated only once, when the mode is
enabled, and be freed upon exit.
To do this, Graphics.cpp owns the pointers, and passes them to
ApplyFilter to modify. Except ApplyFilter doesn't actually modify the
pointers, because it's only a single pointer, not a pointer-to-pointer.
So every frame of rendering it would actually be creating a new surface
and leaking memory.
To fix this, they need to be pointer-to-pointer variables that get
modified.
I also added error logs in case the surface creation failed.
Right now, Windows assumes all our console output is code page ????.
That means our UTF-8 output appears mangled. (I ran into this while
testing IME text input by outputting strings to the console)
For a moment I was scared we'd need to do UTF-16 conversions and call
Windows-specific print functions like WriteConsoleW() in our vlog
functions, but fortunately SetConsoleOutputCP(CP_UTF8) works just fine.
Since VVVVVV 2.3, time trial best times are stored not just with the
number of seconds, but also the number of frames. However, there was
no room to display it with the old design of the time trials screen.
Now there is, so it can easily be displayed now with a small change!
Unset frames are stored as -1, which fits perfectly into the frames
argument of help.format_time(), because in that case the amount of
centiseconds is not shown.
It should be noted that opening VVVVVV 2.2 will instantly wipe your
frames records, as described by #1030. But many people will likely
never open 2.2 anymore.
In 8484b36198, I fixed the title screen
showing up if you go to the language screen from in-game, while not
having any language files. There was also one other possible way to get
this to happen that I missed though: if you do have language files, and
you have not set your language yet, and you start a playtest via the
command line (e.g. by using Ved), and you then change the language
from the in-game options. That is now fixed.
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)
The translations for the prompts used to be looked up at creation time
(when the room is loaded or the activity zone is otherwise spawned),
which meant they would persist through changing the language while
in-game. This would look especially weird when the languages you switch
between use different fonts, as the prompt would show up in the old
language in the new language's font.
This problem is now fixed by letting the activity zone block keep
around the English prompt instead of the translated prompt, and letting
the prompt be translated at display time. This fixes a big part of the
reason I was going to disable changing the language while in-game; I
might only need to do it while textboxes are active now! :)
If someone makes a build of the game without copying the correct
folders, their version will have no translations, and display some text
wrong (like credits or button glyphs, or any custom levels that rely on
characters in the fonts being there). So I added a message in the
bottom left corner of the title screen to warn for that.
If you've never set a language before (<lang_set> is not 1), then the
language screen will show up before the title screen. Selecting the
language will then make the title screen show up.
If no language files are present, the old logic for handling this was
to simply show the language screen at startup anyway, and let it
display the error message that language files are missing, as a warning
that the game is not packaged correctly. However, this logic has two
flaws:
- If the user has ever had language files and set a language before
(in a VVVVVV on that computer), the warning element is gone because
the language screen is not shown in that case - the game is simply in
English
- If the user has never set a language before, and then goes to the
language screen later via the menu, they will be sent to the title
screen, even if they were in-game. The main menu will also be broken.
The new way is to not show the language screen at startup if language
files are missing, and to change the logic so that you will only be
sent to the title screen if you actually haven't seen the title screen
yet.
I will also add a proper warning that fonts or language files are
missing by adding a message in the bottom left corner (in place of the
MMMMMM installed message).
We had two separate cases for translators for this string (a
"TO UNLOCK:" one and a secret lab trophy one) but I forgot to use
the latter in the code, so both places in the game were using the
former. This is now fixed.
Originally, we were using just the hint, but this didn't work well for
toggling VSync (see #831). Then I added SDL_RenderSetVSync to SDL, and
used that instead for toggling, but we were still setting the hint on
game startup.
Now, to keep things consistent, and just to make sure we don't get any
surprising behavior should things change in the future, this makes it so
game startup uses SDL_RenderSetVSync too.
This fixes#1013 by axing the use of SDL_HINT_RENDER_SCALE_QUALITY and
instead using SDL_SetTextureScaleMode.
The hint is unwieldy to use, and since #923, has resulted in a
regression where starting the game in filtered mode then switching to
nearest results in scaled textures still being filtered.
The proper solution is to use SDL_SetTextureScaleMode on the two
textures that are drawn to the final screen: gameTexture and
tempShakeTexture.
This commit fixes an obscure bug with `destroy(moving)` and
`destroy(disappear)` where, when looping through entities, the code
doesn't actually check what the entity is before trying to destroy the
block underneath it.
To fix this, we just put the block-destroying code *inside* of the
check, instead of being outside of it.
I also fixed the code style because it was horrible.
Closes#925.
My fix here is to delay the font change until all fading-out textboxes
have disappeared. See it as adding a sort of `untilbars` or `untilfade`
for text box fadeout, into setfont.
This doesn't prevent every possible way to change the font of an
existing textbox, but you would need to use internal scripting to still
do it (and basically be doing it on purpose) - the problem in
simplified scripting when you simply do textbox-setfont-textbox is
gone.
Showing the option on the "play a level" option feels to me as though
inexperienced players would think they're not supposed to open the
player levels, because the message says editor levels are unsupported,
right? But the message is only referring to the level editor, so in my
opinion, it's clearer to only show it there.
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.
This puts the code to fix mouse coordinates in stretch mode directly
inside KeyPoll::Poll, preventing the need for any other instances of
mouse coordinate usage to copy-paste code.
If this text on the time trial results screen would overlap with the
time value, all rank labels will be displayed on the header line
instead ("TIME TAKEN:" etc). This works because the overlap with the
time most likely only happens with CJK fonts (where the time will be
very wide because of the font size) while strings like "TIME TAKEN"
take up very little space due to only needing 4 characters or so for
the same information.
- 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.
Turns out `graphics.drawrect` exists. Well, not anymore!
This was another function from before the renderer rewrite which tried
to draw a rectangle by using four filled rectangles. We can draw
outline rectangles properly now, so let's make sure everywhere does it!
As #974 states, the lab background only ever uses the first generated
value from `backboxint`, so let's change it to a normal variable
instead of an array. Also, stars don't need their width/height set to
2 constantly... they never change in the first place, Terry!
Some code still used rectangles to draw things like lines and pixels,
so this commit adds more draw functions to support drawing lines and
pixels without directly using the renderer.
Aside from making generated minimaps draw points instead of 1x1
rectangles, this commit also batches the point drawing for an
additional speed increase.
This fixes a bug where if a track was resumed, pausing it by unfocusing
the window (if enabled, of course) would not resume it after refocusing
the window.
This happens because resuming the music doesn't change currentsong back
from -1, and the window refocusing code checks that currentsong isn't -1
before resuming music.
haltedsong is only used when resuming music. It is set back to -1 when
resuming music or when playing a new track.
Autotiling was a mess of functions and if chains and switch statements.
This commit makes autotiling better by assigning each direction to one
bit in a byte, giving each different combination its own value. This
value is then fed into a lookup table to give fine control on which
tiles get placed where.
The lab tileset can now use the single tiles which were before unused
in the autotiler, and the warp zone's background tool now places the
fill used in the main game.
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.
The clock on the Game Saved quicksave screen has always been upside-down
in Flip Mode. And technically, the trinket was too, but this was
unnoticeable because the default trinket sprite is symmetrical.
To fix this, draw flipsprites.png if these sprites are being drawn in
Flip Mode instead of sprites.png.
There's not any ill effects of it being initialized to 0 that I am aware
of (because in most cases, it either gets overwritten anyways or there
isn't a track playing in the first place), but it shouldn't be 0,
because that's Path Complete, so fixing this just in case.
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.
If you had the map button held before the game transitioned to the
credits and ending picture sequences, then you wouldn't be able to skip
them, because those gamemodes don't have logic to detect when you've
released the map button.
To fix this, just implement the map button release logic.
We do need a better input system soon...
This command was changed from setactivityposition(x,y) to
setactivityposition(y), but there's a small problem here:
```diff
else if (words[0] == "setactivityposition")
{
- obj.customactivitypositionx = ss_toi(words[1]);
obj.customactivitypositiony = ss_toi(words[2]);
}
```
This meant that the function still took two arguments, the first of
which was unused and the second of which was the Y position of the
activity zone. This is now fixed.
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.
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
game.quittomenu() correctly resets state, as it's the function that's
always used when quitting to menu. This fixes a bug where if a level
with assets failed to load, it wouldn't unload the assets.
This exports the previously-internal setLevelDirError function in
FileSystemUtils and uses it for if a level is not found or there was a
parsing error. Previously, if a level failed to load in these ways, it
would take you to the error screen with no error, while printing it to
the console. But this makes it more user-friendly.
As a bonus, the text is localizable, just like the existing usage of
FILESYSTEM_setLevelDirError for if a path couldn't be mounted.
After the scriptclass::startgamemode refactor, a lot of common code is
still being executed even if the level loading failed. This sets the
game-gamestate to TITLEMODE in gotoerrorloadinglevel(), and also returns
early just in case.
Fixes#975.
This is quite a simple bug: If you edit a script, then close it, you
will no longer be able to use the mute buttons (N and M).
The problem here is that in 2.3, key.disabletextentry() was called when
closing a script. However, #944 removed the call. Therefore, a
regression.
If you used command-line playtesting to load a level in a zip, the game
would print a warning saying the level wasn't found. This is because the
warning is printed when it tries to load a level before it loads zips,
inside the metadata load function itself.
To fix this, just move the responsibility for printing the error outside
the function, and put it on the caller.
This prints all binary blob check fails. It's an error if the game
rejects the header and will refuse to load it at all, and a warning if
the game continues on.
This also removes the EOF check (`offset + header->size > size`) as a
fatal error. It will only print a warning now. If the last header goes
past the end of file, it will be handled gracefully by PhysFS, which is
the same case in VVVVVV 2.2. This actually fixes a regression from 2.3
where certain custom level tracks that were working perfectly fine in
2.2 (e.g. Summer Spooktacular's track 15) refused to play since 2.3.
This checks the return value of PHYSFS_readBytes() in all cases, and
uses a wrapper to not duplicate common logic. If the read fails, then it
will vlog an error, else if the amount of bytes read was less than
expected, it will vlog a warning.
Additionally, the space allocated for a file in
FILESYSTEM_loadFileToMemory is SDL_calloc()ed instead of SDL_malloc()ed
so if there are less bytes than expected, the memory will at least be
zeroed. This also means we don't have to do the null termination,
because the last byte will already be zeroed.
The return value of PHYSFS_readBytes() when reading the headers of
binary blobs now assigns to `header->size`, so the call has been placed
after the increment to `offset` because `offset` needs the correct (i.e.
intended) size of the header.
In Italian, "Credits" is "Riconoscimenti", which runs offscreen with
the 3x font size that this title uses in the rolling credits at the end
of the game. I'm not sure if the translators saw that specific
instance, or thought the limit complaint was about the main menu button
all along (which is more prominent and *does* stick out far enough that
the complaint could plausibly have been about that, from a translator's
perspective!)
Either way, it's solved now: this string's width is now checked, and if
it will run offscreen at 3x size, it will now be displayed at 2x size
instead. The limit has been increased from 13 to 20 in the language
files accordingly.
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.
Ever since 2.3, if you bind a controller button to the "menu" action
(i.e. back/escape) you won't be able to change that button to any other
action, because pressing it anywhere in the binding menu will exit to
the previous menu, without applying the binding.
I know action sets will overhaul everything, but 2.4 may (probably
"should") come out before we have action sets. This part is very
broken, and the fix is very easy: just move the bind-assigning code to
above the menu-return-on-esc code, and add a return.
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.
This updates the interpolation positions of the player when transforming
into and out of VVVVVV-Man.
Otherwise, it can be seen that the player "zips" quickly during these
transformations if the Secret Lab entrance cutscene is played with
screen effects off.
Unfortunately, 1de459d9b4 caused a
regression where musicclass::niceplay() didn't work, because fading out
the music would cause haltdasmusik() to be called, which would reset the
fade variables.
To fix this, haltdasmusik() will now only reset the fade variables if
it's not called from a fade, which is signaled with a function
parameter.
`platv` is a room property that controls platform speed, and it has
always worked (other than some weird storage issues due to a bug).
However, the editor has no way to edit it currently, so people had to
resort to editing the level file by hand, or with a third-party tool.
This commit simply adds an easy way to modify platform speed.
VED has a fill bucket subtool for tiles and backgrounds, which is
really useful when creating rooms. This commit adds a fill bucket as
well, with an adaptive tool highlight, unlike VED.
This fixes a regression from 2.3 where the very beginning of A New
Dimension isn't silent.
A New Dimension's level music is set to Predestined Fate, but there is a
script box the player touches right upon spawning that stops playing
music. Then after the player ascends upwards, they touch a script box
that plays Predestined Fate. But in 2.2 and before, the very beginning
is silent due to the script box that stops music.
However in 2.3, due to the changes made to playing music during a fade,
the initial level music trying to play Predestined Fate during a music
fadeout from the main menu resulted in Predestined Fate being stored in
the nice fade variables, which kicked in after halting the music since
halting didn't reset those variables.
This resets those variables whenever music is halted, and now the
beginning of A New Dimension is back to how it was in 2.2 and before.
This fixes a regression where the red channel 0 glitch didn't work,
because the surface was always whitened, because LoadSprites would
whiten the image before converting it to surface.
This regression happened because of #923.
Fixes#962.
Misa asked me if this should only work for non-transparent textboxes,
and it shouldn't - that was kind of an oversight.
To make it work for transparent textboxes as well, I made a little
restructuring to avoid duplicating the code - fill_buttons() is now
called textbox_line(), and it replaces the direct accessing of the
textbox lines in the printing loops. The code that checks the width
of the textbox does not need to be copied, since the text box is
naturally not drawn for transparent text boxes.
This makes it so that `CustomEntity`s, at least internally, do not use
global tile position. Instead, they will use room-x and room-y
coordinates, which will be separate from their x- and y- positions.
This makes it much easier to deal with `CustomEntity`s, because you
don't have to divide and modulo everywhere to use them.
Since editorclass::add_entity and editorclass::get_entity_at expect
global tile position in their arguments, I've added room-x and room-y
arguments to these functions too.
Of course, due to compatibility reasons, the XML files will still have
to use global tile position. For the same reason, warp token
destinations are still using global tile position too.