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

1234 commits

Author SHA1 Message Date
Misa
6f4e89e336 Remove unneeded tdrawback/backgrounddrawn assigns in returntopausemenu
Assigning these variables is now wholly unnecessary ever since #522 got
merged, and in fact setting graphics.backgrounddrawn to false actually
causes the warp background to "skip" when the map screen gets closed. So
this fixes that bug, too.
2020-11-07 16:25:04 -05:00
Misa
5759408ded Remove Game::shouldreturntopausemenu
This kludge variable was used to re-set the warp background after coming
back from the in-game settings menus. But since #522 got merged, this
has no longer been necessary.
2020-11-07 16:25:04 -05:00
Misa
d997fae27a Use SDL_fmodf() instead of libc fmodf()
Always good to use the SDL stdlib where possible.
2020-11-06 20:53:05 -05:00
Misa
5faa86baae Add x-room = 13 check to entity 1 behave 13
This check is clearly meant for destroying the factory clouds in the
room "Level Complete!" in the main game, but it covers all rooms on row
8, instead of only (13,8). Adding an x-room check restricts this
behavior to only (13,8).

Trinket9 reported that this weird behavior of destroying specifically
above y-position 60 was undesirable, since they were creating an enemy
with this `behave` in a room on row 8 and it kept disappearing
instantly.
2020-11-06 15:06:11 -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
Misa
87af9bba04 Merge fadeoutqueuesong into nicechange
fadeoutqueuesong basically does the same thing as nicechange - they both
queue a song to be played when the current track is done fading out.
Except, for some reason, I decided to add fadeoutqueuesong instead of
using the existing nicechange/nicefade system.

This has consequences where fadeoutqueuesong would step on the toes of
nicechange. In the case of #390, nicechange would say "let's play
Potential for Anything" when entering the Super Gravitron, but
fadeoutqueuesong had previously said "let's play Pipe Dream" because of
the player having just exited the Super Gravitron. fadeoutqueuesong took
priority because it came first in musicclass::processfade(), and when it
called play(), the Mix_PlayingMusic() in the nicechange check afterwards
would say music would already be playing at that point, so the
nicechange wouldn't take effect.

In the end, the solution is to just merge the new system into the
already-existing system.

Fixes #390.
2020-11-06 06:09:31 -05:00
Misa
410d80b731 Fix song 0/7 check using num_pppppp_tracks instead of num_mmmmmm_tracks
Just looked over this and had to do a double-take. It should be
num_mmmmmm_tracks, not num_pppppp_tracks, because MMMMMM comes first in
the vector of music tracks.
2020-11-06 06:09:31 -05:00
Misa
9698b42432 Initialize nicechange to -1 for consistency
The game uses -1 to denote "no song" elsewhere, especially when a
niceplay() has finished processing and it sets nicechange to -1 there,
too.
2020-11-06 06:09:31 -05:00
Misa
c1ca57e096 Change nicefade to bool instead of int
If it's treated like a bool, why shouldn't it be one?
2020-11-06 06:09:31 -05:00
Misa
f0b9bdc007 Fix niceplay() check having hardcoded number of MMMMMM tracks
The number of MMMMMM tracks is no longer guaranteed to be 16 anymore,
and instead it should use num_mmmmmm_tracks.
2020-11-06 06:09:31 -05:00
Misa
93775ecde2 Fix ACTION press processing on same frame as fade ticks to 0
Here's what causes #401: After the fade to menu delay ticks down to 0,
the game calls game.quittomenu(), but the rest of mapinput() still
executes. This means that the block that detects your ACTION press gets
executed, because there's a check that fadetomenudelay is less than or
equal to 0, and, well, it is.

