Commit Graph
32 Commits
Author SHA1 Message Date
NyakoFox c2a78ba688 Fix two-frame script delay when respawning
At some point, there was some "kludge" added to the game which allowed
script boxes to immediately get triggered when you enter a room, rather
than the update order unfortunately causing scripts to trigger after two
frames.

For some reason, dying and respawning in a room does not have the same
fix, causing really bad inconsistencies for anything that needs precise
timing.

Despite the initial implementation of the kludge being somewhat careless
and breaking levels which relied on timing, this one should not break
anything, but in fact fix things, surprisingly.
2026-05-05 09:16:15 -04:00
NyakoFox 0d69dd3820 Fix audio buffer issues on some platforms
On certain ports of the game, and possibly some Linux distros, the
game's audio would crackle, or slow down with loud buzzing. This commit
attempts to fix that, by enabling `FAUDIO_1024_QUANTUM`, which should
increase the internal FAudio buffer size.
2026-04-30 17:06:30 -04:00
NyakoFox a5e353a0fa Solve render recaching issues
Past solutions were just "recache the screen textures under these known
circumstances" where whenever things OUTSIDE of those known
circumstances happened, the issue would reoccur.

I recently learned about an SDL event, `SDL_RENDER_TARGETS_RESET`,
which is for this exact problem. I ripped out all of the other places
`Screen::recacheTextures()` was called, and just slotted it in there,
and it worked perfectly.

