Commit Graph
100 Commits
Author SHA1 Message Date
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
Misa 69e9a32e1b Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.

The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.

The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.

Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.

This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 14:01:36 -08:00
Misa 6768a33f2d Reset savex, savey, and savegc in hardreset
While refactoring scriptclass::startgamemode, I noticed that these
variables weren't being reset. Fortunately, this doesn't seem to have
affected anything because they get overwritten one way or another in
startgamemode. But it's good just to be defensive and reset them anyway.

They are not reset in 2.2 or 2.0 glitchrunner mode because dying during
exiting to the menu is needed for credits warp, and that means the
checkpoint position needs to be maintained through.
2022-12-29 12:21:18 -08:00
Misa 7a48d0a53e Add Unreachable.h
This is to indicate when a code path is absolutely, for certain, 100%
unreachable. Useful as the default case inside a case-switch that is for
sure 100% exhaustive because it's inside the case of another case-switch
(and the default case is there to suppress compiler warnings about the
case-switch not being exhaustive), which is a situation coming up in my
scriptclass::startgamemode refactor.

It does this by deliberately invoking undefined behavior, either using a
compiler builtin that does the same thing or being a noreturn function
that returns. (And undefined behavior is not undefined behavior if it is
not executed in a code path, otherwise all NULL checks would be useless
because it'd dereference something that could be NULL in another code
path.)
2022-12-29 12:10:25 -08:00
Misa c177f456f4 Unexport getgridpoint
It's a method of entityclass even though it doesn't use anything on the
class and it's not used outside of Entity.cpp.
2022-12-11 12:40:45 -08:00
Misa 7347975cc5 Unexport yline
It's a method of entityclass, even though it doesn't use anything on the
class and it's not used outside of Entity.cpp.
2022-12-11 12:37:55 -08:00
Misa 52e29b68bc Unexport gridmatch
It's a method of entityclass, but it has no reason to be because it
doesn't use anything on entityclass and it's not used outside of
Entity.cpp.
2022-12-11 12:37:43 -08:00
Misa ec520838df Retype testwallsy's tx and ty to be ints, not floats
I have no idea why they were floats in the first place. They are
coordinates, and coordinates are integer positions in this game. They
get converted to float only to be explicitly `static_cast`ed back to
ints in `testwallsy`.
2022-12-11 11:00:20 -08:00
Misa 12820473db Retype dr to be int instead of float
This variable passes along the rule of the entity, which is an int. No
idea why it was converted to a float. Thankfully this is only used for
an unused block type, so it doesn't really matter.
2022-12-11 10:55:25 -08:00
Misa babcadc99f Remove unnecessary consts from Entity.h
`const`s don't mean anything in a prototype declaration unless it's a
pointer or a reference. Remove them to make the header a bit more
readable.
2022-12-11 10:52:12 -08:00
Misa 43bebecf3b Make room names own their own memory
This makes it so room names are no longer pointers to someone else's
memory, and instead to set them you use `mapclass::setroomname`. If the
string is short enough to fit in a static, no-alloc buffer, then it gets
copied there. Otherwise, a new heap allocation is made that duplicates
the string, and the new pointer is used instead.

This makes it possible for room names to contain arbitrary data whose
origin is temporary (e.g. from a script command that could be added in
the future).
2022-11-30 22:53:36 -08:00
Misa a926ce9851 Replace all free calls with VVV_free[func]
This replaces all calls to SDL_free with a new macro, VVV_free, that
nulls the pointer afterwards. This mitigates any use-after-frees and
also completely eliminates double-frees. The same is done for any
function to free specific objects such as SDL_FreeSurface, with the
VVV_freefunc macro.

No exceptions for any of these calls, even if the pointer is discarded
or zeroed afterwards anyway. Better safe than sorry.

This is a macro rather than a function that takes in a
pointer-to-pointer because such a function would have type issues that
require casting and that's just not safe.

Even though SDL_free and other SDL functions already check for NULL, the
macro has a NULL check for other functions that don't. For example,
FAudioVoice_DestroyVoice does not check for NULL.

FILESYSTEM_freeMemory has been axed in favor of VVV_free because it
functionally does the same thing except for `unsigned char*` only.
2022-11-30 22:50:08 -08:00
Misa 6e583d949b Don't set legends if out of bounds
Trinket and teleporter legends would be drawn even if they were out of
bounds. Trinket legends in particular were easy to do because you can
just place a trinket in a custom level and resize the map to not include
the room of the trinket.

