In the main game, if you press R during the trinket collection prompt
after collecting a trinket, AND you have never entered Comms Relay, and
you respawn in a different room, the trinket collection gamestate will
be interrupted, but you will still be left with the advance text prompt,
cutscene bars, and muted music.
The previous workaround to fix the music would be to mute and then
unmute the game, but due to the new music changes, this workaround
(which in and of itself is a bug) no longer works. Instead, the music
would have to be restarted by going into another zone on the map.
Having an advance text prompt outside of a cutscene results in the
player being unable to flip, but they can still move around left and
right.
Speedrunners previously used the no-Comms-Relay interrupting behavior to
skip certain trinket collection prompts entirely with a frame-perfect R
press, so I can't patch that out. Having an advance text prompt outside
of a cutscene is (ab)used in custom levels to intentionally prevent the
player from flipping, and furthermore, it's also used in credits warp
runs of the main game to increment the gamestate; so I cannot patch that
out. The ability to press R everywhere even during cutscenes was added
for good reason - to make it less likely that a softlock can happen - so
I don't want to revert it.
But I still think this is worth fixing because previously, the
punishment for missing the frame-perfect window late was simply not
skipping the trinket prompt (since the R-press would be ignored), but
now the punishment is basically having to reset because of the advance
text prompt.
I would usually handle this in gamestate 0, but awful custom levels
might want to intentionally interrupt the gamestate to do, I don't know,
something. No level does that so far, but I'd like to do the least
invasive thing.
So what I've done is made it so the effects of interruption are undone
if you press R and the gamestate is interrupted. This is handled in
mapclass::resetplayer().
This fixes a bug where the player could bring up the map on the very
first frame of a gamemode(game) animation. This is because the menu
animation checked graphics.menuoffset, but graphics.menuoffset wouldn't
have changed at that point because it only set graphics.resumegamemode.
Instead, just check for graphics.resumegamemode directly. We also need
to assign it to false whenever the map is closed so the player won't be
prevented from using the map screen again.
This fixes all the headaches about map.extrarow having to be the correct
value and which way it should be and whatnot. The latest headache was
the detection that prevent user-initiated menu animations while an
animation was already happening being tripped because
graphics.menuoffset would be 230 (due to closing the menu while being in
a room without a room name), but then going to a room with a room name
would check for 240 instead, and 230 is less than 240. (The numbers are
the wrong way round because I got the ternaries the wrong way round, but
even if the numbers are the correct way round, the bug would still
happen, but it would just be reversed.)
So instead, I've just made it 240 for both. This doesn't change the
duration of the menu animation (because the animation moves in
increments of 25, and 230 / 25 == 240 / 25 under integer division). It
might change the animation slightly, but it was already inconsistent
anyway because map.extrarow was always set to be 1 in custom levels, and
I legitimately would not be able to tell the difference without
recording the animations and nitpicking it frame-by-frame.
Fixes#841.
The player gets kicked out of the Super Gravitron if they have
invincibility or slowdown enabled. However, this can be confusing if no
message pops up
( https://steamcommunity.com/app/70300/discussions/0/3039355280230178910/ )
. So I've made it so that a text box will pop up when they get kicked
out.
This makes it so gamemode(teleporter) will always do an animation, even
if the game is already in TELEPORTERMODE.
I used this script to test:
gamemode(teleporter)
delay(5)
gamemode(teleporter)
delay(5)
gamemode(teleporter)
In 2.2, this script starts the map menu bringing-up animation three
times.
In previous 2.3, this script starts the map menu bringing-up animation
once, but then the next gamemode(teleporter) immediately finishes the
animation, and the third gamemode(teleporter) does nothing.
This commit restores it to 2.2 behavior.
This makes it so it's not even possible to stay on the TELEPORTERMODE
screen by opening the map while it's being brought down. It also makes
it so the map animation is able to be canceled when being brought up
just by opening the map and closing it.
Fixes#833.
This restores it to 2.2 behavior, where the cutscene bars timer also
ticked in TELEPORTERMODE. It was a 2.3 regression that the cutscene bars
timer didn't tick there.
This makes it so if you manage to get stuck in TELEPORTERMODE when a
cutscene ends, the cutscene won't be stuck on untilbars() waiting for
the cutscene bars to go away, since the cutscene bars timer now ticks.
We need to check for graphics.setflipmode, not graphics.flipmode,
because graphics.flipmode only gets assigned at the end of the frame
(due to the deferred callback). Otherwise, returning from the options
menu would always turn flag 73 on, which would make you ineligible to
get the Flip Mode trophy, even if you're in Flip Mode.
Enemy/platform bounds are intended to not be drawn if they cover the
whole screen, since that's what their default bounds are.
However, the code inadvertently made it so if ANY of the bounds touched
a screen edge, the bounds wouldn't be drawn. This is because the
conditionals used "and"s instead of "or"s. The proper way to write the
positive conditional is "x1 is 0 and y1 is 0 and x2 is 320 and y2 is
240", and when you invert that conditional, you need to also invert all
"and"s to be "or"s. This is not the first time that the game developers
failed to properly negate conjunctional statements...
This is to make it so RNG is deterministic when played back with the
same inputs in a libTAS movie even if screen effects or backgrounds are
disabled.
That way, Gravitron RNG is on its own system (seeded in hardreset()),
separate from the constant fRandom() calls that go to visual systems and
don't do anything of actual consequence.
The seed is based off of SDL_GetTicks(), so RTA runners don't get the
same Gravitron RNG every time. This also paves the way for a future
in-built input-based recording system, which now only has to save the
seed for a given recording in order for it to play back
deterministically.
Otherwise, levels could leave stale arguments in the array, and then the
behavior of another level loaded right after might end up being
different because of that.
This is done for consistency with Terry's patrons, which are sorted by
first name and not last.
Also some people go with their usernames and so don't have a last name
to speak of, which ended up being pretty weird.
Kai is my last name. Elizabeth is my middle name. I went with my middle
name as last name for a while before figuring out what I wanted my last
name to be.
Third time's the charm.
The fundamental problem with the previous attempts was that they ended
up saying arguments existed due to stale `words` anyway. So to actually
know if an argument exists or not, we need to assign to `argexists` _as_
we parse the line.
And make sure to take care of that last argument too.
Also I thoroughly tested this this time around. I'm done pulling my hair
out over this.
Ever since tilesheets got expanded, custom levels could use as many
tiles as they wanted, as long as it fit under the 32-bit signed integer
limit.
Until 6c85fae339 happened and they were
reduced to 32,767 tiles.
So I'm being generous again and changing the type of the contents array
(in mapclass and editorclass) back to int. This won't affect the
existing tilemaps of the main game, they'll still stay short arrays. But
it means level makers can use 2 billion tiles once again.
This lets users place down tiles above 1199 in Direct Mode, if their
tilesheet has more than 1200 tiles.
I don't like the copy-pasted code here but it'll have to make do.
If you use Lab tilecol 6, you get the rainbow background. However, this
is unintended, because the associated autotiling is... not very good.
To combat that, Ved disallows using the Lab rainbow background outside
of Direct Mode. We will follow Ved here and only allow switching to the
rainbow background if you're in Direct Mode. Also make sure if someone
is disabling Direct Mode with the rainbow background that it gets reset
properly.
The main game used a set of copy-pasted code to set the music of each
area. There WAS some redundancy built-in, but only three rooms in each
direction from the entrance of a zone.
Given this, it's completely possible for players to mismatch the music
of the area and level. In fact, it's easy to do it even on accident,
especially since 2.3 now lets you quicksave and quit during cutscenes.
Just play a cutscene that has Pause music, then quicksave, quit, and
reload. Also some other accidental ways that I've forgotten about.
To fix this, I've done what mapclass has and made an areamap. Except for
music. This map is the map of the track number of every single room,
except for three special cases: -1 for do nothing and don't change music
(usually because multiple different tracks can be played in this room),
-2 for Tower music (needs to be track 2 or 9 depending on Flip Mode),
and -3 for the start of Space Station 2 (track 1 in time trials, track 4
otherwise).
I've thoroughly tested this areamap by playing through the game and
entering every single room. Additionally I've also thoroughly tested all
special cases (entering the Ship through the teleporter or main
entrance, using the Ship's jukebox, the Tower in Flip Mode and regular
mode, and the start of Space Station 2 in time trial and in regular
mode).
Closes#449.
2.3 has a regression where if you move back and forth between a zone,
you can get the wrong music playing in a zone. An example is the
Overworld and Lab. Just walk in to the Lab and immediately walk back
out, and you'll get Potential for Anything playing in the Overworld.
This regression was caused by facb079b35.
That commit removed assigning -1 to currentsong when a fadeout was
called.
Basically, the previous behavior was: currentsong is 4, we enter Lab and
nicechange gets queued to 3 but currentsong gets set to -1, then going
back nicechange gets queued to 4 again.
However, if we don't assign -1, then going back will keep nicechange at
3. Why? Because niceplay() checks for currentsong before assigning
nicechange. If currentsong is still the same then it doesn't assign
nicechange.
To fix this, just always unconditionally assign nicechange.
If spawned as a custom enemy (createentity entry 56), or spawned outside
of the rooms they spawn in in the main game, they will repeatedly clone
themselves every frame, which profusely leaks memory. In fact it quickly
causes a crash in 2.2 and previous, but 2.3 fixes that crash, so it just
keeps spawning enemies endlessly, which eventually lags the game, and
eventually can out-of-memory your system (bad!).
The problem is those movement types rely on entclass::setenemyroom() to
change their `behave` to be 11 or 13. Else, the new entity created will
still have `behave` 10 or 12, which will create ANOTHER entity in the
same way, and so on, and so forth.
So to fix this, just make it so if an enemy is still `behave` 10 or 12
by the end, then, just set it to -1. That way it'll stay still and won't
cause any harm.
I considered setting the `behave` to 11 or 13 respectively, but, that's
probably going farther than just fixing a memory leak, and anyways, it's
not that much useful for me as a custom level maker, and the entities
spawned aren't really controllable.
In order to let callers provide their OWN callback functions through the
callback function WE provide to PhysFS, we casted the function pointer
to a void pointer.
Unfortunately, this is apparently undefined behavior... if your compiler
doesn't have an extension for it. And most compilers on most
architectures do. (In fact compilers on POSIX systems most certainly
have it due to dlsym() returning a void* which could actually be a
pointer to a function sometimes.)
But imo, it's better to be safe than sorry in this regard. Especially
when given GCC's approach to optimizing int + 100 > int (spoilers: they
remove it entirely! It's faster, but also broken!).
I've decided to wrap it in a struct. And as a nice side effect, if we
ever need more data to be passed through... well we already have this
struct.
Technically, it's also standards-compliant to cast a _pointer to_ a
function pointer to a void pointer. But that extra layer of pointer
indirection would get real confusing to conceptualize real fast (or at
least is more confusing than just putting it in a struct).
Since you've been able to resume music stopped by stopmusic() with
resumemusic(), if a track was stopped by stopmusic(), the unfocus pause
itself would end up resuming the track when regaining focus.
The solution is to simply check for if music.currentsong is -1 or not.