Commit Graph
100 Commits
Author SHA1 Message Date
Misa 880c7ad8e6 Re-add main menu credits button in M&P
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.
2023-08-24 08:43:04 -07:00
Misa 8e3e29a14c Generalize stretch mode mouse scaling fix
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.
2023-08-17 19:57:54 -07:00
Misa 923efe54d6 PR template: Add note on translations
For now, Terry has decided that translations can't be done by just
anyone. This note here should discourage at least some people from
making pull requests to add their own translations.
2023-06-12 14:06:23 -07:00
Misa c1a25aa4c2 CLI: Allow no music to be played if save pos used
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.
2023-06-10 11:41:32 -07:00
Misa dc9c51dbf3 Implement haltedsong
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.
2023-06-08 15:38:26 -07:00
Misa cd713ebc71 Fix regression: Showing teles in customloadquick
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.
2023-06-05 19:21:07 -07:00
Misa 404204172c Fix macOS not liking unconsequential fallthroughs
This is technically a fallthrough, but it is literally inconsequential.
Apparently, though, Apple Clang hates it.
2023-06-05 18:02:24 -07:00
Misa 8e3ec9aeef Use enums for time trial indexes
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.
2023-06-05 17:57:23 -07:00
Misa e931f2a4a1 Use enums for unlock, unlocknotify, unlocknum
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.
2023-06-05 17:57:23 -07:00
Misa 14d034e4c6 Use enums for swngame
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.
2023-06-05 17:57:23 -07:00
Misa 414b0647aa Correct orientation of Game Saved clock in Flip Mode
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.
2023-06-05 17:57:23 -07:00
Misa 4ea26617b8 Initialize currentsong to -1
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.
2023-06-05 17:57:23 -07:00
Misa 4058975ce9 Use enums for sound effects
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.
2023-06-05 17:57:23 -07:00
Misa cdeca65be7 Use enums for music tracks
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.
2023-06-05 17:57:23 -07:00
Misa e9eeaa4186 Remove unneeded play()s in intermission menu
For some reason these were here, but they don't do anything, because the
music here should be playing Presenting VVVVVV already.
2023-06-05 17:57:23 -07:00
Misa ef46dadb68 Show skip prompt during credits and ending picture
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).
2023-06-05 17:57:23 -07:00
Misa d841612be2 Prevent deleting saves in special or customs
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.
2023-06-05 17:57:23 -07:00
Misa 41126c2097 Fix bug: Unable to skip credits in certain cases
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...
2023-06-05 17:57:23 -07:00
Misa d09b0d6d84 Fix bug: Playing music during Game Over screen
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.
2023-05-21 23:31:18 -07:00
Misa 7ff4949066 Call savestatsandsettings after unlocking MotU
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.
2023-05-21 23:08:09 -07:00
Misa 25b4361563 Allow quitting to menu if gamestate is EDITORMODE
Otherwise you wouldn't be able to quit from the editor normally.
2023-05-20 15:22:08 -07:00
Misa 24ef59c65e Prevent other cases of losing unsaved levels by returning to menu
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.
2023-05-18 20:01:03 -07:00
Misa dea77e98fa Defer hardreset to end of frame in quittomenu
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.
2023-05-18 19:11:04 -07:00
Misa 474cdc092e Use levelDirError for graphics errors too
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).
2023-05-17 17:11:26 -07:00
Misa 68199396bd Set custommode and custommodeforreal before loading level
This fixes a bug where if a level errors upon loading, it wouldn't take
you back to the levels list.
2023-05-17 12:12:36 -07:00
Misa 28ef3dd7bf Use quittomenu in gotoerrorloadinglevel
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.
2023-05-17 11:45:21 -07:00
Misa 9387186083 Use setLevelDirError for level not found & parse errors
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.
2023-05-17 10:09:59 -07:00
Misa a5eb361448 Set TITLEMODE if loading level results in error
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.
2023-05-17 08:42:50 -07:00
Misa 27aa10a18c Fix regression: Editing script disables mute buttons
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.
2023-05-15 18:25:43 -07:00
Misa ec587fc37e Remove bogus warning if CLI playtesting zip level
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.
2023-05-15 18:12:28 -07:00
Misa 0f45e0c52e Print binary blob check fails and remove EOF check
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.
2023-05-15 18:04:37 -07:00
Misa 8036471e76 Handle cases where less bytes are read than expected
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.
2023-05-15 17:37:07 -07:00
Misa 90c1a39231 Remove UTF8-CPP from non-bundled dependencies
UTF8-CPP was removed but Dav forgot to also remove it from dependencies
if BUNDLE_DEPENDENCIES was disabled.
2023-05-15 16:26:31 -07:00
Misa fb38668182 Fix regression with controller binds on first launch
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.
2023-04-15 11:20:48 -07:00
Misa 73aee381ad Update lerp positions during VVVVVV-Man transforms
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.
2023-04-05 19:51:58 -07:00
Misa 5fbdf2b663 Fix regression with music fade transitions not working
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.
2023-04-05 19:32:42 -07:00
Misa c88f249f48 Sync language files after merging #963
This syncs the language files to include the new string about changing
platform speed.
2023-04-02 23:50:11 -07:00
Misa 1de459d9b4 Fix nice fade variables persisting when they shouldn't
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.
2023-03-31 21:39:29 -07:00
Misa 8f23261134 Whiten texture only after loading surface
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.
2023-03-30 19:54:46 -07:00
Misa 71dbe95dcb Separate CustomEntity global positions internally
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.
2023-03-29 13:49:08 -07:00
Misa 98feeade02 Fix gray entities applying in main game
As reported by Lilithtreasure on the VVVVVV Discord server, it is
possible to get gray moving platforms and enemies in the main game.