Now, there are checks added so they won't be added if they are out of
bounds. This is in line with the fact that, since 2.3, if a trinket
exists outside of the map in custom levels, it won't count towards the
number of trinkets.
2022-11-30 13:42:10 -08:00
Misa de38b6b55c Unify all queries to map size to map.getwidth and map.getheight
It's becoming pretty clear that the size of the map is important enough
to be queried a lot, but each time it's something like `map.custommode ?
map.customwidth : 20` and `map.custommode ? map.customheight : 20` which
is not ideal because of copy-pasting.

Furthermore, even `map.customwidth` and `map.customheight` are just
duplicates of `cl.mapwidth` and `cl.mapheight`, which are only set in
`customlevelclass::generatecustomminimap`. This is a bit annoying if you
want to, say, add checks that depend on the width and height of the
custom map in `mapclass::initcustommapdata`, but `map.customwidth` and
`map.customheight` are out of date because `generatecustomminimap`
hasn't been called yet. And doing the ternary there requires a `#ifndef
NO_CUSTOM_LEVELS` to reference `cl.mapwidth` and `cl.mapheight` which is
just awful.

So I'm axing `map.customwidth` and `map.customheight`, and I'm axing all
the ternaries that are duplicating the source of truth in
`MapRenderData`. Instead, there will just be one function to call for
the width and height, `mapclass::getwidth` and `mapclass::getheight`,
and everyone can simply call those without needing to do ternaries or
duplication.
2022-11-30 13:35:14 -08:00
Misa d183ea6367 Clean up the code style of generatecustomminimap
The existing code was allergic to putting spaces between tokens, and had
some minor code duplication that I took the time to clean up as well.
The logic should be easier to follow now that the for-loops are no
longer duplicated for each of the map zoom levels.

I tested this by temporarily disabling map fog entirely and going
through a couple different custom levels to compare their minimap with
the existing code. These were A New Dimension and 333333 for 1x, Golden
Spiral and VVVV 4k for 2x, and VVVVVV is NP-Hard for 4x. There was no
difference in the output, not even a single pixel.
2022-11-30 13:25:28 -08:00
Misa 145355e603 Add fraZ0R to the list of contributors
fraZ0R's PR #915 was merged.
2022-11-30 11:22:50 -08:00
Misa 86d90a1296 Add color support to Windows console output, properly
This adds color support to the output of the console on Windows. Now if
you're using Windows 10 build 1511 or later (I think it's build 1511
anyway; they added more VT sequence support in later versions), you will
see colors by default. This isn't due to Windows helping in any way;
this commit has to specifically enable it with SetConsoleMode() because
by default, Windows won't enable color support unless we enable it. (Or
if it's enabled in the registry, but having to go through the registry
to enable basic shit like that is completely fucking stupid.)

I tested this in my Windows 10 virtual machine and it's completely
working.
2022-11-14 21:57:01 -08:00
Misa 40dd2571c2 Add error checks to freopen calls on Windows
I don't know if they could fail but it's still good to attempt to print
_something_ if they do end up failing.
2022-11-14 21:56:02 -08:00
Misa 0a181d8c3d Separate color being supported from being enabled
Previously, we were using `color_enabled` to mean both that the color
was supported and that it was enabled by the user (which it is enabled
by default). But this logic doesn't work well if the color check
function is called again and ends up enabling color after the user
disabled it. To fix this, just separate the two so the user controls one
`color_supported` variable and the `color_enabled` variable is separate.
Check both of them in order to print color, of course.
2022-11-14 21:51:53 -08:00
Misa 3fc23f2b2c Add -console option on Windows
This adds the `-console` command-line option (for Win32 only) so the
game can spawn an attached console window which will contain all console
output.

This is to make it easier for people to debug on Windows systems.
Otherwise, the only way to get console output would be to either compile
the application as a console app (i.e. switch the subsystem to console)
- which is undesirable for regular users as this makes it so a console
is always spawned even when unwanted - or launch the game with shell
arguments that make it so output is redirected to a file.

As a result, color checking support is factored out of vlog_init() into
its own function, even though we don't support colors on Windows.
2022-11-14 19:40:23 -08:00