1
0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-06-26 14:38:30 +02:00
Commit Graph

161 Commits

Author SHA1 Message Date
Misa
749a885d9a Actually finally fix createentity default args
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.
2021-08-22 22:47:47 -07:00
Misa
52918ba510 Increment total flips for flip tokens and flipgravity(player)
They flip the player, they should count towards total flips like gravity
lines do.
2021-08-22 20:39:05 -07:00
Misa
8329afc6df Replace main game music with music area map
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.
2021-08-22 20:35:06 -07:00
Misa
aa439bff16 tokenize(): Check all arguments
63a487d20d only checked for the last
argument, not for all arguments.

Fixes #822.
2021-08-12 23:47:03 -04:00
Misa
63a487d20d createentity command: Actually have p1/p2/p3/p4 defaults
Since createentity() started accepting p1/p2/p3/p4 arguments, it now
unconditionally passes in whatever arguments were present there
previously, when there weren't any before.

This can lead to unexpected behavior when selectively using and then
omitting p1/p2/p3/p4 arguments.

Also, plenty of existing levels already only use the 5-argument version
of createentity(). And createcrewman() can take up to 6 arguments at
once. It's not far-fetched that an existing level could createentity()
right after doing a 6-argument createcrewman(), which would lead to a
different behavior than in 2.2 and previous.

So instead, instead of checking if `words[index]` is an empty string (it
only sets the string to be empty if there are enough argument separators
on the line), ACTUALLY check if it's empty. I've added a static array
(no need for it to be exported) that keeps track of this. createentity()
now checks for that instead of `words`.
2021-08-12 00:20:40 -04:00
Misa
87ec35eb45 Cycle tower color if loading level has error
For consistency.
2021-08-12 00:15:33 -04:00
AllyTally
3500888971 Add audiopause(on/off) command
If you have unfocus pause off, and unfocus audio pause off, then this command will go into effect.
When it's set to on, the audio will pause when you unfocus the game. When it's set to off, the
audio will not. This is different from the setting, and gets saved to the save file.
2021-08-11 21:52:11 -04:00
Misa
8dc5d69ef3 Do not close game if custom level has assets issues
It's quite rude to close the game entirely if there is trouble with
assets. Instead, just unload the assets and gracefully return to the
title screen.
2021-08-10 16:33:52 -04:00
Misa
6c6d347ccf Always reset player when starting gamemode
This fixes a bug where the player would always be facing right if they
were loading in for the first time. This essentially made them always
ignore the facing direction set in the save file if the facing direction
was leftwards.

The problem is facing direction only gets set in map.resetplayer(), but
if loading in for the first time, that path is never taken (unless you
are loading a main game quicksave that's inside a tower). The solution
is to always reset the player, even after creating them for the first
time.
2021-08-05 17:30:25 -04:00
Misa
243f9b92f8 Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).

What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 13:35:21 -04:00
Terry Cavanagh
b07d92ac44
Merge pull request #776 from InfoTeddy/general-bug-fixes-6
Explore all rooms when entering Secret Lab
2021-06-12 22:33:37 +10:30
Misa
6dd01495f3 Explore all rooms when entering Secret Lab
If you enter the Secret Lab from the title screen, all rooms will be
explored. However, if you enter the Secret Lab via the Secret Lab
entrance cutscene (epilogue), not all rooms will be explored, which is
inconsistent.

To do this, just do an SDL_memset() for the entersecretlab script
command.
2021-06-11 23:51:48 -07:00
Misa
d404986e6f Use memset to give 20 trinkets and explore all rooms
SDL_memset() conveys intent better and is snappier than using a
for-loop. Also, using SDL_memset() to explore all rooms is more
future-proof, in case the size of map.explored were to change in the
future, and it's more conducive to optimization.

