1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-18 08:59:42 +01:00
Commit graph

2257 commits

Author SHA1 Message Date
Misa
1e375f9ecf Un-export FILESYSTEM_mount()
This function is never used outside of FileSystemUtils.cpp; there is no
reason to export it.
2021-04-05 16:39:37 -04:00
Ethan Lee
5060b4dfe3 Only do focus fullscreen toggling on X11.
I haven't been able to reproduce this old thing on any setup I have. The patch
from 2013 was originally for X11, and Wayland's fullscreen doesn't allow for
this sort of thing, so let's start scoping this down for eventual removal when
X11 is finally out of our minds forever.
2021-04-05 11:07:32 -04:00
Misa
510ec07021 Re-fix resumemusic/musicfadein once again
So it looks like facb079b35 (PR #316) had
a few issues.

The SDL performance counter doesn't really work that well. Testing
reveals that unfocusing and focusing the game again results in
the resumemusic() script command resuming the track at the wrong time.
Even when not unfocusing the game at all, stopping a track and resuming
it resumes it at the wrong time. (Only disabling the unfocus pause fixes
this.)

Furthermore, there's also the fact that the SDL performance counter
keeps incrementing when the game is paused under GDB. So... yeah.

Instead of dealing with the SDL performance counter, I'm just going to
pause and resume the music directly (so the stopmusic() script command
just pauses the music instead). As a result, we no longer can keep
constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or
unfocused, so I've moved those calls to happen directly when the
relevant SDL events are received (the constant calls were originally in
VCE, and whoever added them (I'm pretty sure it was Leo) was not the
sharpest tool in the shed...).

And we are going to switch over to using our own fade system instead of
the SDL mixer fade system. In fact, we were already using our own fade
system for fadeins after collecting a trinket or a custom level
crewmate, but we were still using the mixer system for the rest. This is
an inconsistency that I am glad to correct, so we're also doing our own
fadeouts now.

There is, however, an issue with the fade system where the length it
goes for is inaccurate, because it's based on a volume-per-frame second
calculation that gets truncated. But that's an issue to fix later - at
least what I'm doing right now makes resumemusic() and musicfadein()
work better than before.
2021-04-02 16:13:54 -04:00
Misa
6d3a73c540 Add pause(), pauseef(), and resumeef() to musicclass
musicclass already had a resume() function for music.

These are just wrappers around the appropriate SDL_mixer functions, to
avoid direct function calls to the mixer API. So if we ever need to do
something with all callers of pausing and resuming in the future, or we
switch to a different audio backend, the work is already done for us.

Also it just looks cleaner to be calling our musicclass function instead
of doing a direct API call to the mixer.
2021-04-02 16:13:54 -04:00
Misa
92b3c0b413 Factor fade amount calculation to separate function
This makes it so to reuse this code, we don't have to copy-paste it.

Additionally, I added a check for the milliseconds being 0, to avoid a
division by zero. Logically and mathematically, if the fade amount is 0
milliseconds, then that means the fade should happen instantly -
however, dividing by zero is undefined (both in math and in C/C++), so
this check needs to be added.
2021-04-02 16:13:54 -04:00
Misa
f6ea05f521 Factor timestep calculation out to Game function
This is so we can grab the game's timestep anywhere else in the codebase
without copy-pasting it.
2021-04-02 16:13:54 -04:00
Misa
f8f6f3b96e Add option to re-enable 1-frame input delay
This is an option for speedrunners whose muscle memory is precisely
trained and used to the 1-frame input delay that existed in 2.2 and
below. It is located in Game Options -> Advanced Options, and is off by
default.

To re-add the 1-frame input delay, we simply move the key.Poll() to the
start of the frame, instead of before an input function gets ran -
undoing what #535 did.

There is a frame ordering-sensitive issue here, where toggling
game.inputdelay at the wrong time could cause double-polling. However,
we only toggle it in an input function, which regardless is always
guaranteed to be ran after key.Poll() (it either happened at the start
of the frame or just before the input function got ran), so this is not
an issue. But, in case we ever need to toggle this variable in the
future, we can just use the defer callbacks system to defer the toggle
to the end of the frame - also added by #535.

Added at the request of Habeechee on the VVVVVV speedrunning Discord
server.
2021-04-02 11:18:32 -04:00
Misa
4b3409e2e8 Remove advancetext check from teleporter prompt logic
This fixes being unable to use teleporters while the "- Press ACTION to
advance text -" prompt is up, which is used to perform credits warp.

In 2.2 and 2.0, this advancetext check was only in gamerender() for
rendering the "- Press ENTER to Teleport -" prompt and didn't affect any
logic. In 2.3, I moved the check (and the rest of the conditional it was
in) to gamelogic() - same as the activity zone prompt conditionals - so
if you gained control while being in a prompt zone, the prompt wouldn't
suddenly appear[1].

As a side effect, this ended up aligning rendering and logic together,
so if you couldn't see the teleporter prompt, you weren't able to
teleport - whereas in 2.2 and 2.0, you could still use the teleporter
even though the prompt wasn't up.

So by removing the advancetext check, you are now able to use the
teleporter again, AND the "- Press ENTER to Teleport -" prompt will also
show up as well.

Habeechee reported this regression on the VVVVVV speedrunning Discord
server.

[1]: f07a8d2143, PR #421
2021-04-02 11:16:42 -04:00
Misa
cd2f5ccde9 Add period to glitchrunner mode description text
This is to be consistent with the other options in the Advanced Options
menu.
2021-04-02 11:15:53 -04:00
Misa
91bc438d79 Fix funcs not being reassigned if delta func is last func in list
One of the solutions to the quit signal unfocus pause regression is to
add a no-op delta func to the unfocused func table. However, this
results in the game being stuck in unfocus pause forever, because when
it reaches the end of a list on a delta func, it won't reassign the
active functions - only when the end of a list is a fixed func will it
do so. A workaround is to then add a no-op fixed func afterwards, but
that's inelegant.

The solution in the end to the quit signal regression is to not bother
with adding a delta func, so the game as of right now actually never has
a delta func at the end of a list, and probably never will - but this is
one piece of technical debt I don't want to leave laying around. In case
we're ever going to put a delta function at the end of a list, I've made
it so that delta functions will now reassign the list of active funcs if
they happen to be at the end of the func list.
2021-04-02 11:15:18 -04:00
Misa
03a0b1feb2 Call VVV_exit() when SDL_QUIT is received
This fixes a regression introduced by #535 where a quit signal (e.g.
Ctrl-C) sent to the window while the game was in unfocus pause wouldn't
close the game.

One problem was that key.quitProgram would only be checked when control
flow switched back to the outer loop in main(), which would only happen
when the loop order state machine switched to a delta function. As the
unfocused func table didn't have any delta functions, this means
key.quitProgram would never be checked.

So a naïve solution to this would just be to add a no-op delta func
entry to the unfocused func table. However, we then run into a separate
issue where a delta function at the end of a func list never reassigns
the active funcs, causing the game to be stuck in the unfocus pause
forever. Active func reassignment only happens after fixed funcs. So
then a naïve solution after that would be to simply add a no-op fixed
func entry after that. And indeed, that would fix the whole issue.

However, I want to do things the right way. And this does not seem like
the right way. Even putting aside the separate last-func-being-delta
issue, it mandates that every func list needs a delta function. Which
seems quite unnecessary to me.

Another solution I considered was copy-pasting the key.quitProgram check
to the inner loops, or adding some sort of signal propagation to
the inner loops - implemented by copy-pasting checks after each loop -
so we didn't need to copy-paste key.quitProgram... but that seems really
messy, too.

So, I realized that we could throw away key.quitProgram, and simply call
VVV_exit() directly when we receive an SDL_QUIT event. This fixes the
issue, this removes an unnecessary middleman variable, and it's pretty
cleanly and simply the right thing to do.
2021-04-02 11:14:24 -04:00
Terry Cavanagh
4ebbfe476a
Merge pull request #588 from InfoTeddy/general-bug-fixes
Fix tile of disappearing platforms during final stretch
2021-04-02 17:51:30 +10:30
Terry Cavanagh
886a57ab83
Merge pull request #667 from InfoTeddy/general-improvements
Add text outlines to textboxless textboxes and gravitron text
2021-04-02 17:49:20 +10:30
Terry Cavanagh
9fbc3804c8
Merge pull request #668 from InfoTeddy/general-bug-fixes-2
Fix oversight with unfocus pause screen in Flip Mode
2021-04-02 17:47:27 +10:30
Misa
ef091de23e Outline all gravitron text
This includes all text from the Gravitron and Super Gravitron.

This is to make the text more readable if they are placed in weird
situations - for example, in custom levels, where the background these
texts get placed on could be anything (custom level makers are crazy!).
2021-03-30 23:57:00 -07:00
Misa
6538d1e5dd Add Graphics::bigrprint()
Same as bigbprint(), we duplicate some of the calculations because it's
better than duplicating another text printing function.
2021-03-30 23:57:00 -07:00
Misa
f7173027ce Add Graphics::bigbprint()
It's just like bigprint() except it duplicates some of the calculations
because I didn't want to make a bigprintoff() function which would
duplicate even more code. I'm beginning to think these text printing
functions are completely horrible to work with...
2021-03-30 23:57:00 -07:00
Misa
827b3e430b Outline textboxless textboxes
In case they get drawn against a non-contrasting background, it's still
useful to keep them readable by outlining them. This could happen if
someone were to use the Game Complete gamestate sequence in a custom
level (or presses R during Game Complete).
2021-03-30 23:57:00 -07:00
Misa
0c72260c5d Fix oversight with unfocus pause screen in Flip Mode
Flip Mode flips all the unfocus pause screen text upside-down, to make
it read in reverse order. This looks kind of strange to me, and I don't
think it was intended. So I'm flipping the text again so it's the right
way up in Flip Mode.
2021-03-30 23:55:42 -07:00
Misa
f9e76d9dc0 Fix tile of disappearing platforms during final stretch
During the final stretch, after Viridian turns off the Dimensional
Stability Generator, the map goes all psychedelic and changes colors
every 40 frames. Entities change their colors too, including conveyors,
moving platforms, and disappearing platforms.

But play around with the disappearing platforms for a bit and you'll
notice they seem a bit glitchy. If you run on them at the right time,
the tile they use while disappearing seems to abruptly change whenever
the color of the room changes. If there's a color change while they're
reappearing (when you die and respawn in the same room as them), they'll
have the wrong tile and look like a conveyor. And even if you've never
interacted with them at all, dying and respawning in the same room as
them will change their tile to something wrong and also look like a
conveyor.

So, what's the problem? Well, first off, the tile of every untouched
disappearing platform changing into a conveyor after you die and respawn
in the same room is caused by a block of code in gamelogic() that gets
run on each entity whenever you die. This block of code is the exact
same block of code that gets ran on a disappearing platform if it's in
the middle of disappearing.

As a quick primer, every entity in the game has a state, which is just a
number. You can view each entity's state in
entityclass::updateentities().

State 0 of disappearing platforms is doing nothing, and they start with
an onentity of 1, which means they turn to state 1 when they get
touched. State 1 moves to state 2. State 2 does some decrementing, then
moves to state 3 and sets the onentity to 4. State 3 also does nothing.
After being touched, state 4 makes the platform reappear and move to
state 5, but state 5 does the actual reappearing; state 5 then sets the
state back to 0 and onentity back to 1.

So, back to the copy-pasted block of code. The block of code was
originally intended to fast-forward disappearing platforms if they were
in the middle of disappearing, so the player respawn code would properly
respawn the disappearing platform, instead of leaving it disappeared.
What it does is keep updating the entity, while the state of the entity
is 2, until it is no longer in state 2, then sets it to state 4.

Crucially, the original block of code only ran if the disappearing
platform was in state 2. But the other block of code, which was
copy-pasted with slight modifications, runs on ALL disappearing
platforms in final stretch, regardless of if they are in state 2 or not.

Thus, all untouched platforms will be set to state 4, and state 4 will
do the animation of the platform reappearing, which is invalid given
that the platform never disappeared in the first place. So that's why
dying and respawning in the same room as some disappearing platforms
during final stretch will change their tiles to be conveyors.

It seems to me that doing anything with death is wrong, here. The root
cause is that map.changefinalcol() "resets" the tile of every
disappearing platform, which is a function that gets called on every
color change. The color change has nothing to do with dying, so why
fiddle with the death code?

Thus, I've deleted that entire block of code.

What I've done to fix the issue is to make it so the tile of
disappearing platforms aren't manually controlled. You see, unlike other
entities in the game, the tile of disappearing platforms gets manually
modified whenever it disappears or reappears. Other entities use the
tile as a base and store their tile offset in the separate walkingframe
attribute, which will be added to the tile attribute to produce the
drawframe, which is the final thing that gets rendered - but for
disappearing platforms, their tile gets directly incremented or
decremented whenever they disappear or reappear, so when
map.changefinalcol() gets ran to update the tile of every platform and
conveyor, it basically discards the tile offset that was manually added
in.

Instead, what I've done is make it so disappearing platforms now use
walkingframe, and thus their final drawframe will be their tile plus
their walkingframe. Whenever map.changefinalcol() gets called, it is now
free to modify the tile of disappearing platforms accordingly - after
all, the tile offset is now stored in walkingframe, so no weird
glitchiness can happen there.
2021-03-30 23:55:34 -07:00
Misa
a405635cb2 Replace other usage of PHYSFS_getBaseDir() with SDL_GetBasePath()
Ethan, you forgot this other one.

I do have to rejiggle the control flow of the function a bit, so it
doesn't leak memory upon failure. (Although the SDL message box leaks
memory anyway because of X11 so... whatever.) Also, there's a NULL check
for if SDL_GetBasePath() fails now.
2021-03-31 02:51:52 -04:00
Ethan Lee
051fe9eba9 Skip the icon on Apple targets, it also has the icns 2021-03-31 02:50:38 -04:00
Ethan Lee
b99abaf1d5 Put Misa at the top of GitHub Friends 2021-03-31 02:43:15 -04:00
Ethan Lee
4c4f8de0d5 Fix leaking GetBasePath result 2021-03-31 02:43:15 -04:00
Misa
8cf79aaf72 Fix memory leak when pasting text
According to SDL documentation[1], the returned pointer needs to be
freed. A glance at the source code confirms that the function allocates,
and also Valgrind complains about it.