So if you've pressed ACTION on the exact frame that it counts down to 0,
then the game detects your ACTION press, then processes it accordingly,
and then sets the fadetomenudelay, which means it'll get reactivated the
next time you open the map screen. But at this point, you get sent to
TITLEMODE, because game.quittomenu() set game.gamestate accordingly.
(This is why resetting game.fadetomenu or game.fadetomenudelay in
game.quittomenu() or script.hardreset() won't fix this bug.)

The solution here is to add a game.fadetomenu check to the ACTION press
processing.

Same-frame state transition logic is hard... actually, any sort of thing
where two things happen on the same frame is really annoying.

This also applies to fadetolab and fadetolabdelay, too.

Fixes #401.
2020-11-06 06:06:45 -05:00
Misa
5aa8b99113 Move all settings to settings.vvv
The game previously did this dumb thing where it lumped in all its
settings with its file that tracked your records and unlocks,
`unlock.vvv`. It wasn't really an issue, until 2.3 came along and added
a few settings, suddenly making a problem where 2.3 settings would be
reset by chance if you decided to touch 2.2.

The solution to this is to move all settings to a new file,
`settings.vvv`. However, for compatibility with 2.2, settings will still
be written to `unlock.vvv`.

The game will prioritize reading from `settings.vvv` instead of
`unlock.vvv`, so if there's a setting that's missing from `unlock.vvv`,
no worries there. But if `settings.vvv` is missing, then it'll read
settings from `unlock.vvv`. As well, if `unlock.vvv` is missing, then
`settings.vvv` will be read from instead (I explicitly tested for this,
and found that I had to write special code to handle this case,
otherwise the game would overwrite the existing `settings.vvv` before
reading from it; kids, make sure to always test your code!).

Closes #373 fully.
2020-11-04 12:06:57 -05:00
Misa
9e1ba97f63 Fix style of showmousecursor boolean check
Same-line brace (but not previous-line brace) AND an unnecessary boolean
comparison to constant boolean? Two-for-one whammy.
2020-11-04 12:06:57 -05:00
Misa
27b28ca55e Fix oldreadytotele not being set in gotoroom()
This fixes a deltaframe glitch where the "- Press ENTER to Teleport -"
prompt would show up for a split second if you exited the game while the
prompt was fully faded in, and then re-entered it.
2020-11-04 08:39:26 -05:00
Misa
3ce442459e Fix conveyor check in collisioncheck() always being true
cppcheck said: "Logical disjunction always evaluates to true".

Yes. Yes it does. Whoops. I learned how to specify ranges like this in
high school math and still screw it up...
2020-11-04 08:38:54 -05:00
Misa
3434ad8777 Fix variables shadowing other variables
In C++, when you have two variables in different scopes with the same
name, the inner scope wins. Except you have to be really careful because
sometimes they're not (#507). So it's better to just always have unique
variable names and make sure to never clash a name with a variable in an
outer scope - after all, the C++ compiler and standard might be fine
with it, but that doesn't mean humans can't make mistakes reading or
writing it.

Usually I just renamed the inner variables, but for tx/ty in editor.cpp,
I just got rid of the ridiculous overcomplicated modulo calculations and
replaced them with actual simple modulo calculations, because the
existing ones were just ridiculous. Actually, somebody ought to find
every instance of the overcomplicated modulos and replace them with the
actual good ones, because it's really stupid, quite frankly...
2020-11-04 08:38:19 -05:00
Misa
6de14d95ee Don't unset Flip Mode when deleting all data in M&P
Whenever you delete all your save data, your settings aren't changed at
all, and you could resave them if you fiddled with a setting somewhere.
But the full game doesn't count Flip Mode as a setting, instead it
counts it as an unlock. This means deleting your save data would unset
Flip Mode in M&P, which would seem weird because in M&P it's just a
simple setting.

For consistency, Flip Mode shouldn't be unset when deleting save data in
M&P.
2020-11-04 01:00:49 -05:00
Dav999-v
4ebf89e844 Add error message if quicksave fails 2020-11-03 22:05:26 -05:00
Dav999-v
5dbb90c7fd Add error message if telesave fails
At least, whenever it would say "Game Saved", it now instead gives an
error message if saving is not successful.
2020-11-03 22:05:26 -05:00
Misa
4570140a6a Fix teleporter sprites sometimes being solid colors
This commit fixes a bug that also sometimes occurred in 2.2, where the
teleporter sprite would randomly turn into a solid color and just be a
solid circle with no detail.

Why did this happen? The short answer is an incorrect lower bound when
clamping the teleporter sprite index in `Graphics::drawtele()`. The long
answer is bad RNG with the teleporter animation code. This commit fixes
the short answer, because I do not want to mess with the long answer.

So, here is what would happen: the teleporter's `tile` would be 6. The
teleporter code decrements its `framedelay` each frame. Then when it
reached a `framedelay` of 0, it would call `fRandom()` and essentially
ask for a random number between 0 and 6. If the RNG ended up being
greater than or equal to 4, then it would set its `walkingframe` to -5.
At the end of the routine, the teleporter's `drawframe` ends up being
its `tile` plus its `walkingframe`. So having a `walkingframe` of -5
here is fine, because its `tile` is 6.

Until it isn't. When its `tile` becomes 2, it still keeps its
`walkingframe` around. The code that runs when its `tile` is 2 does have
the possibility of completely resetting its `walkingframe` to be in
bounds (in bounds after its `tile` is added), but that only runs when
its `framedelay` is 0, and in the meantime it'll just use the previous
`walkingframe`.

So you could have a `walkingframe` of -5, plus a `tile` of 2, which
produces a `drawframe` of -3. Then `Graphics::drawtele()` will clamp
that to 0, which just means it'll draw the teleporter backing, and the
teleporter backing is a simple solid color, so the teleporter will end
up being completely and fully solid.

To fix this, I just made `Graphics::drawtele()` clamp to 1 on the lower
bound, instead of 0. So if it ever gets passed a negative teleporter
index, it'll just draw the normal teleporter sprite instead, which is
better.
2020-11-03 19:12:31 -05:00
Misa
25d00b9ba6 Fix crewmates being drawn behind other entities
This fixes the draw order by drawing all other entities first, before
then drawing all humanoids[1] after, including the player afterwards.

This is actually a regression fix from #191. When I was testing this, I
was thinking about where get a crewmate in front of another entity in
the main game, other than the checkpoints in Intermission 1. And then I
thought about the teleporters, because I remember the pre-Deep Space
cutscene in Dimension Open looking funny because Vita ended up being
behind the teleporter. (Actually, a lot of the cutscenes of Dimension
Open look funny because of crewmates standing behind terminals.)

So then I tried to get crewmates in front of teleporters. It actually
turns out that you can't do it for most of them... except for Verdigris.
And then that's what I realized why there was an oddity in WarpClass.cpp
when I was removing the `active` system from the game - for some reason,
the game put a hole in `obj.entities` between the teleporter and the
player when loading the room Murdering Twinmaker. In a violation of
Chesterton's Fence (the principle that you should understand something
before removing it), I shrugged it off and decided "there's no way to
support having holes with my new system, and having holes is probably
bad anyway, so I'm going to remove this and move on". The fact that
there wasn't any comments clarifying the mysterious code didn't help
(but, this *was* 2.2 code after all; have you *seen* 2.2 code?!).

And it turns out that this maneuver was done so Verdigris would fill
that hole when he got created, and Verdigris being first before the
teleporter would mean he would be drawn in front of the teleporter,
instead of being behind it. So ever since
b1b1474b7b got merged, there has actually
been a regression from 2.2 where Verdigris got drawn behind the
teleporter in Murdering Twinmaker, instead of properly being in front of
it like in 2.2 and previous.

This patch fixes that regression, but it actually properly fixes it
instead of hacking around with the `active` system.

Closes #426.

[1]: I'm going to go on a rant here, so hear me out. It's not explicitly
stated that the characters in VVVVVV are human. So, given this
information, what do we call them? Well, the VVVVVV community (at least
the custom levels one, I don't think the speedrunning community does
this or is preoccupied with lore in the first place) decided to call
them "villis", because of the roomname "The Villi People" - which is
only one blunder in a series of awful headcanons based off of the
assumption that the intent of Bennett Foddy (who named the roomnames)
was to decree some sort of lore to the game. Another one being
"Verdigris can't flip" because of "Green Dudes Can't Flip". Then an OC
(original character) got named based off of "The Voon Show" too. And so
on and so forth.
2020-11-03 13:31:56 -05:00
Misa
733d0dad80 Add entclass::ishumanoid()
"Humanoid" is just a word for "crewmate or player" but without having to
say "crewmate or player". This is just to make it so humanoids get drawn
after all other entities get drawn, meaning humanoids will be drawn on
top.
2020-11-03 13:31:56 -05:00
Misa
d05fbe8687 Unindent drawentity() from previous commit
This unindent is done in a separate commit to minimize diff noise.
2020-11-03 13:31:56 -05:00
Misa
e809cc0615 Factor out entity drawing to separate function
This makes it easy to re-use without duplicating code.
2020-11-03 13:31:56 -05:00
Misa
fd53278163 Add bounds check to drawgravityline()
I noticed that this function didn't have a bounds check, so I added one.
Just in case.
2020-11-03 13:31:56 -05:00
Misa
081030fb2f Make some variables const in drawentities()
I'm going to refactor drawing an entity out to a separate function, and
since I'm going to do that, I might as well make some things const to
clarify intent first and foremost and possibly improve performance or
compiler optimization.
2020-11-03 13:31:56 -05:00
Misa
688b23b85d Standardize on using reference to tilesvec/spritesvec instead of pointer
This cleans up the code readability a bit, as it's nicer to use
reference syntax when indexing an array instead of that ugly pointer
syntax.
2020-11-03 13:31:56 -05:00
Misa
5eab074e4d Remember loaded custom level and read from it when saving
My previous custom level forwards compatibility would only work if you
saved to the same filename as you read from. But what if you saved to a
new filename? Well, your extra XML is lost.