However, the `i` variable has to be explicitly set because it was
previously used here, but it's much better that it's explicitly set here
rather than being subtlely hidden in the inner for-loop initialization.
2021-06-11 23:51:37 -07:00
Misa
6767249558 Prevent losing level data with rollcredits
When rollcredits is ran during in-editor playtesting, all unsaved data
is lost. To prevent this, just return to the editor if rollcredits is
ran, with a note saying "Rolled credits".
2021-06-11 15:30:29 -07:00
Misa
9f603ea3fe Consolidate tower BG bypos and bscroll assignments
Tower backgrounds have a bypos and bscroll. bypos is just the y-position
of the background, and bscroll is the amount of pixels to scroll the
background by on each frame, which is used to scroll it (if it's not
being redrawn) and for linear interpolation.

For the tower background (and not the title background), bypos is
map.ypos / 2, and bscroll is (map.ypos - map.oldypos) / 2. However,
usually bscroll gets assigned at the same time bypos is incremented or
decremented, so you never see that calculation explicitly - except in
the previous commit, where I worked out the calculation because the
change in y-position isn't a known constant.

Having to do all these calculations every time introduces the
possibility of errors where you forget to do it, or you do it wrongly.
But that's not even the worst; you could cause a linear interpolation
glitch if you decide to overwrite bscroll without taking into account
map.oldypos and map.ypos.

So that's why I'm adding a function that automatically updates the tower
background, using the values of map.oldypos and map.ypos, that is used
every time map.ypos is assigned. That way, we have to write less code,
you can be sure that there's no place where we forget to do the
calculations (or at least it will be glaringly obvious) or we do it
wrongly, and it plays nicely with linear interpolation. This also
replaces every instance where the manual calculations are done with the
new function.
2021-04-30 05:31:47 -04:00
Misa
7c55b449e0 Fix two places with map.oldypos not being assigned
These places didn't assign map.oldypos when they assigned map.ypos. This
could have only resulted in visual glitches, but it's good to be
consistent and proactively fix these.
2021-04-30 05:31:47 -04:00
Misa
75133c74e7 Revert part of "Fix music stopping when restarting time trial"
This reverts only a part of f196fcd896 -
as the original commit author did not do their changes atomically, they
also squashed in a de-duplication within the same commit. So I'm only
reverting the part of the commit that wasn't the de-duplication, which
is simply the changes to the music.fadeout() calls.

This is being (partially) reverted for several reasons:

1. It's not the correct behavior. What this does instead is persist the
   track through after you restart the time trial, instead of fading it
   out, then restarting it again. This is in contrast to behavior in
   2.2, and I see no reason to not keep the same behavior.

2. It's a single-case patch. The time trials are not the only time in
   the game a music track could fade out and then be restarted with the
   same track - custom levels could do the same thing too. Instead of
   fixing only one case, we should strive to fix EVERY case.

The original commit author (trelbutate) also didn't write anything in
the commit description of f196fcd896. What
you should write in the commit description is things like rationale,
analysis, and other good information that would be useful to anyone
looking at your commit to understand why you did what you did. Having no
commit description leaves readers in the dark as to why you did what you
did.

Thus, I don't know why trelbutate went with this solution, or if they
knew that it was only a single-case patching, or if they knew that it
wasn't 2.2 behavior.

By not writing the commit description, they miss a chance for
reflection; speaking from personal experience, I myself have gone back
and improved my commits countless times because I wrote commit
descriptions for every single one of them, and sometimes whenever I
write them, I think to myself "hang on a minute, that doesn't sound
quite right" and end up finding improvements.

If trelbutate wrote a commit description, they might have realized that
it wasn't 2.2 behavior, and gone back and fixed up their commit to be
correct. As it stands, though, they didn't have to think about it in the
first place because they never bothered to write a commit description.
2021-04-14 13:02:00 -04:00
Misa
815d96dd4c Fix bracing of time trial startgamemode
It should be next-line brace, not same-line brace.
2021-04-14 13:02:00 -04:00
Malte Desktop
f196fcd896 Fix music stopping when restarting a time trial 2021-04-14 11:09:01 -04:00
Misa
2517900010 De-duplicate zeroing the game clock
For some reason, resetgameclock() is only ever used in gamerender(), and
everywhere else just zeroes the clock manually. This is weird to me, so
I've made it so everywhere that zeroes the clock uses the
resetgameclock() function to do so.
2021-04-13 17:51:24 -04:00
Misa
f6919981e5 Silence music instead of halting in foundtrinket
When you pick up a trinket in the wild, the music gets silenced, so it
silently plays in the background until you advance the trinket text.
However, foundtrinket (used when Victoria or Vitellary give you a
trinket) is inconsistent with this, and halts the music instead of
silencing it.