This happens if you play the main game after loading a custom level with
a room that is gray at the same coordinates. E.g. if you play a custom
level with a gray room at (12, 4), then exit and go to Gantry and Dolly
in the main game which is also at (12, 4), then the platforms there
would be gray too.

This is because there is a missing map.custommode check.
2023-03-29 13:49:08 -07:00
Misa 09c07d8ad4 Copy rest of music code to OGG sound effects
The missing piece from sound effects was handling what to do when the
buffer ran out. Which seems to happen often when decoding from OGG,
unlike WAV. This handling involves callbacks to functions named
refillReserve and swapBuffers.

Without this code,some sound effects would be cut off early, as
documented in #953. This might also explain the division by 20 - which
I've copied too, just in case.

Now OGG sound effect tracks should be identical to music tracks (except
I've stripped the looping code out).

Fixes #953.
2023-03-29 13:49:08 -07:00
Misa c36655d5c7 Don't use FAudioVoice_GetVoiceDetails
We were using this function to check if the format of the existing voice
is different from the format of the voice we intended to play. However,
whereas the format we use is the FAudioWaveFormatEx struct, the only
details we get from FAudioVoice_GetVoiceDetails is the
FAudioVoiceDetails struct, which has much less details and is missing
important things like whether the format is 8-bit or 16-bit or something
else.

And of course, if we don't check that the number of bits matches, then
it still leads to playback issues as described in #953.

There are no other functions in FAudio that operate on
FAudioWaveFormatEx structs. So instead, we'll just have to do it
ourselves, and store the format of the existing voice alongside the
format of the intended voice. And then use SDL_memcmp to make sure the
formats are the same before playing, and if not, then destroy and
re-create the voice.

Fixes #953.
2023-03-29 13:49:08 -07:00
Misa 5180e430a2 Sync language files after #944 merged
This syncs language files after the merge of PR #944, since that PR
obsoleted English strings, but didn't sync them.
2023-03-21 18:46:41 -07:00
Misa e3612afbd3 Store display index of window to settings
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.
2023-03-21 00:23:42 -07:00
Misa 54990638fd Persist windowed mode size through fullscreen mode
Previously, the game would not store the size of the window itself, and
would always call SDL_GetRendererOutputSize() (via
Screen::GetWindowSize()) to figure out the size of the window. The only
problem is, this would return the size of the whole monitor if the game
was in fullscreen mode. And the only place where the original windowed
mode size was stored would be in SDL itself, but that wouldn't persist
after the game was closed.

So, if you exited the game while in fullscreen mode, then your window
size would get set to the size of your monitor (1920 by 1080 in my
case). Then when you opened the game and toggled fullscreen off, it
would go back to the default window size, which is 640 by 480.

This is made worse, however, if you were in forced fullscreen mode when
you previously exited the game in windowed mode. In that case, the game
saves the size of 1920 by 1080, but doesn't save that you were in
fullscreen mode, so opening the game not in forced fullscreen mode would
result in you having a 1920 by 1080 window, but in windowed mode.
Meaning that not even fullscreening and unfullscreening would put the
game window back to normal size.

The solution, of course, is to just store the window size ourselves,
like any other screen setting, and only use GetWindowSize() if needed.
And just to make things clear, I've also renamed the GetWindowSize()
function to GetScreenSize(), because if it was named "window" it could
lead one to think that it would always return the size of the screen in
windowed mode, when in fact it returns the size of the screen whatever
mode it is in - fullscreen size if in fullscreen mode and window size if
in windowed mode.

And doing this also fixes the FIXME above Screen::isForcedFullscreen().
2023-03-20 20:59:37 -07:00
Misa e1e9e47d95 Always update scaling before rendering
This fixes the following bug that only occurs on Wayland: If the game is
configured to be fullscreened and in stretch mode, on startup, it won't
be in stretch mode. It will appear to be in letterbox mode, but the game
still thinks it's in stretch mode.

This is because during the ResizeScreen() call on startup, for whatever
reason, the window size will be reported to be the default size (640 by
480) instead of the screen resolution of the monitor, as one would
expect from being in fullscreen. It seems like when the game queries the
window size, the window isn't actually in fullscreen at that time, even
though this is after fullscreen has been set to true.

To fix this, I decided to always update the logical size before
SDL_RenderPresent() is called. To make this neater, I put the scaling
code in its own function named UpdateScaling().

This bug has existed since 2.3 and does not occur on X11. I tested this
on GNOME Wayland, and for testing it on X11, I used Openbox in a Xephyr
session while running VVVVVV with SDL_VIDEODRIVER=x11.
2023-03-20 20:56:15 -07:00
Misa 64874065eb Rename menuoffTexture to tempShakeTexture
It turns out this texture is only used as a temporary texture to draw
the screen with an offset before rendering it to the output target.

I thought it was used for drawing the map menu animation, but that was
only true of `tempBuffer`, and is no longer true of the new render
system.
2023-03-20 17:05:57 -07:00
Misa 397d7f21b4 Add length check to OGG check
Dav999 pointed out this potential issue on Discord.

While basically all memcmp implementations will terminate early here if
there's a null byte (because it's mismatched), it doesn't hurt to add
the check here.
2023-03-19 14:05:51 -07:00
Misa 2dc7f0b5e8 Remove dialogue and cutscene bars if disabling completestop
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.
2023-03-19 13:48:01 -07:00
Misa 88d49547d4 Evaluate flipping eligibility per-entity
This fixes a regression where the behavior of duplicate player entities
is different, causing a gameplay section in Vespera Scientifica to be
impossible, as described in #903.