Not to worry, I've introduced a variable that remembers the filepath of
the currently-loaded level (the existing `filename` attribute is kind of
weird and not really suited for this purpose, so). I tried to make it a
simple `const char*`, but it turns out that's bad when you take the
`c_str()` of an `std::string` that then gets destroyed, so it's best to
use `std::string` in an `std::string` world.

So now when you save a level, it'll attempt to open the original file,
and if that doesn't exist then your extra XML gets lost anyway, but I
shouldn't be expected to keep your XML if you delete the original file.
2020-11-03 13:30:53 -05:00
Misa
3f954a169a Add XML forwards compatibility to custom level files
Custom level files now have forwards compatibility - except that some
XML objects will simply discard contents and attributes they don't see
fit. For instance, edentities and level properties will not preserve
newer attributes or contents.

This is because preserving them would require having to track the XML
object as part of the edentity internally, because the edentity might
change places or even be deleted throughout the course of editing
someone's level.

I opted to not add support for preserving objects like these, because
frankly, the put-everything-in-one-file level format was and still is a
terrible idea, and we should probably switch to a new format in the
future that isn't single-file. So when that happens, there won't be a
need to preserve XML attributes on edentities.
2020-11-03 13:30:53 -05:00
Misa
ab446790e8 Axe BoolToString() in favor of int casts
When I encountered this function, I asked myself, why make a dedicated
function instead of casting to int?

Well, as it turns out, you can't just cast to int, because you have to
convert it to a string by doing help.String(number).c_str(), like all
the other ints. So it's actually more wasteful to do it the normal way
instead of using BoolToString().

That is, until my recent changes came along and made it so that you can
just provide an int and it'll automatically be converted to a string, no
questions asked. Now, it's more optimal to do a straight cast to int
instead of having to go through a middleman function. So this function
is getting axed in favor of casting to int instead.
2020-11-03 13:30:53 -05:00
Misa
58795a1381 Add XML forwards compatibility to custom level quicksaves
Now these files might have things added to them in the future? I'm not
so sure. But at least they're XML forwards-compatible now.
2020-11-03 13:30:53 -05:00
Misa
0eb39644c1 Add XML forwards compatibility to tsave.vvv and qsave.vvv
These files are probably not of much consequence. The main game isn't
going to have things to be added to it anytime soon.
2020-11-03 13:30:53 -05:00
Misa
17b8d0308e Add XML forwards compatibility to unlock.vvv
This file is probably the biggest one, as there will be more settings
added in the future and we don't want people's settings to be erased. Of
course, this file will be migrated to a settings.vvv sometime later in
2.3 in order to prevent 2.2 from erasing 2.3 settings.
2020-11-03 13:30:53 -05:00
Misa
43e57f5483 Add XML forwards compatibility to levelstats.vvv
Not that hard to do for the smallest XML file in the game.
2020-11-03 13:30:53 -05:00
Misa
cb2f72fd8e Add XMLUtils.cpp and XMLUtils.h
These XML functions will be useful for de-duplicating copy-pasted XML
handling code, while also making it so elements will get updated in
place instead of being thrown out and starting from scratch every time a
file is saved.
2020-11-03 13:30:53 -05:00
Misa
a1957fa518 Remove kludge_to_bg() and bg_to_kludge()
Now that tower, title, and horizontal/veritcal warp backgrounds all use
separate buffers, there's no longer any need to temporarily store
variables as a workaround for the buffers stepping on each other.
2020-11-03 13:25:03 -05:00
Misa
bda92ad0bd Move horizontal/vertical warp backgrounds to separate buffers
Instead of using the same tower buffer that gets used for towers, use a
separate buffer instead so there's no risk of stepping on the tower
buffer's toes at the wrong point in time.