...well, the old behavior worked perfectly; but the old behavior was
flawed as well, because it only checked for "ingame_titlemode" (if
you're in the main menu during gameplay) and forgot to check for the
map screen... plus, it didn't ever regenerate the minimap in custom
levels, which is another "persistant" render target.

Hopefully, this is the last time we'll ever have to think about this
one. I'm certainly sick of it.

This should 100% be backported into 2.4, as the bug occurs there as
well.
2026-04-12 09:09:27 -04:00
NyakoFox 994b3602e5 Clamp editor mouse coordinates
The editor has never clamped mouse bounds, and instead relied on SDL to
return clamped mouse coordinates. Unfortunately, in #1140, this detail
was missed, and the behavior changed, allowing the cursor to leave the
bounds of the screen.

This isn't much of a problem most of the time because anything that
uses position as array indeces has bounds checks. Unfortunately,
placing entities outside of a room's bounds leads to entities you can't
ever touch ever again without modifying the level externally. This is
the worst with checkpoints, as pressing Enter in a room will now take
you to the out-of-bounds checkpoint, causing you to immediately wrap to
another room.

This fix should be backported to 2.4, as the issue can lead to level
files which need manual editing to fix!
2026-04-12 09:08:48 -04:00
NyakoFox 9bcade9776 Add statedelay argument to gamestate command
The `gamestate` command has an argument to set the gamestate, but the
statedelay was always set to 0, despite the statedelay being an
important part of the gamestate system. This commit adds an optional
argument to set the statedelay.
2026-02-25 20:32:55 -05:00
NyakoFox deeed00c95 Make sure special roomnames have enough text
There's another crash, which is indexing out of bounds if there's
not enough text. This adds simple bounds checks for them.
2026-02-16 11:42:43 -05:00
NyakoFox 11b660abb0 Fix empty roomnames potentially crashing the game
TinyXML2 seems to sometimes return NULL when an element has either no
text or whitespace, instead of returning an empty string. Why it does
this, I'm not sure, but I have recently learned from Dav999 that it
will crash the game. Great.
2026-02-16 11:42:43 -05:00
NyakoFox 9256100cb9 Add ifversion
This command adds `ifversion`, as both a simplified and internal
command.

The system works like so:

- `ifversion(2.6,script2)` -> If you're on 2.6.0 or greater
- `ifversion(2.6.0,script2)` -> Same as above
- `ifversion(2.6.3,script2)` -> You're using 2.6.3 or greater
- `ifversion(2.5,script2)` -> You're using 2.5 or greater
- `ifversion(2,script2)` -> You're using 2.0.0 or greater (yep...)

`ReleaseVersion.h` has a few new defines to make this possible, being
`MAJOR_VERSION`, `MINOR_VERSION` and `PATCH_VERSION`. With the help of
a few macros, `RELEASE_VERSION` is now constructed using those.
2025-11-20 21:32:57 -05:00
NyakoFox 552f1b21c2 Add warning for playef command and lower sound IDs
Ethan mentioned how case sensitivity could be a concern -- this commit
lowers the filename to create the sound ID, and also strips spaces.
Script arguments are always lowered, and the spaces get removed, so the
IDs are modified to match this behavior. If someone types in the
command `playef(horses REALLY COOL)`, the argument will get converted
to `horsesreallycool`. Likewise, the filename "horses REALLY COOL.wav"
will get converted to `horsesreallycool`. To the level creator, they
can just enter the same name twice and their sound will play.

This commit also adds a warning for the `playef` script command. While
most creators do not open the console, some warning somewhere is
better than nothing. I do think we should eventually have some sort
of system where it's easier to view level warnings, but for now this
should be sufficient.
2025-10-13 16:36:43 -04:00
NyakoFoxandFussmatte f73fddea72 Load an unlimited amount of sounds
This commit allows the usage of new "extra" sounds in levels for use in
custom level scripting. Including `squid.ogg` inside of your sounds
folder will now allow for the usage of the command `playef(squid)`.
Additionally, you can play built-in sounds by name this way too,
instead of having to memorize the id (`playef(rescue)`).

However, you CANNOT play extra sounds through using their numerical
IDs, since those can shift around depending on the filesystem (or if
someone adds a new sound). Playing an extra sound by ID will NOT play
the sound.

This is another attempt at #902, being loosely based off of that PR.

Co-authored-by: Fussmatte <doormattion@gmail.com>
2025-10-13 16:36:43 -04:00
NyakoFox 0e31253876 Fix mouse coordinates being wrong on HiDPI displays
When writing the initial stretch mode code, the existence of HiDPI
displays completely slipped my mind -- or at least I didn't realize
that they'd be a problem.

We implement scaling modes ourselves, so transforming the mouse
coordinates to our 320x240 viewport is done manually. Unfortunately,
that code did not take into account HiDPI scaling whatsoever, meaning
that all of the math which assumes the window size and the renderer
size is wrong.

To fix this, we use `SDL_GetWindowSizeInPixels` and `SDL_GetWindow` to
find the scaling factor, and then apply that to the mouse coordinates
to get the mouse coordinates in the pixel-space instead, and then we do
all of our normal logic after.

Due to our usage of `SDL_GetWindowSizeInPixels`, this bumps the minimum
SDL version to 2.26.0.

Closes #1235.
2025-05-03 15:15:03 -04:00
NyakoFox 719ed9a67b Fix uncommon division-by-zero
Every now and then, the game crashes for me because of a division by
zero, due to the rect returned by `Graphics::get_stretch_info`. I don't
fully know how the width and height get set to 0, but this should
protect against it.

As I always use integer scaling, my guess is that
`Screen::GetScreenSize` (which later calls `SDL_GetRendererOutputSize`)
returns 0 sometimes, and the code trusts that -- but I know that
windows and things can be finicky, so the clamp is probably a good
idea.
2025-05-01 18:33:02 -04:00
NyakoFox 6810c5fa8c Fix #1223 (analogue mode segfault)
When `SDL_RenderReadPixels` fails for some reason, the game tries to
free the temporary source surface it creates. Unfortunately, it never
sets it to `NULL` after, so the next time the game tries to render the
filter, it'll try to work with a memory region that was already freed.

To fix this, I just replaced `SDL_FreeSurface(*src);` with
`VVV_freefunc(SDL_FreeSurface, *src);` which is a helper macro which
sets the pointer to NULL after freeing.

Now, there's a new issue -- since the temporary buffer is now NULL,
next frame we'll try to remake it! So I've introduced a static bool
which disables the filter entirely if `SDL_RenderReadPixels` fails.
Without this, it'd create and destroy a surface every frame, which
could lead to slowdown. (Not as slow as the filter when it DOES work,
but still...)

I also added a line which frees the second temporary surface... it's
weird that was missing in the first place, but I think reimplementing
analogue mode was one of the last things I did for the renderer
rewrite anyways.

Resolves #1223.
2025-04-29 20:54:11 -04:00
NyakoFox 2ee5aef3fa Fix the campaign trinket count missing from stats
PR #1226 tweaks both the crew and the stats screens. When there's no
trinkets in the level, it removes the trinket count from the STATS
screen in the menu. This, however, is missing a `custommode` check,
meaning that the main game is ALSO missing the trinket count.

This PR fixes that oversight.
2025-04-29 20:53:05 -04:00
NyakoFox c1eaeca9f6 Fix "invalid" activity zones not spawning
There's a problem in #1224 where it breaks spawning custom activity
zones. After a bit of confusion after this was reported, I realized
that I removed the fallback from `getcrewman` and changed the return
value to `-1` if the entity isn't found, to avoid returning the wrong
entity (entity 0, aka probably the player).

Unfortunately, it seems like a ton of levels (including my older ones)
rely on this behavior.

Creating custom activity zones is a long process which uses a bunch of
unintended behaviour, which includes targeting a crewmate with color
35. With the change I mentioned earlier, the `getcrewman` function
would return `-1` instead, which was out of bounds of the entity array,
so the game avoided spawning the activity zone at all. The prior
behaviour of falling back to entity 0 (most likely the player) would
spawn the activity zone around the player instead.

Nowadays, I try to spawn a crewmate with color 35 anyways so I can
control where the box spawns (instead of on the player always), however
most people don't (and haven't) so reverting this change seems best for
now.