In the level, you are allowed to flip in mid-air. Vespera accomplishes
this by having two duplicate player entities stuck in a platform. One of
them is responsible for letting the player flip in one direction, and
one of them is responsible for letting them flip in the other.

In 2.3, this works because in order for a player entity to flip,
`game.jumppressed` is checked, and the entity will flip if
`game.jumppressed` is greater than 0, then `game.jumppressed` will be
set to 0. In this way, whenever a player entity flips, it will set
`game.jumppressed` to 0, so whenever the next player entity is
evaluated, `game.jumppressed` is 0 and thus _that_ entity will not flip.

This is because the for-loop surrounds both the `game.jumppressed` check
and flipping the entity. In 2.4 after #609 and subsequent patches,
however, this is not the case. Here, the for-loop only surrounds
flipping the entity. Therefore, the `game.jumppressed` check is
evaluated only once. So, it will end up flipping every player entity if
the entities are eligible. In this case, one of them is eligible twice,
resulting in the game flipping gravitycontrol four times, which is
essentially the same as not flipping at all (except for all the sound
effects).

Hence, the fix here is to make it so the for-loop surrounds the
`game.jumppressed` check.

Now, this doesn't mean that the intent of #609 - that duplicate player
entities have the same initial velocity applied to them when flipping -
has to be removed. We can just put the for-loops back in. But I
carefully implemented them in such a way that the overall function is
not quadratic, i.e. O(n²). Instead, there's a pass done over the
`obj.entities` vector beforehand to store all indexes of a player entity
in a temporary vector, and then that vector is used to update all the
player entities. In this manner, the function is still linear - O(n) -
over the number of entities in the room.