This was probably due to the musicfadein script command not being
implemented, so Terry or Simon had to simply make do and halt the music
instead. But musicfadein is implemented and is being used in the trinket
cutscenes, so this is another inconsistency that I will fix.
2021-04-12 16:17:31 -04:00
Misa
fe8df3001e Reset completestop in hardreset
If you manage to get softlocked by being stuck in completestop, the next
thing you'll notice is that quitting to the menu or loading back in will
not reset this.

So you can actually softlock yourself in 2.3 by doing the trinket
cutscene, then quitting to the menu (because 2.3 lets you open the pause
menu during completestop). This is a bug, and should be fixed.
2021-04-10 21:47:23 -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
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
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
32be2fcd81 Update player lerpoldxp/yp in moveplayer()
Just like gotoposition(), the player would otherwise appear to "zip"
after the command got run. This did not happen in the previous loop
order.
2021-03-21 02:55:42 -04:00
Misa
c8958de537 Update player lerpoldxp/yp in gotoposition()
Otherwise, the player would appear to "zip" during the deltaframes
between their previous position and their new position. This did not
happen in the previous game loop order and only happens in the new one.
2021-03-21 02:55:42 -04:00
Misa
585ae47d78 Remove script.dontrunnextframe kludge
Now that the game loop order is fixed, this kludge (on top of kludge) is
no longer needed, and can be safely removed.
2021-03-21 02:55:42 -04:00
Misa
596696dcf3 Make foundtrinket() Flip Mode-aware
A-ha! I've spotted an inconsistency! The normal trinket collection text
boxes (gamestate 1000-1003) is aware of Flip Mode, and will position
themselves accordingly to read the correct way in Flip Mode. However,
foundtrinket() doesn't do this.

Well, now it does.
2021-03-21 02:53:25 -04:00
Misa
db9ee0d8e3 Switch flipme script command to use flipme textbox attribute
This is why the text box attribute was named flipme, after all.

You may have noticed that the flipme command inverts textflipme instead
of simply setting it to true. Well, that's because it should be the same
as the previous behavior, which was essentially to invert it instead of
setting it to true - i.e. calling flipme twice would keep the original
text box position in Flip Mode, which means it would be upside-down
(this is a lot of flipping to keep track of...) - because flipme added
to texty in-place instead of simply assigning to it. (It did the
calculation incorrectly in 2.2 and previous, but I digress.)