This commit combined with the previous one fixes #369.
2020-11-03 13:25:03 -05:00
Misa
70f3d457dd Move title usages of towerbg to titlebg
With the previous commit in place, we can now simply move some usages of
the previous towerbg to use a separate object instead. That way, we
don't have to mess with a monolithic state, or some better way to phrase
what I just said, and we instead have two separate objects that can
coexist side-by-side.
2020-11-03 13:25:03 -05:00
Misa
72c048d71e Refactor tower background to use a separate object instead
Previously, the tower background was controlled by a disparate set of
attributes on Graphics and mapclass, and wasn't really encapsulated. (If
that's what that word means, I don't particularly care about
object-oriented lingo.) But now, all relevant things that a tower
background has has been put into a TowerBG struct, so it will be easy to
make multiple copies without having to duplicate the code that handles
it.
2020-11-03 13:25:03 -05:00
Misa
20207e2098 Remove unused attributes author/description/title from editorclass
The author, description, and title of the level are actually stored on
the EditorData instance. These attributes do nothing and should be
removed.
2020-11-03 12:00:08 -05:00
Misa
d9e3e523b7 Move check/cmode temporary vars off of mapclass
Yet another set of temporary variables is on a global class when they
shouldn't be. These two are only used in tower background functions and
are never used anywhere else, so they're clearly temporaries.
2020-11-02 19:53:44 -05:00
Misa
8d8a5d8f57 Fix copy-paste error with SetSurfaceBlendMode of towerbuffer_lerp
Whoops. But I'm not sure if not explicitly setting the blend mode of
towerbuffer_lerp actually did anything bad or not.
2020-11-02 16:26:39 -05:00
Misa
20e02f014b Move temporary bcol/bcol2 off of Graphics
Looks like I had missed yet another two temporary variables that were
actually globals on the Graphics class. Glad I caught these ones.
2020-11-02 16:05:52 -05:00
Misa
25ae117f6a Remove unused attribute Graphics::tl
For some reason, this `tl` is a `point`? But the only other time the
name `tl` is used elsewhere in the code is a float on a `textboxclass`.
Regardless, this is unused.
2020-11-02 16:05:52 -05:00
Misa
06103cc4ca Remove now-unused function mapclass::RGB()
This function was only used in assigments to mapclass::towercol. But
that variable is unused, and has been removed, so after removing that
variable, this one is unused, too.
2020-11-02 15:22:10 -05:00
Misa
9a9f3f57d5 Remove unused variable mapclass::towercol
This variable is written to, but is never actually read. Writing to a
variable doesn't really do anything unless you read from it somewhere
else.
2020-11-02 15:22:10 -05:00
Misa
f847ec7c59 Fix tower background interpolation when scrolling from top
On the deltaframes of the tower background, there would be "pixel bleed"
if the tower background would be scrolling from the top. This is because
there wouldn't be any more pixels from above the screen to grab, because
the background rendering functions didn't draw any pixels above the
screen. But they couldn't draw any pixels above the screen, because that
was simply the end of the buffer. But now that the buffer is expanded,
we can now draw above the screen, and fix this glitchy interpolation
rendering.
2020-11-02 14:02:00 -05:00
Misa
b98c99fd7a Add 8 pixels of padding above and left of towerbuffer[_lerp]
In order to fix the weird title screen pixels at the top on deltaframes,
we'll need to have a bit more space at the top. Also to the left, in
case we need a background to scroll from the left in the future.
2020-11-02 14:02:00 -05:00
Misa
39c88a3c1c Clean up bounds checks and style for customifflag/flag/ifflag/ifcrewlost
These commands now use the INBOUNDS_ARR() macro to convey intent, and to
make sure that if the size of the array changes in the future, that the
bounds check wouldn't end up being wrong. Also fixed some code style for
the flag() and ifcrewlost() commands.
2020-11-02 13:27:04 -05:00
Misa
61fc06bc3b Disable hardcoded (10,5) no-follow rule in custom levels
Scripting crewmates apparently have a specific hardcoded rule in their
follow-player code that makes it so if they're in (10,5) and are to the
left of the line x=155, they will refuse to continue following the
player. This was clearly done to make it so Vitellary in the main game
wouldn't overlap the teleporter, and that was clearly done to make it so
it wouldn't look like he would go behind the teleporter, which would
look weird (I also noticed this in #513).

I stumbled across this code and thought that just like other weird
main-game code that shouldn't apply in custom levels (#136, #137, #144),
this should be fixed, too.
2020-11-01 23:15:00 -05:00
Misa
c590c4b436 Reload window icon when mounting and unmounting assets
The window icon is simply another asset that can be customized by level
makers. And in fact, one of my levels changes the window icon. It's
simply named VVVVVV.png, but it doesn't sit in the graphics folder,
rather it sits in the root VVVVVV directory.

I noticed that this asset was missed when per-level assets loading was
added, so I decided to add it in.

There's a NULL check on screenbuffer because reloadresources() gets
called before screenbuffer's init() does.
2020-11-01 13:59:13 -05:00
Misa
6d8b8d08b9 Factor out icon loading to Screen::LoadIcon()
This is so it can be called multiple times without having to duplicate
code.
2020-11-01 13:59:13 -05:00
Misa
c371d30509 Re-add original oldxp/oldyp assignments in gotoroom()
The intent of #504 was to make it so oldxp/oldyp would never be messed
with for over-30-FPS stuff, but I forgot that I changed these
assignments in my over-30-FPS patch when I was doing #504. So these
assignments have been restored to the way they were in 2.2, and is
fixed now.
2020-11-01 08:30:07 -05:00
Misa
3a8b381dbc Fix lerpoldxp/yp not being updated in setenemy and setenemyroom
I forgot to fix these when I was doing #504.
2020-11-01 08:29:33 -05:00
Misa
f95dbd8426 Disable nocollisionat() for conveyors
While my previous commit fixes the glitchy y-position when you get stuck
inside a conveyor, I noticed that getting inside a conveyor seems to
always push the player out, despite conveyors sharing the same code with
moving platforms, which has code to temporarily disable their own
collision when the player gets stuck inside them, so that the player
DOESN'T get pushed out.

Well, it turns out that the reason this happens is because conveyors in
a room that get placed during mapclass::loadlevel() get tile 1 placed
underneath them. This is mostly so moving platforms will collide with
them, because otherwise platforms don't collide with other platforms,
and a conveyor is considered a platform.

This means that a conveyor without any tiles behind it will simply get
the player stuck if they get inside it, and the player won't be pushed
out. This is bad, because conveyors don't move, so they'll be stuck
there forever until they press R (or save, quit, and load). This
situation doesn't come up in the main game, but it COULD come up in
custom levels that use the internal createentity() command to create
conveyors that don't have any tiles behind them.

It seems good to fix this as well, while we're at it fixing conveyor
physics, so I'm fixing this as well.
2020-10-31 19:07:58 -04:00
Misa
942217f871 Fix glitchy y-position when colliding with a conveyor
There is this issue with conveyors where if you collide with them, your
intended next y-position doesn't get updated to the position of the
conveyor, and then your y-position gets set to your intended next
y-position. This also applies to horizontally moving platforms.

This bug used to not produce any problems, if at all, until #502 got
merged. Since then, respawning from checkpoints that are on conveyors
would sometimes not update your y-position at all, making it possible to
get stuck inside a death loop that would require you to exit the game
and re-enter it.

But you can always reliably create this bug simply by going into the
editor and placing down a conveyor and checkpoint on top of each other.
Then enter and exit playtesting a bunch of times, and you'll notice the
glitchy y-position Viridian keeps taking on.

The root cause of this is how the game moves the player whenever they
stand on the top or bottom of a conveyor (or a horizontally moving
platform). The game sets their intended next x-position (newxp), then
calls obj.entitymapcollision() on them. This would be okay, except their
intended next y-position (newyp) doesn't get set along with their newxp,
so entitymapcollision() will use the wrong newyp, then find that there
is nothing that will collide with the player at that given newyp, then
update the yp of the player to the wrong newyp.

So, the platform logic simply doesn't set the player's newyp. Why does
the player have the wrong newyp? It's because moving platforms (and
conveyors) are updated first before all other entities are, and this
includes the code that checks the player for collisions with moving
platforms. That's right: the moving platform collision code gets ran
before the player properly gets updated. This means that the game will
use the newyp of the previous frame, which could be anything, really,
but it most likely just means the intended next y-position of the player
gets canceled, leaving the player with the same y-position they had
before.