I tested this to make sure that no previous regressions popped up again,
including #839, #855, and #887. And #484 is still resolved.

Fixes #903.
2023-03-18 21:30:23 -07:00
Misa ba7519106f Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.

This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.

This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.

Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.

Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.

Fixes #900.
2023-03-18 18:24:30 -07:00
Misa 05ed7e041c Use VVV_freefunc to call stb_vorbis_close
I noticed that this call wasn't using VVV_freefunc. I missed it earlier
when going through Music.cpp and checking for instances when
VVV_freefunc should have been used
(a926ce9851).
2023-03-18 17:51:55 -07:00
Misa d6bc319535 Add sample rate mismatch check to sound effects
Sound effects already get recreated if the number of channels
mismatches, but the same could be true if the sample rate mismatches
too, which was the case with music tracks as described in #886.

So, just to be sure - and to be consistent with music tracks - sound
effects now check that the sample rate matches, too, and if not, will be
recreated.
2023-03-18 16:40:24 -07:00
Misa d54e98200f Fix loading sounds with non-16 bit depths
This fixes an issue where sound effects of bit depths that weren't 16,
such as 8, were being played incorrectly, as described in #886.

The problem is that when loading the sound effect, we would always set
the bit depth to 16 no matter what! Instead, we should set the bit depth
to the actual bit depth of the file.

Fixes #886.
2023-03-18 16:38:28 -07:00
Misa 671c90f7dc Fix music playing wrong if played after track of different format
As described in #886, if a track was played when an existing track was
already playing, and the sample rates of the two tracks differ, then the
second track would play wrong and distorted.

This is because the second track would play with the sample rate of the
first. To fix this, halt the track if the sample rate is mismatched,
which destroys the voice. This results in the voice being recreated
later in the Play() function. The track is also halted if the number of
channels or bit depth is mismatched.

Fixes #886.
2023-03-18 16:30:19 -07:00
Misa 9a87d23719 Style: Fixup instances of void arguments
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.
2023-03-18 15:28:33 -07:00
Misa 63bc71b796 Always add null terminator when loading files
This removes the `addnull` argument from `FILESYSTEM_loadFileToMemory`
and `FILESYSTEM_loadAssetToMemory`, and makes it so a null terminator is
always appended no matter what.