Similarly, textflipme is not reset in hardreset(), because none of the
other script text box variables are reset either.
2021-03-21 02:53:25 -04:00
Misa
30719b87db De-duplicate "Game Saved" telesave textbox
The "Game Saved" text box, along with its associated telesave() call,
exists in both Game.cpp and Script.cpp, so one of them is the copy-paste
of the other. Unfortunately this copy-paste resulted in an inconsistency
where both of them don't check for the same things when deciding whether
or not the telesave should actually happen (this is why you don't
copy-paste, kids... it's scary!).

Either way, de-duplicating this now is less work for me later.
2021-03-21 02:53:25 -04:00
Misa
d5ed49d8dc Remove commented-out code from Game.cpp and Script.cpp
These commented-out code blocks just get in the way of clarity when I'm
refactoring flipped textboxes created in the gamestate system. So I'm
getting rid of them. If we need them back, we always have Git history.
2021-03-21 02:53:25 -04:00
Misa
f6ecf83190 Ensure oldfadeamount is updated when fadeamount is
Like cutscene bars, I've added Graphics::setfade(), to ensure that no
deltaframe rendering glitches happen due to oldfadeamount not being
updated properly.

And indeed, this fixes a deltaframe rendering glitch that happens if you
return to the editor from playtesting on a faded-out screen, then fade
out again (by either re-entering playtesting and then cause a fadeout to
happen again, or by quitting from the editor afterwards). The same
glitch also happens outside of in-editor playtesting if you exit to the
menu while the screen is faded out.
2021-03-21 01:06:29 -04:00
Misa
f22756dd99 Ensure oldcutscenebars is updated when cutscenebarspos is
To do this, I've added Graphics::setbars(), to make sure
oldcutscenebarspos always gets assigned when cutscenebarspos is. This
fixes potential deltaframe rendering issues if these two mismatch.
2021-03-21 01:06:29 -04:00
Misa
6a3a1fe147
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.

This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.

I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 17:23:59 -05:00
Misa
3927bb9713 Fix entity and block indices after destroying them
This patch restores some 2.2 behavior, fixing a regression caused by the
refactor of properly using std::vectors.

In 2.2, the game allocated 200 items in obj.entities, but used a system
where each entity had an `active` attribute to signify if the entity
actually existed or not. When dealing with entities, you would have to
check this `active` flag, or else you'd be dealing with an entity that
didn't actually exist. (By the way, what I'm saying applies to blocks
and obj.blocks as well, except for some small differing details like the
game allocating 500 block slots versus obj.entities's 200.)

As a consequence, the game had to use a separate tracking variable,
obj.nentity, because obj.entities.size() would just report 200, instead
of the actual amount of entities. Needless to say, having to check for
`active` and use `obj.nentity` is a bit error-prone, and it's messier
than simply using the std::vector the way it was intended. Also, this
resulted in a hard limit of 200 entities, which custom level makers ran
into surprisingly quite often.

2.3 comes along, and removes the whole system. Now, std::vectors are
properly being used, and obj.entities.size() reports the actual number
of entities in the vector; you no longer have to check for `active` when
dealing with entities of any sort.

But there was one previous behavior of 2.2 that this system kind of
forgets about - namely, the ability to have holes in between entities.
You see, when an entity got disabled in 2.2 (which just meant turning
its `active` off), the indices of all other entities stayed the same;
the indice of the entity that got disabled stays there as a hole in the
array. But when an entity gets removed in 2.3 (previous to this patch),
the indices of every entity afterwards in the array get shifted down by
one. std::vector isn't really meant to be able to contain holes.

Do the indices of entities and blocks matter? Yes; they determine the
order in which entities and blocks get evaluated (the highest indice
gets evaluated first), and I had to fix some block evaluation order
stuff in previous PRs.

And in the case of entities, they matter hugely when using the
recently-discovered Arbitrary Entity Manipulation glitch (where crewmate
script commands are used on arbitrary entities by setting the `i`
attribute of `scriptclass` and passing invalid crewmate identifiers to
the commands). If you use Arbitrary Entity Manipulation after destroying
some entities, there is a chance that your script won't work between 2.2
and 2.3.

The indices also still determine the rendering order of entities
(highest indice gets drawn first, which means lowest indice gets drawn
in front of other entities). As an example: let's say we have the player
at 0, a gravity line at 1, and a checkpoint at 2; then we destroy the
gravity line and create a crewmate (let's do Violet).

If we're able to have holes, then after removing the gravity line, none
of the other indices shift. Then Violet will be created at indice 1, and
will be drawn in front of the checkpoint.

But if we can't have holes, then removing the gravity line results in
the indice of the checkpoint shifting down to indice 1. Then Violet is
created at indice 2, and gets drawn behind the checkpoint! This is a
clear illustration of changing the behavior that existed in 2.2.

However, I also don't want to go back to the `active` system of having
to check an attribute before operating on an entity. So... what do we
do to restore the holes?

Well, we don't need to have an `active` attribute, or modify any
existing code that operates on entities. Instead, we can just set the
attributes of the entities so that they naturally get ignored by
everything that comes into contact with it. For entities, we set their
invis to true, and their size, type, and rule to -1 (the game never uses
a size, type, or rule of -1 anywhere); for blocks, we set their type to
-1, and their width and height to 0.

obj.entities.size() will no longer necessarily equal the amount of
entities in the room; rather, it will be the amount of entity SLOTS that
have been allocated. But nothing that uses obj.entities.size() needs to
actually know the amount of entities; it's mostly used for iterating
over every entity in the vector.

Excess entity slots get cleaned up upon every call of
mapclass::gotoroom(), which will now deallocate entity slots starting
from the end until it hits a player, at which point it will switch to
disabling entity slots instead of removing them entirely.

The entclass::clear() and blockclass::clear() functions have been
restored because we need to call their initialization functions when
reusing a block/entity slot; it's possible to create an entity with an
invalid type number (it creates a glitchy Viridian), and without calling
the initialization function again, it would simply not create anything.

After this patch is applied, entity and block indices will be restored
to how they behaved in 2.2.
2021-02-16 19:31:23 -05:00
Misa
52959396bb Remove unneeded comments from scriptclass::scriptclass()
These comments were probably remnants of some late-night coding session
or something. Anyway, they're not needed; there's nothing to do with SDL
here, and the "Init" is obvious because the function is a constructor.
2021-02-15 23:24:31 -05:00
Misa
a113662050 Clean up resetting editor contents and scripts
Contents and scripts should be reset in editorclass::reset(); there's no
reason to reset them again right before you load them from an XML file
in editorclass::load().

Additionally, the resets now consistently use SDL_zeroa() (for contents)
and scriptclass::clearcustom() (for scripts).
2021-02-15 23:24:31 -05:00
Misa
8aa5bb8aab Clean up all program close paths to use VVV_exit()
Wow, there are a lot of these. All of these exit paths now use
VVV_exit() instead, which attempts to save unlock.vvv and settings.vvv,
and also frees all resources so Valgrind is happy. This is a good thing,
because previously unlock.vvv/settings.vvv wouldn't be written to if we
decided to bail for a given reason.
2021-02-15 23:07:35 -05:00
Misa
d39fe96cf2 Move Script.cpp <limits.h> #include to proper place
It should be between the include of the corresponding header file for
the source file (Script.h) and the includes of other local header files
(the files that are specific to this codebase only); this is the
convention that includes in all other source files follow.

However, it seems like I misplaced this, so I'm fixing it now.
2021-02-15 23:07:35 -05:00
Misa
4d7baa9e9e Fix Time Trial results being reset before being shown
This makes it so when a Time Trial gets completed, all the relevant
variables get copied onto variables prefixed with 'timetrialresult',
which never get reset by script.hardreset(). Then titlerender() will use
those variables accordingly.
2021-01-18 13:06:15 -05:00
Misa
ee0ba8a723 Clean up all exit paths to the menu to use common code
There are multiple different exit paths to the main menu. In 2.2, they
all had a bunch of copy-pasted code. In 2.3 currently, most of them use
game.quittomenu(), but there are some stragglers that still use
hand-copied code.

This is a bit of a problem, because all exit paths should consistently
have FILESYSTEM_unmountassets(), as part of the 2.3 feature of per-level
custom assets. Furthermore, most (but not all) of the paths call
script.hardreset() too, and some of the stragglers don't. So there could
be something persisting through to the title screen (like a really long
flash/shake timer) that could only persist if exiting to the title
screen through those paths.

But, actually, it seems like there's a good reason for some of those to
not call script.hardreset() - namely, dying or completing No Death Mode
and completing a Time Trial presents some information onscreen that
would get reset by script.hardreset(), so I'll fix that in a later
commit.

So what I've done for this commit is found every exit path that didn't
already use game.quittomenu(), and made them use game.quittomenu(). As
well, some of them had special handling that existed on top of them
already having a corresponding entry in game.quittomenu() (but the path
would take the special handling because it never did game.quittomenu()),
so I removed that special handling as well (e.g. exiting from a custom
level used returntomenu(Menu::levellist) when quittomenu() already had
that same returntomenu()).

The menu that exiting from the level editor returns to is now handled in
game.quittomenu() as well, where the map.custommode branch now also
checks for map.custommodeforreal. Unfortunately, it seems like entering
the level editor doesn't properly initialize map.custommode, so entering
the level editor now initializes map.custommode, too.

I've also taken the music.play(6) out of game.quittomenu(), because not
all exit paths immediately play Presenting VVVVVV, so all exit paths
that DO immediately play Presenting VVVVVV now have music.play(6)
special-cased for them, which is fine enough for me.

Here is the list of all exit paths to the menu:
- Exiting through the pause menu (without glitchrunner mode)
- Exiting through the pause menu (with glitchrunner mode)
- Completing a custom level
- Completing a Time Trial
- Dying in No Death Mode
- Completing No Death Mode
- Completing an Intermission replay
- Exiting from the level editor
- Completing the main game
2021-01-18 13:06:15 -05:00
Misa
17d06f06be Remove map.finalx/y and map.customx/y
These variables basically serve no purpose. map.customx and map.customy
are clearly never used. map.finalx and map.finaly, on the other hand,
are basically always game.roomx and game.roomy respectively if
map.finalmode is on, and if it's off, then they don't matter.

Also, there are some weird and redundant variable assignments going on
with these; most notably in map.gotoroom(), where rx/ry (local
variables) get assigned to finalx/finaly, then finalx/finaly get
assigned to game.roomx/game.roomy, then finalx/finaly get assigned to
rx/ry. If finalx/finaly made a difference, then there'd be no need to
assign finalx/finaly back to rx/ry. So it makes the code clearer to
remove these weird bits of code.
2021-01-11 00:24:59 -05:00
Misa
775ac4c40c Fix bringing up map menu during gamemode(teleporter)
When gamemode(teleporter) gets run in a script, it brings up a read-only
version of the teleporter screen, intended only for displaying rooms on
the minimap.

However, ever since 2.3 allowed bringing up the map screen during
cutscenes (in order to prevent softlocks), bringing up the map screen
during this mode would (1) do an unnecessary animation of suddenly
switching back to the game and bringing up the menu screen again (even
though the menu screen has already been brought up), and (2) would let
you close the menu entirely and go back to GAMEMODE, thus
unintentionally closing the teleporter screen and kind of ruining the
cutscene.

To fix this, when you bring up the map screen, it will instead instantly
transition to the map screen. And when you bring it down, it will also
instantly transition back to the teleporter screen.

But that's not all. The previous behavior was actually kind of a nice
failsafe, in that if you somehow got stuck in a state where a script ran
gamemode(teleporter), but stopped running before it could take you out
of that mode by running gamemode(game), then you could return to
GAMEMODE yourself by bringing up the map screen and then bringing it
back down. So I've made sure to keep that failsafe behavior, only as
long as there isn't a script running.
2020-12-28 21:12:51 -05:00
Misa
0eddd2d015 De-duplicate menu animation code when bringing up map screen
When bringing up the map screen, the game does a small menu animation
where the menu comes in from the bottom. The code to calculate the menu
offset is copy-pasted everywhere, so I thought I'd de-duplicate it to
make my life easier when working with it. I also included the
game.gamestate assignment in the de-duplicated function, so it would be
easier for a future bugfix.

At the same time, I'm also removing all the BlitSurfaceStandard()s that
copied menubuffer to backBuffer. The red flag is that this blit happened
for every single entry point to MAPMODE and TELEPORTERMODE, except for
the script command gamemode(teleporter). Pressing Enter to bring up the
map screen, pressing Enter to quit the Super Gravitron, pressing Esc to
bring up the pause screen, and pressing Enter to bring up the teleporter
screen all do this blit, so if this blit was there to fix a bug, then
there's a bug with using the script command gamemode(teleporter)... but,
as far as I can tell, there isn't.

That's because the blit basically does nothing. All the blit does is
copy menubuffer onto backBuffer. Then the next thing that happens is
that either maprender() or teleporterrender() will be called, and the
first thing that those functions will always do is fill backBuffer with
solid black, completely overriding the previous blit. So that's why
removing this blit won't have any effect, and it can be safely removed
for code clarity.
2020-12-28 19:55:23 -05:00
Misa
385f9d244e Fix player being invisible upon loading into game again
When I did #567, I didn't test it. And I should have tested it, because
it made the player invisible. This is because map.resetplayer() also
sets the invis attribute of the player to true as well, and I only undid
it setting game.lifeseq to 10.

So instead, I'll just add a flag to map.resetplayer() that by default
doesn't set game.lifeseq or the player's invis attribute. And I tested
it this time, and it works fine. I tested both respawning after death
and exiting to the menu and loading in the game again.
2020-12-28 16:22:13 -05:00
Misa
11302b600a Consistently set lifeseq to 0 when startgamemode() is called
There's a small inconsistency where the first time you load in to the
game, game.lifeseq is at 0, but when you exit to the menu and load in
again, game.lifeseq becomes 10. This is visible as Viridian blinking
when loading in only after the first time you load in, and this also
means that after the first time you load in, you also have to wait 5
frames before being able to move Viridian.

The reason for this inconsistency is because on the first time you load
in to the game, there are no entities loaded in obj.entities yet, so the
game creates a player entity, and doesn't mess with game.lifeseq. When
you exit and then load in for the second time, obj.entities contains at
least one entity (all the entities from the room you just exited out of
- map.gotoroom() hasn't even been called yet, so it doesn't even check
for only the player entity), so the game calls map.resetplayer()
instead, and map.resetplayer() sets game.lifeseq to 10.

There's even an inconsistency to this inconsistency - when you die in No
Death Mode, all entities will be removed from obj.entities, so the next
time you load in to the game, game.lifeseq will be 0.

This inconsistency is pretty minor in the grand scheme of things, but
it still bothers me, so I'm going to fix it.
2020-12-28 08:49:39 -05:00
Misa
e3aa768034 De-duplicate script.running checks
While I was working on #535, I noticed that all the call sites of
script.run() have the exact same code, namely:

    if (script.running)
    {
        script.run();
    }

I figured, why not move the script.running check into the function
itself? That way, we won't have to duplicate the check every single
time, and we don't risk forgetting to add the check and causing a bug
because of that.

The check was already duplicated once since 2.0 (it's used in both
GAMEMODE and TELEPORTERMODE), and with the fix of the two-frame delay in
2.3, it's now duplicated twice, leading to THREE instances of this check
in the code, when there should be only one.
2020-12-24 12:01:37 -05:00
Misa
b62908f0f4 Refactor Game::savestats() to not use a default argument
In order to be able to fix the bug #556, I'm planning on adding
ScreenSettings* to the settings.vvv write function. However, that
entails adding another argument to Game::savesettings(), which is going
to be really messy given the default argument of Game::savestats().
That, combined with the fact that the code comment at the site of the
implementation of Game::savestats() being wrong (!!!), leads me to
believe that using default function arguments here isn't worth it.

Instead, what I've done is made it so callers are explicit about whether
or not they're calling savestats(), savesettings(), or both at the same
time. If they are calling both at the same time, then they will be using
a new function named savestatsandsettings().

In short, these are the interface changes:
 * bool Game::savestats(bool) has been removed
 * bool Game::savestatsandsettings() has been added
 * void Game::savestats_menu() has been renamed to
   void Game::savestatsandsettings_menu()
 * All previous callers of bool Game::savestats() are now using bool
   Game::savestatsandsettings()
 * The one caller of bool Game::savestats(bool) is now using bool
   Game::savestats()
2020-12-21 19:37:34 -05:00
Misa
05b7a38f76 Clean up don't-quick-fade signaling
First, the variable has been inverted, because it's bad practice to have
booleans with negative names. Secondly, instead of magically setting a
signaling variable when calling fadeout(), fadeout() now has a parameter
to set the quick_fade attribute, which is much cleaner than doing the
magical assignment that could potentially look unrelated.
2020-11-06 06:09:31 -05:00