If we wanted to reintroduce the `-1` fallback in the future, things
that call `getcrewman` would have to check for `-1` and use `0`
instead, but that would require a lot more testing and studying where
it's used, and I'd rather squash this bug quickly and worry about
cleanliness later.
2025-04-28 08:04:51 -04:00
NyakoFox 0e9c4f98a6 Replace colour IDs with an enum
Entity colors are just integers. Their colour ID gets passed through a
big switch, returning different RGB values depending on the colour ID
(and can also get affected by randomness or other game state.)

But because of this, there's a bunch of random numbers floating around,
with no actual description on what they are other than comments which,
while most of the time are accurate, only exist in the switch.

To fix this, this commit adds a new enum which labels every colour.
While we can't use it as a type (as we need to allow colours outside of
what are defined, in case people want a "pure white", and scripting can
set any colour ID they want), colours have to stay as `int`.
2025-04-21 20:09:57 -04:00
NyakoFox b8bcdf39df Move saving outside of MetaData (oops!) 2025-04-12 16:43:50 -04:00
NyakoFox 6809aa0135 Attempt to fix external playtesting not respecting colour 2025-04-12 16:43:50 -04:00
NyakoFox 5816e6f51a Add player colour as a level property
This PR adds a new XML property for the player's colour. It is 0 by
default, but you can change it to any colour ID. For example, making
the player use the trinket color is `<PlayerColour>3</PlayerColour>`.

This is mostly a quality-of-life addition, as the player's colour is
always 0 unless changed by scripts. A lot of levels which use different
player colours use an intro script which both changes the player's
colour and sets their respawn colour, which works great for finished,
completed levels, but makes playtesting a little more annoying as they
will spawn in as the wrong colour. Adding a level property for the
default player colour fixes this annoyance.

Additionally, this changes the behavior of `restoreplayercolour`. This
command used to set the player's colour to 0 (ignoring the respawn
colour, so when Viridian would die, they would revert to the respawn
colour). Now, it sets both Viridian's colour AND the respawn colour to
what was present in the level file. This way, you can temporarily
change the player colour using the script commands, and then use
`restoreplayercolour` to revert back to what the player colour
normally is.

The start point colour has also changed, to show the player colour
instead of always colour 0.

Like most changes like this, a way to change this in-editor does not
yet exist, but is planned for the future.
2025-04-12 16:43:50 -04:00
NyakoFox d419c6ed5b Remove extra unnecessary palettes
This merges the colors from other palettes into the general entity
palette function.
2025-02-18 14:14:16 -05:00
NyakoFox d4e472db1b Make 0-length gravity lines invisible again 2024-11-17 11:46:24 -05:00
NyakoFox 6174d62f6f position(force) -> position(absolute) 2024-11-07 09:53:45 -08:00
NyakoFox 98a0931225 Add textoutline(on/off/default)
This commit adds a new scripting command for textbox visual control,
where you're able to force the transparent textbox's outline on or off.
2024-11-07 09:53:45 -08:00
NyakoFox 45e60fa69c Add position(force)
This argument forces the textbox position, meaning it won't be moved
to be inside of the bounds of the screen (nor have the 10 pixel padding
on each side.)
2024-11-07 09:53:45 -08:00
NyakoFox c2642dbdef Add more checks for checkpoint saving 2024-10-02 00:37:25 -07:00
NyakoFox c04c6bc552 Abstract checkpoint saving to its own function
This also makes the save failed textbox not appear in special modes,
and allows custom levels to quicksave from checkpoints as well.
2024-09-27 17:14:57 -07:00
NyakoFox 41d5e688e9 Fix menu option, show textbox when save failed
In my last commit, I accidentally inverted whether the description says
ON or OFF.
2024-09-27 17:14:57 -07:00
NyakoFox e58fd606cf add menu option for checkpoint saving 2024-09-27 17:14:57 -07:00
NyakoFox 8aec83daec Checkpoint autosaving 2024-09-27 17:14:57 -07:00
NyakoFox d8b2b3542a Add enemy speed as a room property
Currently, you can change platform speed, but not enemy speed, which is
always hardcoded to be 4. This commit fixes that, by adding the
"enemyv" property, which is an offset to the speed of 4. Since it
defaults to 0, older levels are not broken by this change.
2024-09-23 18:56:41 -07:00
NyakoFox 3361e71036 Add MSVC version check 2024-03-29 20:31:00 -07:00
NyakoFox 4bba26280f Add /utf-8 to MVSC 2024-03-29 20:31:00 -07:00