Also if it couldn't allocate, the game no longer segfaults (std::strings
do not check if the pointer is non-NULL for operator+=).

[1]: https://wiki.libsdl.org/SDL_GetClipboardText
2021-03-31 02:29:36 -04:00
Ethan Lee
9d2ebbc982
Re-sync CONTRIBUTORS and GitHub Friends lists 2021-03-30 23:08:25 -04:00
Ethan Lee
b87f6e487a Use SDL_GetBashPath instead of PHYSFS_getBaseDir, latter is made of fail on macOS 2021-03-30 12:18:09 -04:00
Ethan Lee
076a870ba2 VS2010 buildfix 2021-03-30 11:51:42 -04:00
Misa
367e77fb59 Fix targets (question marks) not showing up on minimap
Followup to #635: I had misread the original '==0' comparisons as being
truthy comparisons instead of being falsy comparisons. Whoops.
2021-03-26 00:02:26 -04:00
Misa
892e7c93fc Fix background not changing when pressing Esc on main menu
Followup to #664: Pressing "quit game" instead of using Esc changes the
background, but pressing Esc doesn't. Whoops.
2021-03-25 23:32:59 -04:00
Misa
100d986431 Remove mainmenu from Game
Since mainmenu is only ever used in Input.cpp, I might as well make it
clearer by moving it into a static global variable in Input.cpp. (The
same applies to fadetolab/fadetomenu, but I didn't think much about
those at the time... that'll be a refactor for later.)
2021-03-25 23:32:39 -04:00
Misa
be10487c5c Set fademode to temp 0 when going to in-game options
While I've decoupled fademode from gamemode starting, being faded out on
the title screen results in a black screen and you being unable to make
any input. So we'll need to store the current fademode in a temporary
variable when going to in-game options, then put it back when we return
to the pause menu. Yes, you can turn on glitchrunner mode during the
in-game options, and then immediately return to the pause menu to
instantly go back to the title screen; this is intended.

Due to frame ordering, putting the fademode back needs to be deferred to
the end of the frame to prevent a 1-frame flicker.

It's actually sufficient enough to do this temporary fademode storage to
fix the whole thing, but I also decided to decouple fademode and
gamemode starting just to be sure.
2021-03-25 23:32:39 -04:00
Misa
82dfa0b86c Decouple fademode from starting gamemode
Assuming glitchrunner mode is off, if you open the pause menu while
fully faded-out and then go to Graphic Options or Game Options, then the
'mode' that you selected previously will kick in again and you'll be
suddenly warped back.

So if you previously started a new game in the main game (mode 0, also
the selected mode if you do this from command-line playtesting), and
then open the pause menu and go to in-game options, then you'll suddenly
go back to starting a new game again. If you had started a custom level,
doing this will warp you back to the start of the level again.

The problem is simple - when the title screen is fully faded out, it
calls startgamemode(). So the solution is simple as well - just decouple
the fademode from calling startgamemode(), and use a different variable
to know when to actually call startgamemode().
2021-03-25 23:32:39 -04:00
Misa
cd0c9ccb31 De-duplicate setting mainmenu and fademode
This makes my work easier later.
2021-03-25 23:32:39 -04:00
Misa
6a6c09f69d Fix 1-frame flicker entering room with warp lines and entity on edge
Custom levels can have warp lines. If you have a warp line and a warping
background in the same room, the warp line takes precedence over the
warp background.

However, whenever you enter a room with a warp line and warp background,
any entities on the warping edges will be drawn with screenwrapping for
one frame, even though they never wrapped at all.

This is due to frame ordering: when the warp line gets created,
obj.customwarpmode gets set to true. Then when the screen edges and
warping logic gets ran, the very first thing that gets checked is this
exact variable, and map.warpx/map.warpy get set appropriately - so
there's no way the entity could legitimately screenwrap.

However, that happens in gamelogic(). gamelogic() is also the one
responsible for creating entities upon room load, but that happens after
the obj.customwarpmode check - so when the game gets around to rendering
in gamerender(), it sees that map.warpx or map.warpy is on, and draws
the screenwrapping, even though map.warpx/map.warpy aren't really on at
all. Only when gamelogic() is called in the frame later do map.warpx and
map.warpy finally get set to false.

To fix this, just set map.warpx and map.warpy to false when creating
warp lines.
2021-03-25 22:29:40 -04:00
Misa
8fd7210d37 De-duplicate warp line creation in createentity()
Copy-pasted code begone. This makes it much clearer what the difference
between all the warp line entities in this function are.
2021-03-25 22:29:40 -04:00
Misa
c8f4c37c88 Add bounds check for color of createentity number 55
I just spotted this one - if vy isn't bounds-checked, this causes bogus
input from the createentity() script command to commit Undefined
Behavior. Should've spotted this one when I was adding bounds checks to
the rest of createentity() earlier, but at least it's fixed now.
2021-03-25 21:43:51 -04:00
Terry Cavanagh
6490af8783
Merge pull request #680 from InfoTeddy/pr-template-update
Remove 2.3 example from pull request template
2021-03-25 11:57:34 +10:30
Misa
ab59ec35fb Remove 2.3 example from pull request template
Mentioning 2.3 here is going to be anachronistic when 2.4 exists. And
there's not really any point in bumping this version number every time
the game's version number is bumped as well. So it's best to just remove
it.
2021-03-24 14:36:33 -07:00
Misa
423c79b572 Add bounds checks to room explored getter and setter
This means you can no longer cause Undefined Behavior by exploring a
room that is outside the array of explored room statuses.
2021-03-24 15:55:34 -04:00
Misa
c5e999c1d5 Refactor explored rooms to use setters and getters
This makes it easier to add bounds checks to all accesses of
map.explored. Also, all manually-written existing bounds checks have
been removed, because they're going to go into the new getters and
setters.

The getter is mapclass::isexplored() and the setter is
mapclass::setexplored().
2021-03-24 15:55:34 -04:00
Misa
b340a6ccc4 Add bounds checks to room propety getters and setters
It is no longer possible to cause Undefined Behavior via accessing
out-of-bounds room properties.

What happens instead is - if you attempt to fetch an out-of-bounds room
property, you get a "blank" room property that just has all of the
defaults, plus its tileset is 1 because all tilesets that are nonzero
use tiles2.png, and it closely emulates the previous behavior where it
was some bogus value but definitely not zero. Its Direct Mode is also 1,
because the tiles contained within it are just mishmashed repeats of
existing tiles on the map, and we shouldn't autotile that.

The roomname also gets cleared in case the user attempts to set the room
name of an out-of-bounds room property.

If you attempt to set the property of an out-of-bounds room property,
then nothing happens.
2021-03-24 15:55:34 -04:00
Misa
945d5f244a Refactor room properties to use setter and getter funcs
This replaces all raw ed.level accesses with new setter and getter
funcs, which makes it easier to add bounds checks later. And I've also
removed all the manually-written bounds checks, since they will go into
the new getter and setter.

To get the room properties of a specific room, you use
editorclass::getroomprop(), which returns a pointer to the room
properties - then you just read off of that pointer. To set a room
property, you use editorclass::setroom<PROP>(), where <PROP> is the name
of the property. These are maintained using X macros to avoid
copy-pasting. editorclass::getroompropidx() is a helper function and
shouldn't be used directly.
2021-03-24 15:55:34 -04:00
Misa
cfd5be1bc5 Add bounds checks to tile setter and getters
This removes all traces of Undefined Behavior from getting and placing
tiles.

This mimics the previous behavior (2.2 and below) as reasonably as
possible. `vmult` was previously a vector, there was a bunch of unused
space directly after the end of the usable space of the vector, which
was all filled with zeroes. The same goes for `contents`, having
previously been a vector, and so having a bunch of zeroes immediately
following the end of the in-bounds space. That's why both are 0 if you
index them out of bounds.
2021-03-24 15:55:34 -04:00
Misa
344c93e754 Refactor tiles to use setter and getter functions
This makes it easier to add bounds checks to all accesses of
ed.contents.

To do this, I've added editorclass::gettile(), editorclass::settile(),
and editorclass::getabstile() (with a helper function of
editorclass::gettileidx() that really shouldn't be used directly), and
replaced all raw accesses of ed.contents with those functions
appropriately.

This also makes the code more readable, as a side effect.
2021-03-24 15:55:34 -04:00
Misa
ccdb0c9148 Fix bounds checks in drawentity()
The existing bounds checks were correct sometimes but other times were
not.

The bounds check for 2x2 and 2x1 sprites only covered the top-left
sprite drawn; the other sprites could still be out of bounds. But if the
top-left sprite was out of bounds, then none of the other sprites
wouldn't be drawn - although it ought to be that the other sprites still
get attempted to be drawn. So I've updated the bounds checks
accordingly, and now an out of bounds top-left sprite won't prevent the
drawing of the rest of the sprites.

Similarly, if the sprite of a Gravitron square was out of bounds, that
would prevent its indicators from being drawn. But the indicators
weren't being bounds-checked either (2.3 lets you have less than 1200
tiles in a given tilesheet). So the bounds check has been moved to only
cover the drawframe and the indicator indexes accordingly, and an out of
bounds sprite won't prevent attempting to draw the indicators.
2021-03-24 15:42:28 -04:00
Misa
4e52dccdae Initialize temporary edentity when loading levels
It is possible for any of the QueryIntAttribute()s to fail, most
commonly if the attributes don't exist. If that happens, then that part
of the temporary edentity won't be initialized, and we'll end up having
a partially-uninitialized edentity - then doing much of anything with it
will result in undefined behavior.

To fix this, just initialize the temporary edentity.
2021-03-24 15:26:09 -04:00
Misa
69b0f0b650 Add missing pText NULL checks
If an XML tag doesn't contain anything inside, pText will be NULL. If
that happens without being checked, then NULL will be passed to
SDL_strcmp(). SDL_strcmp() will either call libc strcmp() or use its own
implementation; both implementations will still dereference the NULL
without checking it.

This is undefined behavior, so I'm fixing it. The solution is to do what
is done with all other XML parsing functions, and to make sure pText
gets set to a safe empty string (which is just a pointer to a null
terminator) if it happens to be NULL.
2021-03-24 15:26:09 -04:00
Misa
0da9b5069a Capitalize "OFF" when invincibility is off
All other settings capitalize "ON" and "OFF", so this one should, too.
2021-03-21 20:53:40 -04:00
Misa
9ab61af1da Add period to text outline description
This is to be consistent with all other option descriptions, which all
end in a period as well.
2021-03-21 20:53:40 -04:00