1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 10:09:43 +01:00
Commit graph

1469 commits

Author SHA1 Message Date
Misa
c68efac032 Remove unneeded pKey when loading room property XML elements
I don't know why this is here; it's unused. I don't know why the
compiler doesn't warn about this being unused either - maybe it's
secretly being used? That also means I'm not sure if the compiler is
optimizing this away or not. Anyway, this is getting removed.
2021-02-27 01:40:05 -05:00
Misa
d590463834 Use less STL when loading entity XML elements
The STL here cannot be completely eliminated (because the custom entity
object uses std::string), but at least we can avoid unnecessarily making
std::strings until the very end.
2021-02-27 01:40:05 -05:00
Misa
2aeb3f35ce Remove std::strings from levelZipCallback()
There's basically no reason to use heap-allocated strings here, so it's
all gone now.
2021-02-27 01:40:05 -05:00
Misa
5d4c1b7e9d Refactor endsWith() to not use the STL
There's not really any reason for this function to use heap-allocated
strings. So I've refactored it to not do that.

I would've used SDL_strrstr(), if it existed. It does not appear to
exist. But that's okay.
2021-02-27 01:40:05 -05:00
Misa
3171a97160 Replace all SDL_RWFromMem() with SDL_RWFromConstMem()
Since we're not going to be writing to any of these RWops, we might as
well just ensure that we don't by using SDL_RWFromConstMem().
2021-02-25 19:39:48 -05:00
Misa
4a8c0a38ee Use SDL allocators for PhysFS
PhysFS by default just uses system malloc(), realloc(), and free(); it
provides a way to change them, with a struct named PHYSFS_Allocator and
a function named PHYSFS_setAllocator().

According to PhysFS docs, this function should be called before
PHYSFS_init(), which is why this allocator stuff is handled in
FileSystemUtils.cpp.