This simplifies things and removes the need for callers to make the
decision about null termination and what its implications are. Then you
get cases where null termination might not happen when it should be,
such as the one df577c59ef (#947) fixed.

When FIQ added the `addnull` argument in
5862af4445 (#117), I'm guessing he did it
because he wanted to be cautious about adding the null terminator to
every file, so he only did it for XML files, which was the only case
needed at the time. But really, there's no downsides to always appending
a null terminator. In fact, it's already always done whenever the STDIN
buffer is loaded.
2023-03-18 15:12:24 -07:00
Misa acfa085fd1 Remove reference from customlevelclass::load arg
For some reason, originally, this function mutated the std::string
passed into it by reference. So calling the function could potentially
mutate whatever got passed in, and callers potentially could have relied
on that behavior.

Now that the surrounding callsites have all been cleaned up, though
(especially scriptclass::startgamemode), it's clear that it's only used
in two places: Loading the level in the editor, and loading the level in
live gameplay. In both cases, the passed-in string isn't used ever again
afterwards.

So, it's safe to delete the mutable reference without any undesirable
effects, making the code cleaner and easier to understand. The function
now mutates a copy instead of mutating whatever the caller has.
2023-03-16 21:09:41 -07:00
Misa 58d21e956b Fix analogue filter allocating/freeing surfaces every frame
This removes memory churn caused by using analogue mode.

The surfaces are only allocated if analogue mode is turned on, and kept
after they are initialized. Otherwise, if analogue mode is never turned
on (which will be the case for the vast majority of the time the game is
played), then no extra memory is used.
2023-03-04 14:02:47 -08:00
Misa 5eecc2a21d Don't draw texture onto itself for scrolling
Drawing a texture onto itself seems to produce issues on Metal.

To fix this, use a temporary texture instead, that then gets drawn onto
the original texture.

Fixes #927.
2023-03-03 20:21:53 -08:00
Misa 33fa0750be Destroy gameScreen after all other graphics are destroyed
This is because destroying the renderer causes use-after-frees since the
renderer destroys all textures when it gets destroyed.

This fixes a Valgrind error where an invalid read occurs because the
font textures get destroyed again after the renderer is destroyed.
2023-03-03 16:46:15 -08:00
Misa 04743abe91 Destroy towerbg and titlebg textures in destroy_buffers
For consistency, since they are created in create_buffers as well. I
checked with Valgrind (which is very noisy on Wayland, it turns out),
but I didn't see anything about them not being freed. It doesn't hurt to
use VVV_freefunc here anyway, though, since it does a NULL check and
nulls the pointer afterwards, which should prevent double-freeing and
use-after-frees.
2023-03-03 15:34:13 -08:00
Misa d1e9bdc284 Rename tempTexture to menuoffTexture
I'm going to soon be creating an actually temporary texture, so having
two textures named "temp" would get confusing. This is also a good
chance to correct the name of this texture, because it's not really
temporary, but it's used for map menu animation rendering.
2023-03-03 15:25:15 -08:00
Misa e31344c68c Remove trailing whitespace in create_buffers
This was added in by AllyTally in
19b2a317f1.
2023-03-03 15:23:55 -08:00
Misa 27da7fe935 Update BUNDLE_DEPENDENCIES description with removal of UTF-CPP
We no longer use UTF8-CPP, so the mention of it should be removed. Also,
I conjoined the two phrases with a semicolon; it flows better.
2023-03-01 22:58:15 -08:00
Misa ede52ccc48 Add Jules de Sartiges to Credits.h
I noticed this name was present in CONTRIBUTORS.txt but missing in
Credits.h. Their contribution was PR #782.
2023-02-21 12:28:57 -08:00
Misa abab6864d7 Add iliana etaoin to contributor list
PR #931 was merged, so this adds xer to CONTRIBUTORS.txt and Credits.h.
2023-02-21 12:27:50 -08:00
Misa 2ac85e6929 Fix editor selection box not showing up
Whoops.

Also outlined the text for page 2 too.
2023-02-19 12:31:58 -08:00
Misa 6e6cf1bfc3 Draw text outline on editor hotkeys
This makes them stand out more.

The border around the tool has also been moved to be drawn first.
Otherwise, it would be drawn on top of the outline of the text, which
would look bad.
2023-02-19 12:12:02 -08:00
Misa 64be99d496 Add -addresses command-line argument
This prints the address of every global class to the console, and then
exits.

This is useful for autosplitters, which read memory addresses directly.
Any time a new version of the game is shipped, this makes updating the
autosplitters much easier as people don't have to find the addresses of
the global classes themselves.
2023-02-17 20:47:32 -08:00
Misa acca4747f7 Remove x-position from setactivityposition
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.
2023-02-17 20:47:32 -08:00
Misa 299ed90493 static-qualify mount_pre_datazip
This function is used nowhere else, so it should be made static and
unable to be linked to from other files.
2023-02-14 09:44:31 -08:00
Misa b29f3e2fae Silence various warnings in builds removing content
The MAKEANDPLAY, NO_CUSTOM_LEVELS, and NO_EDITOR defines remove content
or features. However, they then raise several warnings because of some
cases, functions, or variables that end up not being used.

This silences them by using the UNUSED macro, or by adding a default
catch-all case if the define is defined (so unhandled cases will still
raise warnings in a build that doesn't have these defines).
2023-02-04 00:14:04 -08:00
Misa f2089c954f Silence warnings about using long long in C++03
We get these warnings because of the typedefs for 64-bit integers in
PhysFS's header files. The compiler will treat them as extensions and
will still compile it fine but it does mean we aren't strictly standards
conforming. Which really isn't a problem anyway. Probably.
2023-02-04 00:13:02 -08:00
Misa 113fbb0fdb Add build type to -version
This adds the build type in brackets in `-version` output after e.g.
"VVVVVV v2.4". The build type is MAKEANDPLAY, NO_CUSTOM_LEVELS, or
NO_EDITOR (which are not necessarily mutually exclusive).

This is appended on to the end of the first line so as to not break
Ved's existing `-version` check which only checks if the beginning of
STDOUT is "VVVVVV" followed by any version number.
2023-02-02 13:10:25 -08:00
Misa 6665f4f8f6 Prioritize loading processed script names
This makes it so that whenever the game loads a script as directed by a
script command, it will first try to load the script from the processed
argument, and if that fails only then will it try to load the script
from the raw argument.

This fixes a regression reported by Dav999 in the custom level "Vungeon"
created by Dynaboom, where a script `ifflag`s to `aselectP1.1` even
though the actual script name is `aselectp1.1`. In 2.3, it would
lowercase `aselectP1.1` and load the script properly, but previous to
this commit it would try to load the script with a capital name and then
fail.
2023-01-31 20:09:44 -08:00
Misa fbe613ce5c Abort and print error if window/renderer cannot be created
This aborts and prints the error from SDL_GetError() if
SDL_CreateWindow() or SDL_CreateRenderer() fails.

We abort because there's not much point in continuing if the window or
renderer can't be created. There might be a use case for running the
game in headless mode, but let's code that in explicitly if we ever want
it.
2023-01-28 23:44:07 -08:00
Misa 2525017990 Set minimum window size (to 320 x 240)
This sets the minimum window size (to 320 x 240), so that the window
cannot be resized to lower than that.

This is because there's no utility in having a game window smaller than
that, and it provides a bonus convenience of being able to resize the
game to exactly 320x240 without needing to be exactly precise about it.

This idea was suggested by Dav999.
2023-01-28 23:37:24 -08:00
Misa fbc9b3ddd7 Use SDL_Point instead of rolling our own point struct
The `point` struct was a relic of ActionScript and was added because of
the Flash 'point' object. However, it seems like Simon Roth didn't
realize that SDL has its own point struct.

With this, `Maths.h` can be un-included from a couple headers, which
exposes the fact that `preloader.cpp` was relying on `Maths.h` being
transitively included from `Graphics.h`.
2023-01-28 23:32:14 -08:00
Misa c7098f84e5 Fix alphabetical ordering of includes in preloader.cpp
Dav999 added `#include "Localization.h"` before `#include "KeyPoll.h"`,
when it should go after instead, because the letter L comes after the
letter K in the English alphabet.
2023-01-28 23:29:04 -08:00
Misa 172e3a8985 Add branch name to window title in brackets
The branch name will be added to the window title if it is an interim
version, e.g. "VVVVVV [master]".

This makes it easier for developers to tell at a glance which build of
the game they're running.
2023-01-16 12:59:48 -08:00
Misa 19a83853b8 Default width and height settings to 640x480
While the window is initialized with 640x480, the screen settings
defaulted to 320x240, which is a tiny window. The screen settings take
priority over the initialized window, so people with no previous
settings file will get 320x240. This makes it so they get 640x480
instead.

The window is still initialized to 640x480 (constants used for clarity)
just in case there's some weirdness if it's initialized to a potentially
odd resolution from the user's settings file.
2023-01-16 12:45:40 -08:00
Misa d2b6fb2d06 Add branch name to interim version information
This is useful for developers who may have multiple builds of the game
from various different branches and may easily forget which build of the
game is what.

This shows up in the bottom-right corner of the title screen and also
with the `-version` command-line option, and in the status message
printed when building the game.
2023-01-07 19:18:28 -08:00
Misa f35618999f Allocate to the size of source surface instead
Otherwise it will slow down since the destination surface is usually
pretty big.
2023-01-05 12:23:34 -08:00
Misa 2e03f2b03d Re-add temporary allocation in BlitSurfaceTransform
Unfortunately there needs to be an intermediate surface for proper alpha
color blending to happen via SDL_BlitSurface. The exact SDL blending
logic seems complicated and unclear for me to implement at the moment,
and my attempts kind of failed, so this is just a stopgap measure to at
least get the game rendering how it was before I screwed it up.
2023-01-04 15:33:56 -08:00
Misa 91339c77db Refactor BlitSurfaceColoured and BlitSurfaceTinted to not allocate
This refactors them to not allocate a temporary surface by instead
simply drawing directly to the destination surface.

This means re-implementing the original semantics of SDL_BlitSurface in
them, which is the function signature they (and BlitSurfaceStandard)
were based off of. So now if src_rect or dest_rect are NULL, it
automatically uses a rect of the entirety of the corresponding surface,
and other things like that. And also some other optimizations like
no-opping if the alpha is 0 (because then nothing will be drawn), and
critical checks (not drawing if the destination pixel is out of bounds,
because then otherwise it would wrap around, or at least that's what it
did when I tested it).

This resulted in a bunch of code that would really suck to copy-paste
because then you'd have to remember to update the other copy, so I
refactored them further and put the common code into one function, while
separating the different code (the exact transformation they do) into
different functions that get passed in through function pointers.
2023-01-02 18:01:42 -08:00
Misa e5d32c653b Rename tempBuffer to menuoffbuffer
In general, "temp" is a bad name because it could mean anything. In this
case the buffer isn't really temporary and it's only used for drawing
the menu with a certain offset, so I made it use a better name. But also
because I'm going to be adding temporary buffers so I don't want the
names to be confused.
2023-01-02 11:19:08 -08:00
Misa 0a51141720 Use named constants in Graphics::create_buffers
I'm gonna be doing work on these and I took the opportunity to clean up
the code style a bit.
2023-01-02 11:17:02 -08:00
Misa 38c3b4ab41 Move warpfcol, warpbcol, and warprect off of Graphics
These are all temporary variables only used when drawing the all-sides
warp background.

warpskip isn't though, it's a persistent variable.
2023-01-02 11:16:03 -08:00
Misa b0ca322b3f Use SDL_GetRGBA and SDL_MapRGBA to read and draw pixels
Honestly not too sure why we ever wrote the mask handling logic
ourselves instead of using SDL functions. Hell, we even used SDL_MapRGB
for Graphics::getRGB before.
2023-01-02 10:44:35 -08:00
Misa 8dc088896f Axe Graphics::ct and Graphics::setcolreal
`ct` was used to be a variable that a color was temporarily stored in
before being passed to a draw function. But this is unnecessary and you
might as well just have a temporary of the color directly. I guess this
was the practice used because temporaries were apparently really bad in
Flash.

setcolreal() was added in 2.3 to do basically the same thing (set it
directly from entities' realcol attributes). But it's no longer needed.

Correspondingly, Graphics::setcol has been renamed to Graphics::getcol
and now returns an SDL_Color, and Graphics::huetilesetcol has been
renamed to Graphics::huetilegetcol for the same reason.

Some functions (notably Graphics::drawimagecol and
Graphics::drawhuetile) were relying on the `ct` to be implicitly set and
weren't ever having it passed in directly. They have been corrected
accordingly.
2023-01-01 20:16:08 -08:00
Misa 351a022ebd Use SDL_Color for colors instead of colourTransform
colourTransform is a struct with only one member, a Uint32. The issue
with `Uint32`s is that it requires a bunch of bit shifting logic to edit
the colors. The issue with bit shifting logic is that people have a
tendency to hardcode the shift amounts instead of using the shift amount
variables of the SDL_PixelFormat, which makes it annoying to change the
color masks of surfaces.

This commit fixes both issues by unhardcoding the bit shift amounts in
DrawPixel and ReadPixel, and by axing the `Uint32`s in favor of using
SDL_Color.

According to the SDL_PixelFormat documentation (
https://wiki.libsdl.org/SDL2/SDL_PixelFormat ), the logic to read and
draw to pixels from colors below 32-bit was just wrong. Specifically,
for 8-bit, there's a color palette used instead of some intrinsic color
information stored in the pixel itself. But we shouldn't need that logic
anyways because we don't use colors below 32-bit. So I axed that too.
2023-01-01 16:36:43 -08:00
Misa f24265f0fb Fix up temporary variables being in topmost scope and bad style
This makes it so temporary variables have their scopes reduced (if
possible). I also didn't hesitate to fix style issues, such as their
names ("temp" is such a bad name), making them const if possible, and
any code it touched too.
2022-12-31 20:46:01 -08:00
Misa 5a6bc8bb9b De-duplicate room tile drawing code in editor
It previously duplicated the for-loop twice, once for tiles.png and
tiles2.png, which just made me sad. Now it doesn't do that.

Also it previously had an alternate tileset == 10 condition for
tiles.png, which didn't seem to do anything because there's no such
thing as tileset 10, and anyways it's useless in-game because when
playing in the actual game it won't draw tiles.png, so I removed it. I
don't know why it was there in the first place.

Since I removed the temp variable from the outer scope, the other usage
of it has to be updated.
2022-12-31 20:45:56 -08:00
Misa e889a4a9b1 Make Catalan language files not executable
For some reason they all have their executable bits set. Presumably this
is because they were made on an NTFS system where every file is
executable (which doesn't sound secure at all but that's another story).
2022-12-31 20:23:04 -08:00
Misa aa2cf3ab4b Use _SDL_HAS_BUILTIN
I think this is because if you both check that __has_builtin is defined
and use it in the same 'if' preprocessor statement, it can error because
there's no equivalent to short-circuiting in preprocessor statements.
_SDL_HAS_BUILTIN should be safer.
2022-12-29 16:13:54 -08:00
Misa 3758d61fe9 Axe scriptclass::resetgametomenu
This creates the game over screen for dying in No Death Mode. It's three
lines long and it's only called once. There's no reason it has to be a
separate function. From the name it sounds like it was meant to be a
generic function but it's anything but that. So just inline it in to
where it's called.
2022-12-29 15:51:16 -08:00
Misa 593641ce82 Always destroy and create new player in startgamemode
This fixes a bug where players could flip in mid-air at the start of
custom levels whose start points were in mid-air, because
onground/onroof were not being reset. This could also be done when
loading in to a save with a checkpoint in mid-air. All that's required
is to exit the game while standing on a surface (or otherwise having a
nonzero onground/onroof).

This reminded me that there were other variables on the player entity
persisting through that made loading in to a level not have a totally
clean slate, such as walkingframe (which could affect sequencing
individual TASes together into one TAS), so it's better to just destroy
the player entity and recreate it.

Except some attributes still have to be persisted for 2.2 and 2.0
glitchrunner mode. But it's better that this is done via a whitelist
rather than a blacklist.

The player duplicate removal code in hardreset is mostly redundant now
(except for some very unlikely corner cases), but there's nothing wrong
with having redundant code as long as it's not harmful. I had a
paragraph here justifying why it could be removed but decided it was
simpler to just keep it.
2022-12-29 15:51:16 -08:00
Misa 97deda2675 Add debug print to unlockAchievement
This print is useful to know if an achievement (one that's not already
unlocked) would actually be unlocked in an end user environment, while
running the game in a dev environment.

Also fixed up the style of the function because it was definitely
inconsistent with the surrounding code.
2022-12-29 15:51:14 -08:00