Okay, but this bug only seems to happen when you put a checkpoint inside
a conveyor (or a moving platform that hasn't started moving yet) and
start playtesting from it, so why doesn't this bug happen more often,
then? Well, it's probably because of luck and coincidence. Most of the
time, if you're colliding with a conveyor or horizontally moving
platform, you probably have a correct newyp from the previous frame of
the game, so there'd be no problems. And before #502 got merged, this
previous frame would be provided by the player having to fall to the
surface due to the y-offset of their savepoint. However, if you make it
so that you immediately teleport on to a conveyor (because you died),
then this bug will rear its ugly head.
2020-10-31 19:07:58 -04:00
Dav999-v
70e82dfe12 Fix "name lookup of ‘i’ changed" warning in editor.cpp
I got this warning during compilation because there were two nested for
loops both defining i. Better to have different names to make sure some
compilers won't overwrite the outer variable with the inner one.
2020-10-13 22:39:12 -04:00
Misa
b8a4b4dfe7 Restore previous oldxp/oldyp variables in favor of lerpoldxp/lerpoldyp
I was investigating a desync in my Nova TAS, and it turns out that
the gravity line collision functions check for the `oldxp` and `oldyp`
of the player, i.e. their position on the previous frame, along with
their position on the current frame. So, if the player either collided
with the gravity line last frame or this frame, then the player collided
with the gravity line this frame.

Except, that's not actually true. It turns out that `oldxp` and `oldyp`
don't necessarily always correspond to the `xp` and `yp` of the player
on the previous frame. It turns out that your `oldyp` will be updated if
you stand on a vertically moving platform, before the gravity line
collision function gets ran. So, if you were colliding with a gravity
line on the previous frame, but you got moved out of there by a
vertically moving platform, then you just don't collide with the gravity
line at all.

However, this behavior changed in 2.3 after my over-30-FPS patch got
merged (#220). That patch took advantage of the existing `oldxp` and
`oldyp` entity attributes, and uses them to interpolate their positions
during rendering to make everything look real smooth.

Previously, `oldxp` and `oldyp` would both be updated in
`entityclass::updateentitylogic()`. However, I moved it in that patch to
update right before `gameinput()` in `main.cpp`.

As a result, `oldyp` no longer gets updated whenever the player stands
on a vertically moving platform. This ends up desyncing my TAS.

As expected, updating `oldyp` in `entityclass::movingplatformfix()` (the
function responsible for moving the player whenever they stand on a
vertically moving platform) makes it so that my TAS syncs, but the
visuals are glitchy when standing on a vertically moving platform. And
as much as I'd like to get rid of gravity lines checking for whether
you've collided with them on the previous frame, doing that desyncs my
TAS, too.

In the end, it seems like I should just leave `oldxp` and `oldyp` alone,
and switch to using dedicated variables that are never used in the
physics of the game. So I'm introducing `lerpoldxp` and `lerpoldyp`, and
replacing all instances of using `oldxp` and `oldyp` that my over-30-FPS
patch added, with `lerpoldxp` and `lerpoldyp` instead.

After doing this, and applying #503 as well, my Nova TAS syncs after
some minor but acceptable fixes with Viridian's walkingframe.
2020-10-11 16:19:26 -04:00
Misa
e8084fe699 Remove now-unused entityclass::hormovingplatformfix()
This function is no longer used after I removed it in favor of using
`entityclass::moveblockto()`.
2020-10-11 16:18:30 -04:00
Misa
99297659ee Restore platform evaluation order to 2.2
This commit restores the evaluation order of moving platforms and conveyors to
be what it was in 2.2. The evaluation order changed in 2.3 after the patchset
to improve the handling of the `obj.entities` and `obj.blocks` vectors (#191).

By evaluation order, I'm talking about the order in which platforms and
conveyors will be evaluated (and thus will take priority) if Viridian stands
on both a conveyor or platform at once, and they either have different speeds
or are pointing in different directions. Nowhere in the main game is there a
place where you can stand on two different conveyors/platforms at once, so
this is solely within the territory of custom levels, which is my specialty.

So what caused this evaluation order to change? Well, every moving platform
and conveyor in the game is actually made up of two objects: an entity, and a
block. The entity is the part that moves around, and the block is the part
that actually has the collision.

But if the entity is the part that moves around, and entities and blocks are
in entirely separate vectors, how is the block part going to move along with
it? Well, maybe you'd guess some sort of unique ID system, but spend some time
digging around the code and you won't find any trace of any (there's no
attribute on an entity to store such an ID, for starters).

Instead, what the game does is actually remove all blocks that coincide with
the exact top-left corner of the entity, and then create a new one. Destroying
and creating blocks like this all the time is hugely wasteful, but hey, it
worked.

So why did the evaluation order change in 2.3? Well, to understand that,
you'll need to understand 2.2's `active` system. Instead of having an object
be real simply by virtue of it existing, 2.2 had this system where the object
was only real if it had its `active` attribute set to true. In other words,
you would be looking at a fake object that didn't actually exist if its
`active` attribute was false.

On the surface, this doesn't seem that bad. But this can lead to "holes" in a
given vector of objects. A hole is simply an inactive object neighbored by
active objects (or the inactive object could be the first one in the vector,
but then have an active object immediately following it).

If you have a vector of 3 objects, all of them active, then removing the
second one will result in the vector containing an active object, followed by
an inactive object, followed by an active one. However, since the switch to
more properly use vectors instead of relying on this `active` system, there's
no longer any way for holes to exist in a vector. Properly removing an object
from a vector will just shift the rest of the objects down, so if we remove
the second object after the vector fix, then this will simply move the third
object into the slot of where the second object used to be.

So, what happens if you destroy a block and then create a new one in the
`active` system? Let's say that your `obj.blocks` looks like this, and here
I'm denoting each block by writing out its coordinates:

    [30,60] [70,90] [80,100]

and that you want to update the position of the second one, because the entity
that that blocks belongs to has been updated. Okay, so, you delete that block,
which then makes things look like this:

    [30,60] [-] [80,100]

and then afterwards, you create a new block with the updated position,
resulting in this:

    [30,60] [74,90] [80,100]

Since `entityclass::createblock()` will find the first block slot that has a
false `active` attribute, it puts the new object in the same slot as the old
one. What has been essentially done here is that the slot of the block has
basically been reserved for the new block with the new position. Here, the
evaluation order of each block will stay the same.

But then 2.3 comes along and changes things up. So we start with an
`obj.blocks` like this again:

    [30,60] [70,90] [80,100]

and we want to update the second block, like before. So we remove the second
block, resulting in this:

    [30,60] [80,100]

It should be obvious that unlike before, where the third block stayed in the
third slot, the third block has now been moved to the second slot. But
continuing on; we are now going to create the new block with its updated
position, resulting in this:

    [30,60] [80,100] [70,90]

At this point, we can see that the evaluation order of these blocks has been
changed due to the fact that the third block has now been moved to the slot
that was previously the slot of the second block.

So what can we do about this? Well, we can basically emulate what VVVVVV did
in 2.2, which is disable a block without actually removing it - except I'm not
going to reintroduce an `active` attribute or anything. I'll disable the
collision of all blocks at a certain position by setting their widths and
heights to 0, and then re-enable them later by finding the first block at that
same position, updating its position, and re-assigning its width and height
again.

The former is what `entityclass::nocollisionat()` does; the latter is what
`entityclass::moveblockto()` does. The former mimicks turning off the `active`
attribute of all blocks sharing a certain top-left corner; the latter mimicks
creating a new block - and it will only do this for one block, because
`entityclass::createblock()` in 2.2 only looked for the first block with a
false `active` attribute.

Now, some quirks relied on the previous behavior of destroying and creating
blocks, but all of these quirks have been preserved with the way I implemented
this fix.

The first quirk is that platforms passing through 0,0 will destroy all spike
hitboxes, script boxes, activity zones, and one-way hitboxes in the room. The
hitboxes of moving platforms, disappearing platforms, 1x1 quicksand, and
conveyors will not be affected.

This is a consequence of the fact that the former group uses the `x` and `y`
of their `rect`, while the latter group uses the `xp` and `yp` attributes. So
the `xp` and `yp` of the former are both 0. Meaning, a platform passing
through 0,0 destroys them all.

Having these separate coordinates seems like an artifact from the Flash days.
(And furthermore, there's an unused `x` and `y` attribute on all blocks,
making for technically three separate sets of coordinates! This should
probably be cleaned up, except for what I'm about to say...) But actually, if
you merge both sets of coordinates into one, this lets moving platforms
destroy script boxes and activity zones if it passes through the top-left
corner of them, which is probably far worse than the destruction being
localized to a specific coordinate that would never likely be reached
normally.

This quirk is preserved just fine without any special-casing, because instead
of destroying all blocks at 0,0, they just get disabled, which does the same
job. This quirk seems trivial to fix if I made it so that the position of a
platform's block was updated instantaneously instead of having one step to
disable it and another step to re-enable it, but I aim to preserve as much
quirks as possible.

The second quirk is that a moving platform passing through the top-left corner
of a disappearing platform or 1x1 quicksand will destroy the block of that
disappearing platform. This is because, again, when a moving platform updates,
it destroys all blocks at its previous position, not just only one block. This
is automatically preserved because this commit just disables the block of the
disappearing platform instead of removing it. Just like the last one, this
quirk seems extremely trivial to fix, and this time by simply making it so
`entityclass::nocollisionat()` would have a `break` statement, i.e. only
disabling the first block it finds instead of all blocks it finds, but I want
to keep all quirks that are possible to keep.

The last quirk is that, apparently, in order to prevent pushing the player
vertically out of a moving platform if they get inside of one, the game
destroys the block of the moving platform. If I had missed this edge case,
then the block would've been destroyed, leaving the moving platform with no
collision. But I caught it in my testing, so the block gets disabled instead
of destroyed. Also, it seems obtuse for those who don't understand why a
platform's block gets destroyed or disabled whenever the player collides with
it in `entityclass::collisioncheck()`, so I've put up a comment documenting it
as well.

The different platform evaluation order desyncs my Nova TAS, but after
applying this patchset and #504, my TAS syncs fine (save for the different
walkingframe from starting immediately on the ground instead of in the air
(#502), but that's minor and can be easily fixed).

I've attached a test level to the pull request for this commit (#503) to
demonstrate that this patchset not only fixes platform evaluation order, but
preserves some bugs and quirks with the existing block system.

The first room demonstrates the fixed platform evaluation order, by stepping
on the conveyors that both point into each other. In 2.2, Viridian will move
to the right of the background pillar, but in 2.3, Viridian will move to the
left of the pillar. However, after applying this patch, Viridian will now move
to the right of the pillar once again.

The second room demonstrates that the platform-passing-through-0,0 trick still
works (as explained above).

The last room demonstrates that platforms passing through the top-left corners
of disappearing platforms or 1x1 quicksand will remove the blocks of those
entities, causing Viridian to immediately pass through them. This trick is
still preserved after my patchset is applied.
2020-10-11 16:18:30 -04:00
Misa
c83132f4fa Add entityclass::moveblockto()
This function will restore a block's hitbox after it has been disabled
by `entityclass::nocollisionat()`.
2020-10-11 16:18:30 -04:00
Misa
d8cee4866e Add entityclass::nocollisionat()
This function will disable a block instead of removing it entirely,
mimicking the previous `active` system of 2.2.
2020-10-11 16:18:30 -04:00
Misa
b3b1a0696b Unindent entity update loops from previous commit
This unindent is done in a separate commit to minimize diff noise.
2020-10-11 16:18:30 -04:00
Misa
7e04908cd4 Collapse and invert if-statements in entity logic loops
This makes the code easier to read by reducing the amount of indentation
it has.
2020-10-11 16:18:30 -04:00
Misa
c56cf009e7 Use SDL_abs() instead of libc abs() in Logic.cpp
There are other instances where SDL_abs() should be used, but I'm fine
with at least fixing these for now.
2020-10-11 16:18:30 -04:00
Misa
71c8c54313 Clean up music handling to one place
Previously, setting the actual volume of the music was all over the
place. Which isn't bad, but when I added being able to press N to mute
the music specifically, I should've made it so that there would be a
volume variable somewhere that the game looks at if the music is
unmuted, and otherwise sets the actual volume to 0 if the game is muted.

This resulted in things like #400 and #505 and having to add a bunch of
special-cased checks like game.musicmuted and game.completestop. But
instead of adding a bunch of special-case code, just make it so there's
a central place where Mix_VolumeMusic() actually gets called, and if
some piece of code wants to set the music volume, they can set
music.musicVolume. But the music handling logic in main.cpp gets the
final say on whether to listen to music.musicVolume, or to mute the game
entirely.

This is how the music handling code should have been from the start
(when pressing N to mute the music was added).

Fixes #505.
2020-10-11 16:16:57 -04:00
Misa
d0b3cfa08c Don't hardcode MIX_MAX_VOLUME value of 128
The value of the macro might not change in the future, but it's there
for a reason. That reason being to improve code readability, because
otherwise 128 would just be a magic number that plopped in out of
nowhere. Sometimes the game uses MIX_MAX_VOLUME, other times it uses
128, so to be consistent I'm just going to enforce MIX_MAX_VOLUME
entirely.
2020-10-11 16:16:57 -04:00
Misa
ba04c361c6 Revert "Fix #400"
This reverts commit cf5ad166e3.

My implementation will make it so single-case patches like this commit
won't be necessary anymore (there's no need to add a special-case check
for game.musicmuted, the way that I'm gonna do it). In fact, it's better
if I just revert the commit entirely.
2020-10-11 16:16:57 -04:00
Misa
7b7c7b2dc7 Fix inconsistencies with y-positions of spawns
It seems like the start point of a custom level and all checkpoints in
the game end up putting your spawn point one pixel away from the surface
it touches, which seems like an oversight. I'm going to enforce some
consistency here and make it so that all spawn points, whenever you
start from a start point or a checkpoint, will always be correctly
positioned flush with the surface they're standing on, and not one pixel
more or less than that.
2020-10-06 02:24:51 -04:00
Misa
dfdad165f5 Fix spawning from an exotic checkpoint setting invalid gravitycontrol
An exotic checkpoint is a checkpoint with a sprite that is neither the
flipped checkpoint nor unflipped checkpoint. More specifically, it's a
checkpoint whose edentity p1 attribute is something other than 0 or 1.

Normally, whenever you touch an exotic checkpoint in-game, your
savepoint's y-position and gravitycontrol don't get touched. However, in
the editor, spawning from an exotic checkpoint means that your
gravitycontrol gets set to a value that is neither 0 nor 1. In this
invalid gravitycontrol state, Viridian is treated like they're flipped,
but they cannot unflip.

This is an inconsistency between the editor and in-game, so I'm fixing
it. Now, spawning from an exotic checkpoint in the editor will just set
your gravitycontrol to 0, i.e. unflipped.
2020-10-06 02:24:51 -04:00
Misa
b4a0acc01d Fix onground/onroof taking 1 frame after landing on vertical plat
So, 77a636509b fixed the fact that you
only got 1 frame of onground/onroof when standing on a vertical moving
platform, but removing those lines entirely means that it takes 1 frame
before the onground/onroof of 2 actually takes effect. This desyncs my
Nova TAS, so it seems important to fix.
2020-10-06 02:16:49 -04:00
Misa
77a636509b Remove duplicate onroof/onground assignment for vertical moving plats
The onroof/onground attributes are used to determine if the player is
standing on a surface and is eligible to flip. Most notably, it is an
integer and not a boolean, and it starts at 2, giving the player 2
frames to edge-flip, i.e. they can still flip 2 frames after walking off
an edge.

However, these attributes are unnecessarily reassigned in
movingplatformfix() (which is the function that deals exclusively with
vertically-moving platforms; horizontal moving platforms get their own
hormovingplatformfix()). Whoever wrote this misunderstood what
onroof/onground meant; they thought that they were booleans, and so set
them to true, instead of the proper value of 2. This ends up setting
onroof/onground to 1 instead of 2, causing a discrepancy with vertical
moving platforms and the rest of the surfaces in the game.

The bigger mistake here is duplicating code that never needed to be
duplicated. The onroof/onground assignment in gamelogic() works
perfectly fine for vertical moving platforms. Indeed, after testing it
with libTAS, I can confirm that removing the duplicate assignments
restores being able to edge-flip off of moving platforms with 2 frames
of leeway, instead of only 1 frame. It also doesn't change how long it
takes for the onroof/onground to get set when the player is recognized
as standing on a vertically-moving platform, either.

And so, it's better to not duplicate this code, because when you
duplicate it you run the risk of making a mistake, as I just
demonstrated.
2020-09-28 15:46:41 -04:00
Misa
733cad723b Remove unused attributes from entclass
These attributes were `jumping` and `jumpframe`. Removal of unused
attributes makes reading the code easier.
2020-09-28 15:45:53 -04:00
Misa
cbceeccf78 Clean up and prevent unnecessary qualifiers to self
By "unnecessary qualifiers to self", I mean something like using the
'game.' qualifier for a variable on the Game class when you're inside a
function on the Game class itself. This patch is to enforce consistency
as most of the code doesn't have these unnecessary qualifiers.

To prevent further unnecessary qualifiers to self, I made it so the
extern in each header file can be omitted by using a define. That way,
if someone writes something referring to 'game.' on a Game function,
there will be a compile error.

However, if you really need to have a reference to the global name, and
you're within the same .cpp file as the implementation of that object,
you can just do the extern at the function-level. A good example of this
is editorinput()/editorrender()/editorlogic() in editor.cpp. In my
opinion, they should probably be split off into their own separate file
because editor.cpp is getting way too big, but this will do for now.
2020-09-28 01:34:40 -04:00
Misa
571ad1f7d8 Move all temporary variables off of entityclass
This is a refactor that simply moves all temporary variables off of
entityclass, and makes it so they are no longer global variables. This
makes the resulting code easier to understand as it is less entangled
with global state.

These attributes were:
 - colpoint1
 - colpoint2
 - tempx
 - tempy
 - tempw
 - temph
 - temp
 - temp2
 - tpx1
 - tpy1
 - tpx2
 - tpy2
 - temprect
 - temprect2
 - x (actually unused)
 - dx
 - dy
 - dr
 - px
 - py
 - linetemp
 - activetrigger
 - skipblocks
 - skipdirblocks

Most of these attributes were assigned before any of the times they were
used, so it's easy to prove that ungloballing them won't change any
behaviors. However, dx, dy, dr, and skipblocks are a bit more tricky to
analyze. They relate to blocks, with dx, dy, and dr more specifically
relating to one-way tiles. So after some testing with the quirks of
one-way tiles, it seems that the jankiness of one-way tiles haven't
changed at all, either.

Unfortunately, the attribute k is clearly used without being assigned
beforehand, so I can't move it off of entityclass. It's the same story
with the attribute k that Graphics has, too.
2020-09-27 19:08:37 -04:00
Misa
695e720c1c Prevent typing pipes in the script editor
This prevents users from being confused whenever they type a pipe in the
script editor, then save the level and load it again and see their
script lines unexpectedly splitting in two. Now if you attempt to type a
pipe, it simply won't happen at all.

Fixes #379.
2020-09-27 18:45:02 -04:00
Misa
25f27d502a Prevent undefined behavior with integer under/overflow with help.Int()
It's possible that SDL_atoi() could call the libc atoi(), and if a
string is provided that's too large to fit into an integer, then that
would result in undefined behavior. To avoid this, use SDL_strtol()
instead.
2020-09-27 17:05:00 -04:00
Misa
605b8a427c Make 'custom_' check more readable
Instead of copying to a temporary string, just use SDL_strncmp(). Also,
I checked the blame, and apparently I committed the line that used
strcmp() instead of SDL_strcmp(), for whatever reason. But that's fixed
now.
2020-09-27 16:31:40 -04:00
Misa
fe56764fbc Don't use bounds check for checktrigger() in twoframedelayfix()
This is like the previous patch, but for twoframedelayfix(), because I
forgot to read the comment that talked about twoframedelayfix().
2020-09-25 18:07:19 -04:00
Misa
c8f000af02 Don't use bounds check for result of checktrigger(), it's a gamestate
checktrigger() returns a gamestate number, not the index of an entity.

Whoops.
2020-09-25 18:00:18 -04:00
Misa
a38faad156 Add bounds checks to indexing of global "temporary" variable k
For some reason, the variable `k` is on entityclass and gets mutated in
createentity() and createblock(). Then updateentities() uses it without
checking if it's valid, because either `k` or the size of `entities`
could have changed in the meantime. To fix any potential undefined
behavior, these bounds checks should be added.
2020-09-25 17:16:15 -04:00
Misa
990ee63a6e Fix brace style in Graphics::Makebfont() and Graphics::bfontlen()
Seriously, Leo, why did you have to use braces like this...
2020-09-25 16:33:54 -04:00
Misa
2e78eab92f Clear font_positions if there is no font.txt
This fixes a bug where font_positions wouldn't get cleared after exiting
a custom level that had a font.txt if it didn't exist in the default
graphics, leading to messed-up-looking font rendering.

This bug was originally discovered by Ally.
2020-09-25 16:33:54 -04:00
Misa
96be0fc7a9 Fix unwinnable save from rescuing Violet out of order
You're intended to rescue Violet first, and not second, third, or
fourth, and especially not last.

If you rescue her second, third, or fourth, your crewmate progress will
be reset, but you won't be able to re-rescue them again. This is because
Vitellary, Verdigris, Victoria, and Vermilion will be temporarily marked
as rescued during the `bigopenworld` cutscene, so duplicate versions of
them don't spawn during the cutscene, and then will be marked as missing
later to undo it.

This first issue can be trivially fixed by simply toggling flags to
prevent duplicates of them from spawning during the cutscene instead of
fiddling with their rescue statuses.

However, there is still another issue. If you rescue Violet last, then
you won't be warped to the Final Level, meaning you can't properly
complete the game. This can be fixed by adding a `crewrescued() == 6`
check to the Space Station 1 Level Complete cutscene. There is
additionally a temporary unrescuing of Violet so she doesn't get
duplicated during the `bigopenworld` cutscene, and I've had to move that
to the start of the `bigopenworld` and `bigopenworldskip` scripts,
otherwise the `crewrescued() == 6` check won't work properly.

I haven't added hooks for Intermission 1 or 2 because you're not really
meant to play the intermissions with Violet (but you probably could
anyway, there'd just be no dialogue).

Oh, and the pre-Final Level cutscene expects the player to already be
hidden before it starts playing, but if you rescue Violet last the
player is still visible, so I've fixed that. But there still ends up
being two Violets, so I'll probably replace it with a special cutscene
later that's not so nonsensical.
2020-09-25 14:02:09 -04:00
Misa
3ffe1b2e3b Add bounds-check to entcolours in editor rendering
I have no idea why neither conveyors and moving and disappearing
platforms rendered in the editor or in-game use
Graphics::drawentcolours(), but this needs to be bounds-checked just as
I did for the in-game rendering function.
2020-09-25 13:51:47 -04:00
Misa
c325085ddb Fix up trinket and coin ID bounds checks
Trinket IDs weren't being bounds-checked upon creation, and coin IDs
weren't being bounds-checked upon pickup. But now they both are.
2020-09-25 13:51:47 -04:00
Misa
140861a79d Add bounds checks to both versions of Graphics::drawsprite()
For some reason, I forgot to add bounds checks to these functions. Well,
I'm glad I caught it, anyways.
2020-09-25 13:51:47 -04:00
Misa
76d6a3536b Bounds check all entity getters that can return 0
The entity getters I'm referring to are entityclass::getscm(),
entityclass::getlineat(), entityclass::getcrewman(), and
entityclass::getcustomcrewman().

Even though the player should always exist, and the player should always
be indice 0, I wouldn't want to make that assumption. I've been wrong
before.

Also, these functions returning 0 lull you into a false sense of
security. If you assume that commands using these functions are fine,
you'll forget about the fact that `i` in those commands could be
potentially anything, given an invalid argument. In fact, it's possible
to index createactivityzone(), flipgravity(), and customposition()
out-of-bounds by setting `i` to anything! Well, WAS possible. I fixed it
so now they can't.

Furthermore, in the game.scmmoveme block in gamelogic(), obj.getplayer()
wasn't even checked, even though it's been checked in all other places.
I only caught it just now because I wanted to bounds-check all usages of
obj.getscm(), too, and that game.scmmove block also used obj.getscm()
without bounds-checking it as well.
2020-09-25 13:51:47 -04:00
Misa
19a8352775 Don't hardcode 400 in editorclass::save()
That way, if the size of `level` changes, this code won't end up being
wrong.
2020-09-25 13:51:47 -04:00
Misa
f02dcbfdad Don't manually write out INBOUNDS_ARR() checks
When this is done, there is potential for a mistake to occur when
writing out the bounds check, which is eliminated when using the macro
instead. Luckily, this doesn't seem to have happened, but what's even
worse is I hardcoded 400 instead of using SDL_arraysize(ed.level), so if
the size of ed.level the bounds checks would all be wrong, which
wouldn't be good. But that's fixed now, too.
2020-09-25 13:51:47 -04:00
Misa
7b20d90446 Don't manually write out INBOUNDS_VEC() checks
This is because if they are manually written out, they are more likely
to contain mistakes.

In fact, after further review, there are several functions with
incorrect manually-written bounds checks:
 * entityclass::entitycollide()
 * entityclass::removeentity()
 * entityclass::removeblock()
 * entityclass::copylinecross()
 * entityclass::revertlinecross()

All of those functions forgot to do 'greater than or equal to' instead
of 'greater than' when comparing against the size of the vector. So they
were erroneous. But they are now fixed.
2020-09-25 13:51:47 -04:00
Misa
32b6de729d Use SDL_arraysize() instead of writing out array length
Using the macro makes it less likely that there will be a mistake when
writing out the array length.
2020-09-25 13:51:47 -04:00
Misa
b34be3f1ac Use explicit INBOUNDS_VEC() instead of checking sentinel -1
It's better to do INBOUNDS_VEC(i, obj.entities) instead of 'i > -1'.

'i > -1' is used in cases like obj.getplayer(), which COULD return a
sentinel value of -1 and so correct code will have to check that value.
However, I am now of the opinion that INBOUNDS_VEC() should be used and
isn't unnecessary.

Consider the case of the face() script command: it's not enough to check
i > -1, you should read the routine carefully. Because if you look
closely, you'll see that it's not guaranteed that 'i' will be initialized
at all in that command. Indeed, if you call face() with invalid
arguments, it won't be. And so, 'i' could be something like 215, and
that would index out-of-bounds, and that wouldn't be good. Therefore,
it's better to have the full bounds check instead of checking only one
bounds. Many commands are like this, after some searching I can also
name position(), changemood(), changetile(), changegravity(), etc.

It also makes the code more explicit. Now you don't have to wonder what
-1 means or why it's being checked, you can just read the 'INBOUNDS' and
go "oh, that checks if it's actually inbounds or not".
2020-09-25 13:51:47 -04:00
Misa
b925352864 Reduce scope of 'j' in screen transition companion logic
Reducing the scope of the variable makes analysis easier - for example,
we can now see it is always initialized before being used.
2020-09-25 13:51:47 -04:00
Misa
a098448e89 Remove unused variable from gamestate 115
For some reason it called obj.getplayer() and did nothing with the
result. Weird. But it does say "Test script for space station" above.

Removing this fixes an 'unused variable' warning.
2020-09-25 13:51:47 -04:00
Misa
fad0071966 Reduce scope of temporary variables in Game::updatestate()
With the scope of these variables reduced, it makes analyzing this
function easier, as you can now clearly see all temporary variables are
actually initialized before they're used.
2020-09-25 13:51:47 -04:00
Misa
7ed495c373 Rename INBOUNDS() macro to INBOUNDS_VEC()
Since there's an INBOUNDS_ARR() macro, it's much better to specify the
macro for the vector is a macro for the vector, to avoid confusion.

All usages of this macro have been renamed accordingly.
2020-09-25 13:51:47 -04:00
Misa
fae14f4e98 De-duplicate stuck prevention for the player/SCM
Stuck prevention (pushing the player/supercrewmate out if they are
inside a wall) has been factored out into its own function, so it's no
longer copy-pasted but slightly tweaked just for the supercrewmate.
2020-09-25 13:37:38 -04:00
Misa
5e29d676e9 Use case-switch for entityclass::collisioncheck()
This makes each case in the function less verbose.
2020-09-25 13:37:38 -04:00
Misa
b5806c8bb0 De-duplicate entity collision checks for player/SCM
The entity collision check routine has been factored out into its own
function, so it no longer needs to be copy-pasted for the supercrewmate.
2020-09-25 13:37:38 -04:00