Also, I've had to make two "bridge" functions, because PHYSFS_Allocator
wants pointers to functions taking in `PHYSFS_uint64`s, not `size_t`s.
2021-02-25 19:39:23 -05:00
Misa
38d5664601 Change all surface-clearing FillRect()s to use ClearSurface()
ClearSurface() is less verbose than doing it the old way, and also
conveys intent clearer. Plus, some of these FillRect()s had hardcoded
width and height values, whereas ClearSurface() doesn't - meaning this
change also has better future-proofing, in case the widths and heights
of the surfaces involved change in the future.
2021-02-25 19:38:25 -05:00
Misa
e545c89b5e Combine declaration/initialization of color in two FillRect()s
Just a small simplification, so it's both declared and initialized on
the same line.
2021-02-25 19:38:25 -05:00
Misa
e641063d68 Fix two versions of FillRect() unnecessarily creating SDL_Rects
When you pass NULL in for the SDL_Rect* parameter to SDL_FillRect(), SDL
will automatically fill the entire surface with that color. There's no
need for us to create the SDL_Rect ourselves.
2021-02-25 19:38:25 -05:00
Misa
163b85d206 Add ClearSurface()
This is a function that does what it says - it clears the given surface.
This just means doing a FillRect(), but it's better to use this function
because it conveys intent better.
2021-02-25 19:38:25 -05:00
Misa
994f47e7d8 Remove commented-out code from Graphics.cpp and GraphicsUtil.cpp
This is pretty old commented-out code from earlier versions of the game;
they are no longer useful, and are just distracting. If we need them, we
can always refer back to this commit (but I sincerely doubt that we'll
need them).
2021-02-25 19:38:25 -05: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
0e313d0d75 Remove unneeded <algorithm> includes
One of these days, I need to get around to running Include What You Use
on this codebase. Until then, while I was working on #624, I noticed
these; I'm removing them now.
2021-02-20 00:12:11 -05:00
leo60228
c1950037c2
Use SDL_OpenURL in FILESYSTEM_openDirectory (#623)
The recently released SDL 2.0.14 adds a native function for opening URIs
from the host system, superseding the OS-specific implementations of
FILESYSTEM_openDirectory.
2021-02-19 21:16:19 -05:00
Misa
4e7d63cf09 Remove assigning block type to -1 when disabling them
This fixes a regression where moving platforms had no collision. Because
their width and height would be maintained, but their type would be -1.
(Also because I didn't test enough.)

In #565, I decided to set blocks' types to -1 when disabling them, to be
a bit safer in case there was some code that used block types but not
their width and heights. However, this means that when blocks get
disabled and re-created in the platform update loops, their types get
set to -1, which effectively also disables their collision.

In the end, I'll just have to compromise and remove setting blocks to
type -1. Because in a better world, we shouldn't be destroying and
creating blocks constantly just to move some platforms - however, fixing
such a fundamental problem is beyond the scope of at least 2.3 (there's
also the fact that this problem also results in some bugs that are a
part of compatibility, whether we like it or not). So I'll just remove
the -1.
2021-02-19 18:21:40 -05:00
Misa
cd5408e396 Fix potential out-of-bounds in next_split_s() wrt buffer length
next_split_s() could potentially commit out-of-bounds indexing if the
amount of source data was bigger than the destination data.

This is because the size of the source data passed in includes the null
terminator, so if 1 byte is not subtracted from it, then after it passes
through the VVV_min(), it will index 1 past the end of the passed buffer
when null-terminating it.

In contrast, the other argument of the VVV_min() does not need 1
subtracted from it, because that length does not include a null
terminator (next_split() returns the length of the substring, after all;
not the length of the substring plus 1).

(The VVV_min() here also shortens the range of values to the size of an
int, but we'll probably make size_t versions anyway; plus who really
cares about supporting massively-sized buffers bigger than 2 billion
bytes in length? That just doesn't make sense.)
2021-02-19 17:17:24 -05:00
Misa
72b8afcf7d Print if PHYSFS_enumerate() has an error
If PHYSFS_enumerate() isn't successful, we now print that it wasn't
successful, and print the PhysFS error message. (We should get that
logging thing going sometime...)
2021-02-19 07:12:13 -05:00
Misa
83976016c7 Refactor level dir listing to not use STL data marshalling
Note that level dir listing still uses plenty of STL (including the end
product - the `LevelMetaData` struct - which, for the purposes of 2.3,
is okay enough (2.4 should remove STL usage entirely)); it's just that
the initial act of iterating over the levels directory no longer takes
four or SIX(!!!) heap allocations (not counting reallocations and other
heap allocations this patch does not remove), and no longer does any
data marshalling.

Like text splitting, and binary blob extra indice grabbing, the current
approach that FILESYSTEM_getLevelDirFileNames() uses is a temporary
std::vector of std::strings as a middleman to store all the filenames,
and the game iterates over that std::vector to grab each level metadata.
Except, it's even worse in this case, because PHYSFS_enumerateFiles()
ALREADY does a heap allocation. Oh, and
FILESYSTEM_getLevelDirFileNames() gets called two or three times. Yeah,
let me explain:

1. FILESYSTEM_getLevelDirFileNames() calls PHYSFS_enumerateFiles().

2. PHYSFS_enumerateFiles() allocates an array of pointers to arrays of
   chars on the heap. For each filename, it will:

   a. Allocate an array of chars for the filename.

   b. Reallocate the array of pointers to add the pointer to the above
      char array.

      (In this step, it also inserts the filename in alphabetically -
      without any further allocations, as far as I know - but this is a
      COMPLETELY unnecessary step, because we are going to sort the list
      of levels by ourselves via the metadata title in the end anyways.)

3. FILESYSTEM_getLevelDirFileNames() iterates over the PhysFS list, and
   allocates an std::vector on the heap to shove the list into. Then,
   for each filename, it will:

   a. Allocate an std::string, initialized to "levels/".

   b. Append the filename to the std::string above. This will most
      likely require a re-allocation.

   c. Duplicate the std::string - which requires allocating more memory
      again - to put it into the std::vector.

      (Compared to the PhysFS list above, the std::vector does less
      reallocations; it however will still end up reallocating a certain
      amount of times in the end.)

4. FILESYSTEM_getLevelDirFileNames() will free the PhysFS list.

5. Then to get the std::vector<std::string> back to the caller, we end
   up having to reallocate the std::vector again - reallocating every
   single std::string inside it, too - to give it back to the caller.

And to top it all off, FILESYSTEM_getLevelDirFileNames() is guaranteed
to either be called two times, or three times. This is because
editorclass::getDirectoryData() will call editorclass::loadZips(), which
will unconditionally call FILESYSTEM_getLevelDirFileNames(), then call
it AGAIN if a zip was found. Then once the function returns,
getDirectoryData() will still unconditionally call
FILESYSTEM_getLevelDirFileNames(). This smells like someone bolting
something on without regard for the whole picture of the system, but
whatever; I can clean up their mess just fine.

So, what do I do about this? Well, just like I did with text splitting
and binary blob extras, make the final for-loop - the one that does the
actual metadata parsing - more immediate.

So how do I do that? Well, PhysFS has a function named
PHYSFS_enumerate(). PHYSFS_enumerateFiles(), in fact, uses this function
internally, and is basically just a wrapper with some allocation and
alphabetization.

PHYSFS_enumerate() takes in a pointer to a function, which it will call
for every single entry that it iterates over. It also lets you pass in
another arbitrary pointer that it leaves alone, which I use to pass
through a function pointer that is the actual callback.

So to clarify, there are two callbacks - one callback is passed through
into another callback that gets passed through to PHYSFS_enumerate().

The callback that gets passed to PHYSFS_enumerate() is always the same,
but the callback that gets passed through the callback can be different
(if you look at the calling code, you can see that one caller passes
through a normal level metadata callback; the other passes through a zip
file callback).

Furthermore, I've also cleaned it up so that if editorclass::loadZips()
finds a zip file, it won't iterate over all the files in the levels
directory a third time. Instead, the level directory only gets iterated
over twice - once to check for zips, and another to load every level
plus all zips; the second time is when all the heap allocations happen.

And with that, level list loading now uses less STL templated stuff and
much less heap allocations.

Also, ed.directoryList basically has no reason to exist other than being
a temporary std::vector, so I've removed it. This further decreases
memory usage, depending on how many levels you have in your levels
folder (I know that I usually have a lot and don't really ever clean it
up, lol).

Lastly, in the callback passed to PhysFS, `builtLocation` is actually no
longer hardcoded to just the `levels` directory, since instead we now
use the `origdir` variable that PhysFS passes us. So that's good, too.
2021-02-19 07:12:13 -05:00
Misa
532dbee4fd Contextualize PHYSFS_mountHandle() failure output
If PHYSFS_mountHandle() failed to mount a zip file, we would print
PhysFS's error message straight, without any surrounding context. This
seems a little weird, and doesn't maximize understanding for readers;
I've made it so now the error message is "Could not mount <zip file>:
<PhysFS error>".
2021-02-19 07:12:13 -05:00
Misa
f22691c32a Remove hardcoded check for "data" in level dir listing
When Ethan added PhysFS to the game, he put in a hardcoded check (marked
with a FIXME) that explicitly removed all filenames that were "data"
returned by PHYSFS_enumerateFiles(). Apparently this was due to a weird
bug with the function putting in "data" strings in its output in PhysFS
2.0.3; however, the game now uses PhysFS 3.0.2, and I could not
reproduce this bug on my system. (I also tested, and this also
straight-up ignores legitimate level filenames that just happen to be
"data" (without the .vvvvvv extension).)

After talking with Ethan in Discord DMs, I asked if we could remove this
check, and he said that we could. So I'm doing it now.
2021-02-19 07:12:13 -05:00
Misa
bf97e23fb6 Add static keywords to replace_all and tag finder funcs
These functions will never be used outside editor.cpp; they should
therefore be marked with the `static` keyword.
2021-02-19 07:12:13 -05:00
Misa
141181dee7 Refactor extra binary blob tracks to not use STL data marshalling
Just like I refactored text splitting to no longer use std::vectors,
std::strings, or temporary heap allocations, decreasing memory usage and
improving performance; there's no reason to use a temporary
heap-allocated std::vector to grab all extra binary blob indices, when
instead the iteration can just be more immediate.

Instead, what I've done is replaced binaryBlob::getExtra() with
binaryBlob::nextExtra(), which takes in a pointer to an index variable,
and will increment the index variable until it reaches an extra track.
After the caller processes the extra track, it is the caller's
responsibility to increment the variable again before passing it back to
getExtra().

This avoids all heap allocations and brings down the memory usage of
processing extra tracks.
2021-02-19 07:07:39 -05:00
Misa
7a02f174ac Add option to not use bundled TinyXML-2, PhysFS, and UTF8-CPP
If you configure the build with -DBUNDLE_DEPENDENCIES=OFF, then VVVVVV
will dynamically link with TinyXML-2 and PhysicsFS instead of using the
bundled source code in third_party/ and statically linking with them.

Unfortunately, it doesn't seem like distros package LodePNG, and LodePNG
isn't intended to be packaged, so we can't dynamically link with it, nor
can we use some system LodePNG header files somewhere else because those
don't exist.

UTF8-CPP is a special case, because no matter what, it's going to be
statically linked with the binary (it doesn't come as a shared object
file in any way). So with -DBUNDLE_DEPENDENCIES=OFF, we will use the
system UTF8-CPP header files instead of the bundled ones, but it will
still be statically linked in with the binary.

The main motivation for doing this is so if VVVVVV ever gets packaged in
distros, distro maintainers would be more likely to accept it if there
was an option to compile the game without bundled dependencies. Also, it
discourages modifying the third-party dependencies we have, because it's
always possible for someone to compile those dependencies without our
changes, with this CMake option.
2021-02-17 09:05:11 -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
99276d7cdd Fix using ii instead of i in script serialization
Otherwise this results in an infinite loop. Whoops.
2021-02-16 00:13:39 -05:00
Misa
b243b116c9 Fix VVV_isxdigit() accepting A-Za-z instead of A-Fa-f
This function accepted more characters than it should have. (I made the
same mistake on my PR to SDL, embarassingly enough...)
2021-02-15 23:50:08 -05:00
Misa
3225be6d9e Fix is_number() accepting "-" as a number
The function would accept the string "-" as a number, when it isn't one.

To fix this, don't put it as part of the loop, just add another special
case.
2021-02-15 23:50:08 -05:00
Misa
28caa994e9 Add a blank line to the end of is_number()
This is to be consistent with is_positive_num().
2021-02-15 23:50:08 -05:00
Misa
4a853fec97 Add a blank line to the end of is_positive_num()
The return statement is too close to the block above it, so I want to
space it out for aesthetics.
2021-02-15 23:50:08 -05:00
Misa
30b0fc828f Change is_number() increment to pre-increment
This is also to be consistent with is_positive_num().
2021-02-15 23:50:08 -05:00
Misa
62533300b0 Change is_number() iterator type to size_t
This is to be consistent with is_positive_num().
2021-02-15 23:50:08 -05:00
Misa
7ea70ad1d6 Fix is_number() returning true for empty strings
Just like is_positive_num(), an empty string is not a number.

I've also decided to unroll iteration 0 of the loop here so readability
is improved; this happens to also knock out the whole "accepting empty
string" thing, too.
2021-02-15 23:50:08 -05:00
Misa
b9bf2be436 Fix is_positive_num() returning true for empty strings
To account for empty strings, we simply have to special-case them.
Simple as that.

This was also a problem with the previous std::string implementation of
this function; regardless, this is fixed now.
2021-02-15 23:50:08 -05:00
Misa
89bddf98b6 Refactor is_positive_num() to use C-strings
This makes the future move to C easier, as well as just shows that it's
plain unnecessary to use an std::string here.
2021-02-15 23:50:08 -05:00
Misa
825dbac228 Make const the 'hex' argument of is_positive_num()
Always good to get in the habit of clearly marking arguments you won't
mutate as const. Makes the code easier to read.
2021-02-15 23:50:08 -05:00
Misa
62808ca97a Remove unnecessary static_cast in is_number()
Just like is_positive_num(), the implicit conversion here is completely
okay, and adding an explicit cast here introduces noise.
2021-02-15 23:50:08 -05:00
Misa
623f4edc6e Remove unnecessary static_casts in is_positive_num()
The implicit conversion is completely okay. Adding an explicit cast here
just creates noise for the reader to filter out.
2021-02-15 23:50:08 -05:00
Misa
84ac4a40c1 Refactor loading arrays from XML to not use the STL
The current way "arrays" from XML files are loaded (before this commit
is applied) goes something like this:

1. Read the buffer of the contents of the tag using TinyXML-2.

2. Allocate a buffer on the heap of the same size, and copy the
   existing buffer to it. (This is what the statement `std::string
   TextString = pText;` does.)

3. For each delimiter in the heap-allocated buffer...

   a. Allocate another buffer on the heap, and copy the characters from
      the previous delimiter to the delimiter you just hit.

   b. Then allocate the buffer AGAIN, to copy it into an std::vector.

4. Then re-allocate every single buffer YET AGAIN, because you need to
   make a copy of the std::vector in split() to return it to the caller.

As you can see, the existing way uses a lot of memory allocations and
data marshalling, just to split some text.

The problem here is mostly making a temporary std::vector of split text,
before doing any actual useful work (most likely, putting it into an
array or ANOTHER std::vector - if the latter, then that's yet another
memory allocation on top of the memory allocation you already did; this
memory allocation is unavoidable, unlike the ones mentioned earlier,
which should be removed).

So I noticed that since we're iterating over the entire string once
(just to shove its contents into a temporary std::vector), and then
basically iterating over it again - why can't the whole thing just be
more immediate, and just be iterated over once?

So that's what I've done here. I've axed the split() function (both of
them, actually), and made next_split() and next_split_s().

next_split() will take an existing string and a starting index, and it
will find the next occurrence of the given delimiter in the string. Once
it does so, it will return the length from the previous starting index,
and modify your starting index as well. The price for immediateness is
that you're supposed to handle keeping the index of the previous
starting index around in order to be able to use the function; updating
it after each iteration is also your responsibility.

(By the way, next_split() doesn't use SDL_strchr(), because we can't get
the length of the substring for the last substring. We could handle this
special case specifically, but it'd be uglier; it also introduces
iterating over the last substring twice, when we only need to do it
once.)

next_split_s() does the same thing as next_split(), except it will copy
the resulting substring into a buffer that you provide (along with its
size). Useful if you don't particularly care about the length of the
substring.

All callers have been updated accordingly. This new system does not make
ANY heap allocations at all; at worst, it allocates a temporary buffer
on the stack, but that's only if you use next_split_s(); plus, it'd be a
fixed-size buffer, and stack allocations are negligible anyway.

This improves performance when loading any sort of XML file, especially
loading custom levels - which, on my system at least, I can noticeably
tell (there's less of a freeze when I load in to a custom level with
lots of scripts). It also decreases memory usage, because the heap isn't
being used just to iterate over some delimiters when XML files are
loaded.
2021-02-15 23:24:31 -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
fe77ada160 Clean up comments in script XML parsing code
I'm partial to slash-asterisk-style comments, so I'll use those here.
Also, having a space after the start of comments is good. I've also
removed the "Add the script if we have a preceding header" comments
since it can be inferred by reading the surrounding code.
2021-02-15 23:24:31 -05:00
Misa
4f224e1657 Clean up whitespace in script XML parsing code
There is now a space between all 'if's and their opening parentheses.
I've also cleaned up the blank line spacing so the code looks better.
2021-02-15 23:24:31 -05:00
Misa
ca973a6547 Unindent from collapsing empty pText check
As always, the diff algorithms do terribly with unindentation, so I'm
doing the actual unindenting in this commit.
2021-02-15 23:24:31 -05:00
Misa
8d5829dd26 Remove indentation level from checking for empty pText
Instead of checking the length() of an std::string, just check if
pText[0] is equal to '\0'.

This will have to be done anyway, because I'm going to get rid of the
std::string allocation here, and I noticed this inefficiency in the
indentation, so I'm going to remove it.

The actual unindent will be done in the next commit.
2021-02-15 23:24:31 -05:00
Misa
968bb5f960 De-duplicate <customlevelscore> loading to use LOAD_ARRAY_RENAME()
This now means every XML array loading is done with common,
re-duplicated code. The only exceptions to this are special cases other
than the the majority of cases; the majority being a simple matter of
reading an array of integers and putting it into another array.

Seems like the only reason I hadn't caught the <customlevelscore> tag
until now was because I was focused on de-duplicating all the array
loads in Game::loadstats() and below, forgetting about
Game::loadcustomlevelstats().
2021-02-15 23:24:31 -05:00
Misa
6dc423e78e Move LOAD_ARRAY[_RENAME] macros to before loadcustomlevelstats()
In order to be able to use the LOAD_ARRAY() and LOAD_ARRAY_RENAME()
macros in Game::loadcustomlevelstats(), they have to be moved to earlier
in the file.
2021-02-15 23:24:31 -05:00
Misa
3fcdc084d0 Refactor editor gotoroom shortcut to not use split()
Even if split() didn't use the STL, using this function here is a bit
unnecessary, because a simple SDL_strchr() suffices. Refactoring split()
to not use the STL will break this caller anyway, so I might as well
just refactor this to not use split() in the first place.

This refactor also properly checks if the inputs are valid integers. And
since split() is no longer used, it also rejects inputs ending with a
trailing comma as being invalid, too; this didn't happen previously.
It's intentional that I used is_number() here instead of
is_positive_num(), thus accepting negative numbers; in the future it
might be possible to have negative room coordinates.
2021-02-15 23:24:31 -05:00
Misa
fe8d163041 Fix reading uninitialized memory when creating Menu::levellist
Valgrind reported this.

The error here is that the buffer here is only guaranteed to be
initialized up until (and including) the null-terminator, by
SDL_snprintf(). Iterating over the entire allocated buffer is bad and I
should feel bad as the girl who wrote this code; doing that reads
uninitialized memory and passes it to SDL_tolower().

As a bonus, the iterator increment is now a preincrement instead of a
postincrement.
2021-02-15 23:07:35 -05:00
Misa
3226d4f312 Free loaded file in editorclass::getLevelMetadata()
This fixes memory leaking every single time a file gets loaded(!) when
the list of custom levels gets loaded(!!!), which Valgrind reports. This
memory leak is completely my bad; 2.2 properly frees the loaded file,
and VCE uses an std::unique_ptr - which I decided to ignore and not
think about why it would be there.

It's safe to do this free after uMem gets copied into std::string;
although, in the future, I *am* thinking about refactoring this function
(and the tag finder function) to not use std::strings, and I'll have to
be careful to make sure that the memory management with the file is
correct when I do so.
2021-02-15 23:07:35 -05:00
Misa
16fef54ae1 Pass 1 to Mix_LoadMUS_RW() in MusicTrack::MusicTrack()
This makes the freesrc argument of Mix_LoadMUS_RW() 1 instead of 0. If
the argument is nonzero, then the passed SDL_RWops will be automatically
freed when m_music is freed, too.

I don't know why this was 0 before. Setting it to 1 fixes a memory leak
that Valgrind reports (which turns into an actual leak every time custom
assets are mounted or unmounted).
2021-02-15 23:07:35 -05:00
Misa
8052f61cef Check if file can't be loaded in SoundTrack::SoundTrack()
This adds a check that the pointer passed to
FILESYSTEM_loadFileToMemory() isn't NULL, and if it is, just returns
early in the function, instead of continuing later and producing a
different, slightly-misleading error message.
2021-02-15 23:07:35 -05:00
Misa
f401cc6230 Unconditionally call FILESYSTEM_freeMemory() in SoundTrack constructor
Previously, it was guarded behind a check for the length, which is... I
guess still perfectly fine behavior, but there's no reason to have a
length check here; FILESYSTEM_freeMemory() uses SDL_free(), which does a
check that the pointer passed is non-NULL (the pointer that is passed
here, despite not being initialized upon declaration, is guaranteed to
be initialized by FILESYSTEM_loadFileToMemory() anyway, so).
2021-02-15 23:07:35 -05:00
Misa
b19daebeef Bail for all SDL_malloc() failures
Following Ethan's example of bailing (calling VVV_exit()) if
binaryBlob::unPackBinary() couldn't allocate memory, I've searched
through and found every SDL_malloc(), then made sure that if it returned
NULL, the caller would bail (because you can't do much when you're out
of memory).

There should probably be an error message printed when the process is
out of memory, but unPackBinary() doesn't print an error message for
being out of memory, so this can probably be added later. (Also we don't
really have a logging system, I'd like to have something like that added
in first before adding more messages.)

Also, this doesn't account for any allocators used by STL stuff, but
we're working on removing the STL, and allocation failure just results
in an abort anyway, so there's not really a problem there.
2021-02-15 23:07:35 -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
de1e773b7f Remove <string.h> #include from main.cpp
We no longer use libc string functions, instead preferring SDL's; this
unused include should be removed.
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
4964cb7bc3 Add VVV_exit()
This is just a function that calls the cleanup() in main.cpp, as well as
calls exit().

I would have liked to use SDL_ExitProcess() here, because that function
has ifdefs for different runtime environments. But alas, it's an
internal function and isn't exported. Ah well; exit() seems to be fine
anyway.
2021-02-15 23:07:35 -05:00
Misa
efbaeeffde Clean up all leftover resources in cleanup()
If there's a resource that doesn't otherwise need to be cleaned up and
is still alive upon program shutdown, then it should go in cleanup().

This cleans up Screen, GraphicsResources, Graphics buffers, Graphics
tiles, and musicclass audio upon program shutdown.

Even we technically don't NEED to clean these resources up ourselves
(the kernel is going to get rid of all of it anyway, else it'd be a
security problem), I'm doing this because otherwise Valgrind will
complain about these, and then it'd be difficult to see which memory
leaks are real and which are just "well this isn't really a leak but you
haven't freed this thing when the process exited, and that's technically
what a memory leak is".

These are all resources whose cleanup functions can be safely called
even if they haven't initialized anything yet.
2021-02-15 23:07:35 -05:00
Misa
39e316828e Add NULL guards to Game::savestats() and savesettings()
This is so those functions can safely be called when
graphics.screenbuffer is NULL.
2021-02-15 23:07:35 -05:00
Misa
1f1b39a77a Free base tilesheet image after processing it
This isn't a memory leak (not even Valgrind complains), because it gets
properly cleaned up in GraphicsResources::destroy(). Still, it's memory
that is just laying around not being used, and in the name of
deallocating things as soon as you no longer need them, we should
deallocate the base tilesheet images after we split all of them into
tiles.

This reduces the memory cost of all tilesheet images by half, since we
were essentially keeping around duplicates for nothing; this doesn't
really have much of an impact with conventional tilesheet sizes, since
they're usually small enough, but since 2.3 allowed for tilesheet images
of any size, this is a pretty big deal for really big tilesheet images.

It's okay to do this, even though they also get freed in
GraphicsResources::destroy(), because SDL_FreeSurface() does a NULL
check on the pointer passed to it, and we set the pointer to NULL after
freeing the surfaces.
2021-02-15 23:07:35 -05:00
Misa
6077015fdc Guard PHYSFS_deinit() with PHYSFS_isInit()
A quick glance at PhysFS source code will show that PhysFS will bail if
PHYSFS_deinit() is called if it's not initialized.

"Bail" here just means setting an error code and returning early, so
it's not that bad. Still, it's the principle of the thing, and I just
want to ensure that FILESYSTEM_deinit() can be safely called no matter
if the filesystem hasn't initialized yet; having an error set by PhysFS
kind of taints the whole safety thing, even if it does nothing wrong,
no?

(although, speaking of which, we should be handling all errors by
PhysFS, but that's for later...)
2021-02-15 23:07:35 -05:00
Misa
951e1653a6 Move cleanup code to separate function
I will need to re-use this code, so it's best that it not be
copy-pasted.
2021-02-15 23:07:35 -05:00
Misa
de26596d54 Fix FIXME comments with outdated referents in Screen.cpp
These FIXME comments are still correct about code duplication, but
they're incorrect about where exactly the original code is after the
original code got moved around. So I've fixed them to refer to the
correct locations.

We really should get around to de-duplicating the code mentioned in
these comments...
2021-02-15 23:07:35 -05:00
Misa
a3ad7b73f3 Add Screen::destroy()
In order to have squeaky-clean memory management, we'll need to clean up
all the things that Screen allocates. This is the function to call to do
so.
2021-02-15 23:07:35 -05:00
Misa
7f3ebda8ea Clear musicWriteBlob after writing BinaryMusic.vvv
Since musicWriteBlob is a temporary object that gets destroyed at the
end of musicclass::init(), in order to make sure we don't leak memory
and lose all the pointers to the blocks we just allocated in
musicWriteBlob, we need to call its clear() method after writing
BinaryMusic.vvv.
2021-02-15 23:07:35 -05:00
Misa
77748f29f9 Separate musicReadBlob into mmmmmm_blob and pppppp_blob
musicReadBlob was used for both MMMMMM and PPPPPP soundtracks. This
causes a memory leak if you have mmmmmm.vvv installed, because the
pointers holding each allocated block of MMMMMM would be lost when
PPPPPP got loaded. Valgrind complains about this memory leak.

This is in contrast to 2.2 and previous behavior, where musicReadBlob
was only a temporary object instead of being held in musicclass.
However, this wasn't really a memory leak (moreso something that just
didn't get cleaned up when closing the game), but it did get turned into
a leak when per-level assets mounting and unmounting got introduced in
2.3 (loading a level with custom assets after starting the game with an
mmmmmm.vvv, or exiting out of a level that had an mmmmmm.vvv, would
cause the game to leak memory). Leo recognized this, and moved
musicReadBlob onto musicclass in a separate 2.3 PR, but either he didn't
think about what was happening here too closely, or he didn't use
Valgrind, because he forgot about the memory leak caused by re-using the
same binaryBlob for PPPPPP and MMMMMM.

So instead, just use two different binaryBlob objects for MMMMMM and
PPPPPP. That way, no memory leaks happen.
2021-02-15 23:07:35 -05:00
Misa
f39174b3e3 Refactor TRACK_NAMES to take in a blob parameter
I'm going to introduce another binaryBlob object in to the mix, and I
want to be able to re-use an existing FOREACH_TRACK #define without
having to copy-paste it again. So, TRACK_NAMES now takes in a blob
parameter, which will be passed to the temporary FOREACH_TRACK #define.
2021-02-15 23:07:35 -05:00
Misa
0ea1a0e28e Move musicclass cleanup to separate function musicclass::destroy()
This removes the music cleanup code from musicclass::init(), and
requires that we also call destroy() in Graphics::reloadresources().

This is because we'll need to re-use the musicclass cleanup code
elsewhere, and we don't want to copy-paste the cleanup code. Or at
least, I don't (but I'm not a game dev, game devs copy-paste all the
friggin' time).
2021-02-15 23:07:35 -05:00
Misa
b3679ce1e5 Fix brace style of Graphics::font_idx()
This really should be next-line brace, as is (most of) the rest of the
codebase.
2021-02-15 23:07:35 -05:00
Misa
a505059719 Move Graphics buffer creation to new func create_buffers()
It doesn't feel quite write leaving all the buffer creation code in
main(), even though it's perfectly okay to do so and it doesn't result
in any memory mismanagement that Valgrind can report; so I'm factoring
all of it out to a separate function, Graphics::create_buffers().

As a bonus, we no longer have to keep qualifying with `graphics.` in the
buffer creation code, which is nice.
2021-02-15 23:07:35 -05:00
Misa
5595bb0964 Add Graphics::destroy_buffers()
These destroy all the buffers that are created on the Graphics class.
Since these buffers can't be created at the same time as the rest of
Graphics is (due to the fact that they require knowing the pixel format
of the game screen), they can't be destroyed at the same as the rest of
Graphics is, either.
2021-02-15 23:07:35 -05:00
Misa
3fe1870b32 Move Graphics cleanup to new function Graphics::destroy()
This makes it so this cleanup code can be re-used without copy-pasting
it over and over again.
2021-02-15 23:07:35 -05:00
Misa
c955ce4380 Remove making new GraphicsResources object in reloadresources()
This is a very complicated way of zeroing out grphx (instead of using
SDL_zero()), which itself is completely unnecessary because grphx.init()
gets called immediately afterwards anyway.
2021-02-15 23:07:35 -05:00
Misa
1140f3cae3 Fix brace style of Graphics::reloadresources()
It should be next-line brace, not same-line brace. Even in a codebase
that uses same-line braces everywhere, I still prefer having next-line
braces inside functions (because they're at the top level, and you can't
next them). But regardless, this should still be next-line brace like
(most of) the rest of the codebase.
2021-02-15 23:07:35 -05:00
Misa
7ffafc8f4d Remove grphx.init() from Graphics::init()
grphx.init() is going to be called again (after grphx.destroy()) in
graphics.reloadresources() anyway, so there's no reason to have it here.
2021-02-15 23:07:35 -05:00
Misa
70b9ffe6a5 Tidy up binaryBlob initialization to use SDL_zeroa()
It's simpler and more concise than writing out the SDL_memset() in full,
which means you're less likely to screw up those lines of code.
2021-02-15 23:07:35 -05:00
Misa
bd97378862 Unconditionally free and zero in binaryBlob::clear()
The function previously conditionally freed a m_memblocks pointer if its
corresponding m_headers was valid. This makes me slightly worried about
the possibility that memory would be allocated, but the header would
still be marked as invalid.

I don't see how that could happen, but it's better to be safe than
sorry. SDL_free() does a guaranteed NULL pointer check (like most SDL
functions), so it's okay to pass NULL pointers to it.

Just to be sure, I'm also zeroing m_memblocks and m_headers after
freeing everything in the function.
2021-02-15 23:07:35 -05:00
Ethan Lee
47df6c9d66 Ignore resize events for unfocused windows 2021-02-14 22:51:07 -05:00
Misa
39abcfa8d9 Fix unreachable code warnings
MSVC complains about these, doesn't seem like GCC does. These can be
safely removed because they're unreachable, and they always follow a
case-switch or similar that has a default case which this code is a
duplicate of anyway. (Unless it isn't, in which case all the better to
remove it, becausee otherwise it looks misleading or confusing to casual
glances at the code.)
2021-02-14 16:48:27 -05:00
Misa
a2ba37a1a4 Fix out-of-bounds indexing with malformed XML entities in find_tag()
find_tag() would commit out-of-bounds indexing if someone made a level
file with malformed XML entity encodings in the metadata tags.

This would happen if the end of the string followed immediately after an
ampersand and hash, or if there wasn't a semicolon ending an XML entity.

Valgrind complains about these, so I've fixed it.
2021-02-11 19:45:33 -05:00
Misa
5de7c180ea Give static storage duration to radix in ss_toi()
Since the value is a const value that reads from an integer literal, it
should be static so we read it directly from storage instead of copying
it.
2021-02-08 09:44:29 -05:00
Misa
09841ef3a5 Don't mutate the decimal place in ss_toi()
This fixes a bug where "12" gets properly evaluated as 12, but "148"
gets evaluated as 1408. It's because `place` gets multiplied by `radix`
again, so `retval` gets multipled by 100 instead of 10.

There's no reason to have a `place` variable, so I've removed it
entirely. This simplifies the function a little bit.
2021-02-08 09:44:29 -05:00
Misa
adbae16666 Clean up script header check in editorclass::load()
The previous person who wrote this (a girl named Misa) clearly didn't
understand the reason why you couldn't compare line[line.length()-1]
directly to a string literal. It's because the former is a char, and the
latter is a pointer to a char. Both are ints, so it compiles fine, but
it doesn't do what you want it to.

Why not just make the latter a char instead of a string literal? Well,
because you can, but also I clearly didn't think this through earlier,
so that's why I didn't do it in the first place.

But this is fixed now.
2021-02-07 18:30:13 -05:00
Misa
4728f64e3a Remove commented-out "version 1" code from editorclass::load()
Search results for terms in this code, such as the split() function,
become annoying false-positives. So it's better to just remove it
entirely.
2021-02-07 18:30:13 -05:00
Misa
06cc5afe27 Pass input of UtilityClass::GCString() by const reference
This avoids an unnecessary copy of the input std::vector, since we don't
need to modify it for anything. This cuts down on unnecessary memory
operations.
2021-02-07 18:30:13 -05:00
Misa
46b0257cf1 Refactor ss_toi() to not use stringstreams
Apart from the std::string, this function no longer uses the STL.

ss_toi() is a simple function - it converts the input into an int,
taking as many digits as possible until it reaches a non-digit
character, at which point it stops. It's trivial to implement this
without the STL.

I could've used Int() here, but that would've required copying the
string to a temporary buffer to insert a null-terminator (we can't just
use a pointer-and-length data type either, the string functions don't
operate like that - one disadvantage of C strings!). Instead, I decided
to implement my own conversion to int here, because I don't think the
way we humans write our Arabic numerals is going to change anytime soon.

Also, the std::string input is now passed by const reference, instead of
making a copy - cutting down on unnecessary memory operations.
2021-02-07 18:30:13 -05:00
Misa
e1ae25b29b Use case-switch inside GCChar() instead of if-else tree
A case-switch here takes up less lines (at least with next-line brace
style) and doesn't repeat the 'button ==' part over and over.
2021-02-07 18:30:13 -05:00
Misa
eea37046ef Add constness to GCChar() input and switch position of asterisk
I personally like putting the asterisk with the type, because despite
the language parsing the asterisk as a part of the name, the pointer
part is clearly a part of the return type of the function. Also,
put constness here, to indicate that the input won't be modified inside
the function.
2021-02-07 18:30:13 -05:00
Misa
3cd18b337a Remove comment from GCChar()
This comment indicates that the function is used by
UtilityClass::GCString(). Which is unnecessary, because the reader can
trivially search for usages of GCChar in the file itself (the 'static'
preceding the function should be a good enough hint) - and if there
aren't any, then the reader will know the function is unused, whereas if
they read the comment, they would have been under the assumption that it
wasn't used. (There might also a compiler warning about it being unused,
which would be more confusing if the comment was still there.)

Point is, comments can get outdated, so removing the comment here makes
the code more self-documenting.
2021-02-07 18:30:13 -05:00
Misa
f9fa56a08a Remove unused cctype #include from UtilityClass.cpp
This header used to be needed for isxdigit(), but ever since we switched
to using our own VVV_isxdigit(), we don't need it anymore.
2021-02-07 18:30:13 -05:00
Misa
ef4418a7cb Remove commented-out code in Game::gameclock()
This code involves stringstreams, which causes a false-positive any time
I ripgrep for stringstream in the codebase.
2021-02-07 18:30:13 -05:00
Misa
3bc3b59378 Re-fix glitchy y-position when spawning onto a conveyor
This is a re-do of 942217f871 (#509), but
with a more conservative fix that only resets the player's newxp and
newyp when they respawn from a checkpoint or spawn in to the map.

Unlike the previous patch, if the player were to suddenly collide with a
conveyor or horizontally-moving platform during gameplay, their
y-position would revert back to the intended next y-position of the
previous frame. But this is the same behavior as before, I haven't ever
seen such a contrived situation come up, and this behavior is probably
more preferable for gameplay than actually going to the conveyor, so
it's fine.

I also decided to reset newxp here, and not just newyp, because while
resetting newyp seems to be enough, it's safer to also reset newxp (and
so future readers won't question why only newyp is reset but not newxp).

I tested this and it once again fixes the death loop issue from earlier,
while also still allowing for that Trench Warfare trick to be possible
(I tested it with the libTAS movie I mentioned in #606; it syncs fine).
There are no other known regressions resulting from this fix
(hopefully).
2021-02-01 19:51:55 -05:00
Misa
a406b99f4b Revert "Fix glitchy y-position when colliding with a conveyor"
This reverts commit 942217f871.

This fix (of a regression of a fix) has a regression where immediately
flipping off of horizontally-moving platforms or conveyors will no
longer provide you with a "boost" given certain vertical pixel
alignments.

The regression that this fix fixed will be fixed another way.

Fixes #606.
2021-02-01 19:51:55 -05:00
leo60228
b5ef10cae5 Consistently use Screen::GetWindowSize 2021-01-18 13:10:22 -05:00
leo60228
46d8599f62 Use SDL_GetRendererOutputSize instead of SDL_GetWindowSize
SDL_GetRendererOutputSize will always return the actual size, even in
some obscure HiDPI/macOS cases.
2021-01-18 13:10:06 -05:00
leo60228
d9fff2a8c3 Set SDL window as DPI-aware
This works on macOS, Wayland, and a few more esoteric platforms. X11
doesn't have a concept of DPI-awareness. Note that with this flag
SDL_GetWindowSize() isn't guaranteed to return the actual window size.
2021-01-18 13:09:47 -05:00
Misa
a32fc4a307 Improve support for retextured checkpoints and terminals in the editor
Retextured checkpoints have always been in the game, but clicking on
them in the editor would lead to them losing their retextured-ness. So,
checkpoints should be left alone if their p1 isn't either 0 or 1. Also,
they don't show up properly in the editor, so that's fixed, too.

Retextured and flipped terminals were added in 2.3, and show up properly
in-game, but don't properly show up in the editor, either. So now they
show up in the editor. Additionally, clicking on them will flip the
terminal as well, but only if its p1 is 0 or 1, just like checkpoints
now do.
2021-01-18 13:07:32 -05:00
Misa
afba320083 Remove duplicate graphics.Makebfont() in main()
This call to Makebfont() always existed, but ever since 2.3's per-level
custom assets were added, graphics.reloadresources() also calls
graphics.Makebfont(), meaning this call is unnecessary. Calling it twice
results in graphics.bfont and graphics.flipbfont having twice the number
of elements, until custom assets get mounted (or unmounted, technically).
2021-01-18 13:06:59 -05:00
Misa
adbab6355b Free data upon failure in LoadImage()
Otherwise, if SDL_CreateRGBSurfaceFrom() returned NULL, then this memory
would be leaked.
2021-01-18 13:06:43 -05:00
Misa
626aac59fb Fix No Death Mode results being reset before being shown
This does the same thing as the last commit, but for No Death Mode
instead of Time Trials. Whenever you die in No Death Mode, or complete
it, all the relevant variables get copied to variables prefixed with
'ndmresult' that never get reset by script.hardreset(), and these
variables are what titlerender() use, instead of the "live" ones.
2021-01-18 13:06:15 -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
07cc5f68ac Remove commented-out code from gamestates 3101 and 3500
Comments in general don't get verified by the compiler, but
commented-out code is even worse. Especially since this looks to be
outdated code.

As always, if we need some of this code, then we can just look back in
the Git history.
2021-01-18 13:06:15 -05:00
Misa
f06701ff90 Fix wrong function being used for musicclass::play() check
Whoops.
2021-01-16 15:37:22 -05:00
Misa
74740c5a21 Remove LODEPNG_NO_COMPILE_ZLIB
This fixes a segfault, because we would then pass compressed image data
to SDL_ConvertSurfaceFormat() in LoadImage(). I didn't test my previous
PR. Whoops.
2021-01-14 06:31:30 -05:00
Misa
9c9433d21a Disable MSVC warnings about implicit conversion
Implicit conversion warnings happen all over the codebase, but there's
no reason to warn on all of them, and adding casts everywhere is
annoying to read and patently unnecessary.

MSVC is the only compiler that has this warning (GCC even on -Wall
-Wextra doesn't warn about implicit conversions), so disable it for
MSVC.
2021-01-14 06:30:22 -05:00
Misa
fad3bab2d0 Initialize bcol and bcol2 in Graphics::drawbackground()
While compiling in release mode, GCC warns about these two potentially
being used uninitialized further down. The only way this could happen is
if the case-switches below didn't match up with a case, which would
require the game to be in an invalid state (and have invalid values for
rcol and spcol), but it's better to be safe than sorry.
2021-01-13 22:47:12 -05:00
Misa
2a781083e9 Disable unneeded LodePNG features
The only thing we need LodePNG for is to decode a PNG that we've already
loaded into memory. We handle the filesystem part ourselves, so we don't
need LodePNG's filesystem functions; we don't encode images, and we
don't use the zlib functions. So disable all of those.
2021-01-13 22:44:52 -05:00
Misa
769f99f590 Reduce dependency on libc functions
During 2.3 development, there's been a gradual shift to using SDL stdlib
functions instead of libc functions, but there are still some libc
functions (or the same libc function but from the STL) in the code.

Well, this patch replaces all the rest of them in one fell swoop.

SDL's stdlib can replace most of these, but its SDL_min() and SDL_max()
are inadequate - they aren't really functions, they're more like macros
with a nasty penchant for double-evaluation. So I just made my own
VVV_min() and VVV_max() functions and placed them in Maths.h instead,
then replaced all the previous usages of min(), max(), std::min(),
std::max(), SDL_min(), and SDL_max() with VVV_min() and VVV_max().

Additionally, there's no SDL_isxdigit(), so I just implemented my own
VVV_isxdigit().

SDL has SDL_malloc() and SDL_free(), but they have some refcounting
built in to them, so in order to use them with LodePNG, I have to
replace the malloc() and free() that LodePNG uses. Which isn't too hard,
I did it in a new file called ThirdPartyDeps.c, and LodePNG is now
compiled with the LODEPNG_NO_COMPILE_ALLOCATORS definition.

Lastly, I also refactored the awful strcpy() and strcat() usages in
PLATFORM_migrateSaveData() to use SDL_snprintf() instead. I know save
migration is getting axed in 2.4, but it still bothers me to have
something like that in the codebase otherwise.

Without further ado, here is the full list of functions that the
codebase now uses:

- SDL_strlcpy() instead of strcpy()
- SDL_strlcat() instead of strcat()
- SDL_snprintf() instead of sprintf(), strcpy(), or strcat() (see above)
- VVV_min() instead of min(), std::min(), or SDL_min()
- VVV_max() instead of max(), std::max(), or SDL_max()
- VVV_isxdigit() instead of isxdigit()
- SDL_strcmp() instead of strcmp()
- SDL_strcasecmp() instead of strcasecmp() or Win32 strcmpi()
- SDL_strstr() instead of strstr()
- SDL_strlen() instead of strlen()
- SDL_sscanf() instead of sscanf()
- SDL_getenv() instead of getenv()
- SDL_malloc() instead of malloc() (replacing in LodePNG as well)
- SDL_free() instead of free() (replacing in LodePNG as well)
2021-01-12 14:02:31 -05:00
Misa
b944d2d847 Fix editor tool menu inconsistencies
This patch de-duplicates the tool drawing code a bit in the menu that
gets brought up when you press Space in the level editor, as well as
fixes several bugs related to the fact that the original author(s) of
the code decided to copy-paste everything. (It was most likely Terry,
judging by the distinct lack of whitespace between tokens in the code.)

There are two "pages" of tools that get shown when you open the tool
menu, according to your currently-selected tool.

1. On the first page, your currently-selected tool gets a brighter
   outline. However, on the second page, the code to draw the outline over
   your currently-selected tool is missing. So I've fixed that.

2. On the first page, the glyph indicator next to the tool icon also
   gets brighter when you have that tool selected. However, on the
   second page, the code that drew the brighter-colored indicator got
   ran before the code that drew the normal-colored indicator, so this
   was never shown. This is also fixed.

3. The glyph indicator of the gravity line tool didn't get brighter when
   you had it selected, due to its special-cased copy-pasted code
   drawing its brighter color before drawing its normal color. This has
   also been fixed.

4. Lastly, the tool menu no longer draws the brighter-colored glyphs on
   top of the normal-colored glyphs. Instead, the menu will simply draw
   the brighter-colored glyphs and will not draw the normal-colored
   glyphs in the first place. This is because double-drawing text like
   this will look bad if the user has a custom font.png that has
   translucent pixels, like I do.

All of these bugs have been fixed by paying off the technical debt of
copy-pasting code.
2021-01-12 13:42:16 -05:00
leo60228
89886e6c52
Run CI on CentOS 7 (#574) 2021-01-11 00:30:15 -05:00
Misa
b1558f574c Remove game.savemystats
This variable seems to have been intended to make sure
game.savestatsandsettings() was called at the end of the frame, or make
sure that it didn't get called more than once per frame. I don't see any
frame ordering-related reason why it needs to be called specifically at
the end of the frame (the function doesn't modify any state), so it's
more plausible that it was added to make sure it didn't get called more
than one per frame.

However, upon further analysis, none of the code paths where
game.savemystats is used ever calls or sets game.savemystats more than
once, and a majority of the code directly calls
game.savestatsandsettings() anyway, so there's no reason for this
variable to exist. If we ever need to make sure it doesn't get called
more than once, and there's no way to change the code paths around to
prevent it otherwise, we can use the defer callbacks system that I added
to #535, when it gets merged.
2021-01-11 00:26:14 -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
b571fa0919 Add F9 "reload resources" hotkey to list of hotkeys in Shift menu
2.3's per-level assets feature also added a hotkey to reload the custom
assets of the level you're currently editing in the editor, so you
wouldn't have to re-load the level yourself. This hotkey is F9, but
however, it hasn't been documented in the hotkey list brought up by
pressing Shift, until now.
2021-01-11 00:21:50 -05:00
Misa
e9c62ea9a3 Clean up unnecessary exports and add static keywords
This patch cleans up unnecessary exports from header files (there were
only a few), as well as adds the static keyword to all symbols that
aren't exported and are specific to a file. This helps the linker out in
not doing any unnecessary work, speeding it up and avoiding silent
symbol conflicts (otherwise two symbols with the same name (and
type/signature in C++) would quietly resolve as okay by the linker).
2021-01-10 12:23:59 -05:00
Misa
fdee4007f7 Move gamerenderfixed() in between gameinput() and gamelogic()
Line clipping and second-frame edge-flipping have been broken since #539
was merged (d910c5118d). The cause of this
is moving the onground/onroof code around.

A proper loop order fix is going to come once #535 gets finalized and
merged, so this is a stopgap measure just to make sure people don't
report that line clipping or second-frame edge-flipping are broken in
current builds of 2.3.
2021-01-09 20:16:19 -05:00
Misa
979c5e3aa4 Fix attempts to place enemy/plat bounds & scripts with bad corner order
There is a certain ordering of which corners you click on to place enemy
and platform boundaries, and script boxes. You must first click on the
top-left corner, then click on the bottom-right corner. The visual box
that is drawn after you've first clicked on the top-left corner clearly
shows this intended way of doing things.

However, it seems like despite the visuals, the game didn't properly
prevent you from clicking on the corners in the wrong way. If you placed
it from top-right to bottom-left, or bottom-left to top-right, then the
game would place the boxes accordingly, and they would have a weird
shape where two of its opposite sides would just be missing. But,
placing them from bottom-right to top-left is prevented accordingly.

The bug comes down to a simple use of "or", instead of the correct
"and". This isn't the first time the wrong conjunction was used in a
conditional... (8260bb2696, #136)

Since the code block that the if-statement guards is the code that will
execute if the corners placed were correct, the if-statement thus should
be written in the positive case and use a more restrictive "and",
instead of the negative case, which would use a more looser "or". There
are less cases that are correct than cases which are incorrect - in this
case, there is only 1 correct way of doing things (top-left to
bottom-right), compared to 3 incorrect ways of doing things (top-right
to bottom-left, bottom-left to top-right, bottom-right to top-left) - so
when thinking of positive cases, you should be using "and".

Or, you can always just test it. This bug has been in the game since
2.0, so it seems like no one just tested that incorrect input actually
didn't work.
2021-01-09 11:00:53 -05:00
Misa
d271907f8c Fix Secret Lab Time Trial trophies having wrong colors
Ever since 2.0, the colors of some of the Time Trial trophies in the
Secret Lab don't correspond to the crewmate of the given level. The
trophy for the Tower uses Victoria's color, and the Lab trophy uses
Vermilion's color. The Space Station 2 trophy uses Viridian's color, and
the Final Level trophy uses Vitellary's color.

This doesn't appear to be intentional, and it would be odd if it was,
since this game matches the colors everywhere else (each zone on the map
is colored with their respective crewmate in mind, for instance). Also,
the Lab trophy has the sad expression, which is Victoria's trait - it
would be weird if this was intended for Vermilion instead.

But the biggest piece of evidence that this was unintentional is the
corresponding comment for each color in Graphics::setcol(). It mislabels
yellow as cyan, cyan as yellow, blue as red, and red as blue.

To fix this, I simply have to set the correct color for each trophy in
case 25 of entityclass::createentity(). I could fix it in
Graphics::setcol() itself, but custom levels might depend on those
certain colors being the way they are, so it's a safer bet to just fix
it in the trophy creation case itself.

The diff of this might look weird. Even though all I'm doing is changing
some value assignments around, it looks like the "patience" algorithm
thinks I'm moving a whole case of the trophy switch-case around.
2021-01-08 15:17:36 -08:00
Misa
952a130595 Fix Shift+F1 from Space Station tileset not switching to Ship
So... it looks like being able to switch through tilesets backwards has
been in 2.3 for a while, guess no one just uses 2.3 or the level editor
that much. It seems like it's always been broken, too.

If you were on the Space Station tileset (tileset 0), pressing Shift+F1
would keep you on the Space Station tileset instead of switching to the
Ship (tileset 4).

It looks like the problem here was mixing size_t and int together - so
the modulus operation promoted the left-hand side to size_t, which is
unsigned, so the -1 turned into SIZE_MAX, which is 18446744073709551615
on my system. You'll note that that ends in a 5, so the number is
divisible by 5, meaning taking it modulo 5 leads to 0. So the tileset
would be kept at 0.

At least unsigned integer underflow/overflow is properly defined, so
there's no UB here. Just careless type mixing going on.

The solution is to make the modulus an int instead of a size_t. This
introduces an implicit conversion, but I don't care because my compiler
doesn't warn about it, and implicit conversion warnings ought to be
disabled on MSVC anyway.
2021-01-08 18:13:45 -05:00
Misa
3dd1dcc131 Move mapclass r/g/b variables off onto TowerBG
This fixes a bug where if you entered a tower before watching the
credits sequence, the credits sequence would have mismatched text and
background colors.

This bug happened because entering a tower modified the r/g/b attributes
of mapclass, and updated graphics.towerbg, without updating
graphics.titlebg too. Then gamecompleterender() uses the r/g/b
attributes of mapclass.

The solution is to put the r/g/b attributes on TowerBG instead. That
way, entering a tower will only modify the r/g/b attributes used to
render towers, and won't affect the r/g/b attributes used to render the
credits sequence.

Additionally, I also de-duplicated the case-switch that updated the
r/g/b attributes based off of the current colstate, because it got
copy-pasted twice, leading to three instances of one piece of code.
2021-01-07 21:15:34 -05:00
Misa
6b18e3875d Move title screen color initialization to main()
This fixes a bug where if you completed a custom level during
command-line playtesting, when returning to the title screen, the
background would be red and the text would be white.

This is because playtesting skips over the code path of pressing ACTION
to start the game and advance to the title screen, and the code path of
that ACTION press specifically initializes the title screen colors to
cyan.

This is also caused by the fact that completing a custom level doesn't
call map.nexttowercolour(), but my guess is that the intent there was
that the player would select a custom level, complete it, and return to
the title screen on the same screen with the same colors, so I decided
not to add a map.nexttowercolour() there.

Instead, I've moved the cyan color initialization to main(), so that it
is always executed no matter what, and doesn't require you to take a
specific code path to do it.
2021-01-05 21:20:58 -05:00
Misa
5e557ffd1a Fix regression with playing the same track after it fades out
With commit 48313169b6 (PR #453),
AllyTally added a single-case patch for a regression, instead of fixing
it at its root cause.

In fact, that commit only fixes the music if Presenting VVVVVV is
playing while exiting to the menu, not if you enter a level that plays
Presenting VVVVVV - so it only fixes it going one way, and not going the
other way around; neither fixing also all the other cases this could
happen.

It doesn't, say, fix the case where you are exited to the menu
automatically after collecting the last crewmate in the level (or if the
level calls gamestate 1013 itself), which is what happens in my MIRA-VIU
TAS video at the end, and which I noted in the description of that video
( https://www.youtube.com/watch?v=OYQO4ePbYW4&t=111 ).

So, the problem here is that when musicclass::play() is called, it sees
that currentsong is the same as its input, and decides that since the
music is already playing, it shouldn't play the music again. Thus, the
music fades out, and we get silence instead of the music playing again.

But I said this was a regression. Why didn't this happen in 2.2? Well,
it's because of the fact that 2.2 sets currentsong to -1 (no music
playing at all) immediately when starting a fadeout, and not when the
fadeout completes (commit facb079b35,
PR #316). As you can imagine, this discrepancy could lead to bugs, given
that the game would think that music wasn't playing when in actuality it
was, but fixing this bug could also break code that expected this wrong
behavior. And in this case, it has.

So to properly fix the root cause of this, instead of naïvely
single-case patching out every case that comes up randomly, in
musicclass::play(), the function will now ignore if the input given is
the same as currentsong if the music is currently fading out.
2021-01-04 19:16:35 -05:00
Misa
d5763640a8 Revert hardcoded check for track 6 when quitting to menu
This reverts commit 48313169b6, "Don't
fade music out when returning to the menu if it's Presenting VVVVVV".

This commit is being reverted because it is only a single-case patch -
that is, it fixes only a single symptom of the bug, and not its
underlying cause.
2021-01-04 19:16:35 -05:00
Misa
8e5714439a Unindent musicclass::play() from previous two commits
As always, the actual unindenting happens in a separate commit in order
to minimize diff noise.
2021-01-04 05:09:23 -05:00
Misa
f64cf4f831 Invert t comparison with -1 and un-nest rest of function
This is also another conditional where the rest of the function is
nested. Furthermore, in order to not repeat ourselves, I've also decided
to unconditionally assign currentsong to t, because if t is -1
currentsong gets assigned to -1 anyway - so it's the same thing, but
it's much easier to see and think about.
2021-01-04 05:09:23 -05:00
Misa
fe5bacfdc2 Invert currentsong check and un-nest rest of function
This removes an indentation level, and makes it easier to reason about
the function since you essentially now view it as the function returning
right there.
2021-01-04 05:09:23 -05:00
Misa
96c479a11f Fix whitespace inconsistency in musicclass::play()
Spaces are used around operators and keywords accordingly, and blank
lines are used to visually separate lines of code from each other.
2021-01-04 05:09:23 -05:00
Misa
ff3e390352 Remove unused function editorclass::warpzonematch()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
a5e5c19913 Remove unused function editorclass::warpzoneedgetile()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
54ff36da97 Remove unused function Graphics::textboxmove()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
ca904a4d7c Remove unused function Graphics::textboxcenter()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
b3305e4820 Remove unused function SoundSystem::playMusic()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
945961c1fb Remove unused function editorclass::placetile()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
916a19c09c Remove unused function KeyPoll::isUp()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
ad34e95128 Remove unused function entityclass::fixfriction()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
a656f4c4d8 Remove unused function edentclear()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
9b3bf69491 Remove unused function Graphics::drawentcolours()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
6261fa51e9 Remove unused function Graphics::RPrint()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
42106cb59a Remove unused function Graphics::PrintOff()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
Misa
0e0f5c47a5 Remove unused function FlipSurfaceHorizontal()
This function was marked as unused by cppcheck.
2021-01-02 09:06:42 -05:00
leo60228
6795856431 Use SDL_fabsf where std::abs was previously used 2021-01-01 22:37:46 -05:00
leo60228
91c3e6cbc5 Revert "Document SDL_abs vs. SDL_fabs"
This reverts commit 8c472ed73d.
2021-01-01 22:37:46 -05:00
leo60228
8c472ed73d Document SDL_abs vs. SDL_fabs 2021-01-01 21:10:16 -05:00
leo60228
c4893a133f Use SDL_abs instead of std::abs
This prevents issues when calling std::abs with a float on some older
compilers. While it would normally be promoted to an int, std::abs is
special due to being overloaded despite being a C function. This can
cause errors due to the compiler being unable to find a float overload.
SDL_abs doesn't have this problem, since it's a normal C function.
2021-01-01 21:10:16 -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
c52a274a7a Reset invis and lifeseq when loading in, in mapclass::resetplayer()
When I did #569, I forgot that taking out the code path that set the
player's invis to false meant that the player would still be invisible
upon loading back in to the game if they exited the game while
invisible. Taking out that code path also meant that if game.lifeseq was
nonzero, it wouldn't be reset properly, either. So this fixes those
things.
2020-12-28 16:43:13 -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
eb7b540346 Fix no-draw frames when exiting a level with custom assets
When exiting a level, music.init() gets called again, and every time it
gets called after the first time it gets called, it will free all music
tracks.

To do so, it calls Mix_FreeMusic(). Unfortunately, if there is music
fading, Mix_FreeMusic() will call SDL_Delay(), which will result in
annoying no-draw frames. Meaning, the screen freezes and doesn't draw
anything, and has to wait a bit before continuing.

Here's the relevant piece of code from SDL2_mixer's music.c:

    if (music == music_playing) {
        /* Wait for any fade out to finish */
        while (music->fading == MIX_FADING_OUT) {
            Mix_UnlockAudio();
            SDL_Delay(100);
            Mix_LockAudio();
        }
        if (music == music_playing) {
            music_internal_halt();
        }
    }

This is especially annoying if you're a TASer, because no-draw frames in
a libTAS movie aren't guaranteed to last for a consistent number of
frames when you change your inputs around.

After this patch, as long as your computer can unmount and re-mount
assets fast enough (it doesn't seem like mine can, unfortunately), then
you won't get any freezes when exiting a level that has custom assets.
(This freeze didn't happen when loading a level because the title screen
music fadeout upon pressing ACTION had enough time to fully complete
before the level got loaded.)
2020-12-28 16:01:05 -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