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

64 commits

Author SHA1 Message Date
Misa
14afae1a40 Add bounds checks to script commands that didn't have them
Continuing from #280, another potential source of out-of-bounds indexing
(and thus, Undefined Behavior badness) comes from script commands. A
majority of them don't do any input validation at all, which means the
potential for out-of-bounds indexing and segfaulting in custom levels.
So it's always good to add bounds checks to them.

Interesting note, the only existing command that has bounds checks is
the flag() command. That means you can't turn out-of-bounds flags on or
off. But there's no bounds checks for ifflag(), or customifflag(), which
means you CAN index out-of-bounds with those commands! That's a bit bad
to do, so.

Also, I decided to add the bounds checks for playef() at the
musicclass::playef() level, instead of just the level of the playef()
command. I don't know of any other cases outside of the command where
musicclass::playef() will index out of bounds, but musicclass is the one
containing the indexed vector anyway, I wanted to cover more cases, and
it's better to be safe than sorry.
2020-06-13 01:24:42 -04:00
Misa
beab344267 Guard all cases obj.getplayer() is used unchecked
obj.getplayer() can return -1, which can cause out-of-bounds indexing of
obj.entities, which is really bad. This was by far the most changes, as
obj.getplayer() is the most used entity-getting function that returns
-1, as well as the most-used function whose sentinel value goes
unchecked.

To deal with the usage of obj.getplayer() in mapclass::warpto(), I just
added general bounds checks inside that function instead of changing all
the callers.
2020-06-12 23:55:48 -04:00
Misa
08e47e839f Guard all cases obj.getteleporter() is used unchecked
obj.getteleporter() is able to return -1. If there's no check on it, it
will end up indexing out-of-bounds, which is Undefined Behavior.
2020-06-12 23:55:48 -04:00
AllyTally
805992a1e1 Fix mixed indentation
The editors I use replace tabs with spaces, so I never really thought about mixed indentation happening. Whoops.
2020-06-12 19:11:48 -04:00
AllyTally
eb52657c23 Add a player trail to the editor (ghosts)
A few months ago, I added ghosts to the VVVVVV: Community Edition editor. I was told recently I should think
about upstreaming it, and with Terry saying go ahead I finally ported them into VVVVVV. There's one slight
difference however--you can choose whether you have them or not in the editor's settings menu. They're off by
default, and this is saved to the save file.
Anyway, when you're playtesting, the game saves the players position, color, room coordinates and sprite every 3
frames. The max is 100, where if it tries to add more, the oldest one gets removed.
When you exit playtesting, the saved positions appear one at a time, and you can use the Z key to speed it up.

[Here's a video of them in action.](https://o.lol-sa.me/4H21zCv.mp4)
2020-06-12 19:11:48 -04:00
Misa
7150c9ef2d Unindent scriptclass::loadcustom() from previous commit
The actual unindent is done in a separate commit to minimize noise,
because diffs are terrible at clearly conveying unindents (it should put
all the minus lines together and all the plus lines together, too).
2020-06-11 22:13:52 -04:00
Misa
6e0119d753 Invert contents check in scriptclass::loadcustom()
The entirety of the rest of scriptclass::loadcustom() is encased in a
block that first checks if the script with the name even exists. Instead
of indenting the rest of the function, just invert the check and reduce
indentation level.
2020-06-11 22:13:52 -04:00
Misa
8edf2f0ac6 Refactor custom scripts to not be stored in one giant vector of lines
This commit refactors custom level scripts to no longer be stored in one
giant vector containing not only every single script name, but every
single script's contents as well. More specifically,
scriptclass::customscript has been converted to an std::vector<Script>
scriptclass::customscripts (note the extra S), and a Script is just a
struct with an std::string name and std::vector<std::string> contents.

This is an improvement in both performance and maintainability. The game
no longer has to look through script contents in case they're actually
script names, and then manually extract the script contents from there.
Instead, all it has to do is look for script names only. And the
contents are provided for free. This results in a performance gain.

Also, the old system resulted in lots of boilerplate everywhere anytime
scripts had to be handled or parsed. Now, the boilerplate is only done
when saving or loading a custom level. This makes code quality much,
much better.

To be sure I didn't actually change anything, I tested by first saving
Dimension Open in current 2.3 (because current 2.3 gets rid of the
awful edentity whitespace), and then resaved it on this patch. There is
absolutely no difference between the current-2.3-resave and
this-patch-resave.
2020-06-11 22:13:52 -04:00
Misa
c561cd9740 Fix custom assets being unmounted in scriptclass::hardreset()
This resulted in two bugs:
 1. Custom assets would not be unmounted when quitting to the menu.
 2. Custom assets would be unmounted when playtesting a level.

The solution is to unmount assets in Game::quittomenu() instead.
2020-06-03 21:44:56 -04:00
Fussmatte
aaa25c7b47 Fixed some custom asset bugs, added .zip level loading
Main game would retain custom level assets, now fixed. Also, custom fonts load properly. Finally, levels can be stored as a zip and placed in the levels folder, with the .vvvvvv file at the root of the zip and custom asset folders (graphics, sounds etc) also at the root.
2020-06-03 15:35:39 -04:00
Misa
cfcfccf58b Fix segfault if say/reply/text asks for more lines than there are
This commit adds bounds checks to those commands in case say()/reply()
asks for more lines than there are left in the all-script-lines buffer
(not just the current script, so in order for it to segfault your script
has to be last in the all-script-lines vector), and in case text() asks
for more lines than there are in the rest of the rest of the parsed
internal script.
2020-05-31 18:29:16 -04:00
Misa
ff6cc1a777 De-duplicate say/reply line adding code
I found it patently ridiculous that `i++; add(customscript[i]);` was
copy-pasted four separate times. Well, at least it's only copy-pasted
twice now.
2020-05-31 18:29:16 -04:00
Misa
79d55baf6d Remove unnecessary references to global self in loadcustom()
For some reason, scriptclass::loadcustom() sometimes refers to
`customscript` as `script.customscript` instead of `customscript`. The
`script.` is unnecessary.
2020-05-31 18:29:16 -04:00
Misa
a623190b09 Fix using speak/speak_active without a text beforehand
Otherwise, it would end up indexing a vector out-of-bounds, which is UB
and causes a segfault, too. This is used in some constructs like

    say(-1)
    <internal command>

where the text contents don't matter, only that a text box shows up.
2020-05-23 16:04:04 -04:00
Misa
46559bbe0a De-duplicate speak_active/speak code
The `speak` command is the exact same as the `speak_active` command,
except without one line of code. So instead of copy-pasting the entire
thing, it's better to just combine them into the same chunk of code.
2020-05-23 16:04:04 -04:00
Misa
7afe206a0d Fix indentation of scriptclass::loadcustom()
It's now indented with tabs like the rest of the file. Furthermore, two
indentation levels have been knocked off.
2020-05-22 09:46:12 -04:00
Misa
fcea247c43 Move custom script parser to its own function
scriptclass::load() is a large enough function as it is, we don't need
any more trouble by shoving the custom script parser in there as well.
2020-05-22 09:46:12 -04:00
Misa
b5a009f2e2 Reset game.activeactivity and game.act_fade in scriptclass::hardreset()
Resetting game.activeactivity fixes triggering Undefined Behavior if you
quit to the menu while standing inside an activity zone, and then
re-entered the game. Resetting game.act_fade also fixes the activity
zone prompt fadeout that happens once the above segfault is fixed.

Don't worry, other activity-zone like variables such as teleporter
prompts and trophy text are already covered. obj.trophytext is reset in
scriptclass::hardreset(), too, and game.readytotele is reset every time
mapclass::gotoroom() is called, and mapclass::gotoroom() is called every
time a gamemode is started.
2020-05-20 05:12:49 -04:00
Misa
e795fbb511 Simplify inits/resets in entityclass/mapclass
Instead of using somewhat-obtuse for-loops to initialize or reset these
vectors, it takes up less lines of code and is clearer if we use
std::vector::resize() and std::vector::clear() instead.
2020-05-19 20:41:56 -04:00
Misa
c8d50d3067 Re-draw tower background when dying in No Death Mode
Otherwise, if you died after entering a room with a horizontal or
vertical warp background (but not the all-sides warp background), the
warp background would be the first thing you see when going to the Game
Over screen, and would then start scrolling downwards with the proper
tower background coming in from the top.

This oversight seems to have always been in the game.

Was No Death Mode actually tested? Like, did anyone ever play through
the entire game without dying in the Warp Zone, or even AFTER completing
the Warp Zone, like, ever?
2020-05-04 23:33:37 -04:00
Misa
28db7038fc Merge drawtowerbackgroundsolo() into drawtowerbackground()
It's less code being copied and pasted, especially since for my
over-30-FPS patch I would have to make a separate function for each if
both of them were still there, but if they're unified into one then I
will only have to make one more function.

And since map.scrolldir is now used outside of GAMEMODE, we'll need to
reset it in hardreset() and when exiting playtesting.
2020-04-29 18:08:13 -04:00
Misa
edb930344a Reset screen effects timers in hardreset()
That way a gigantic timer from one in-game jaunt doesn't carry over to
the next.
2020-04-27 15:07:58 -04:00
Misa
4d9c834a13 Change gamestate ints to their enum names
This is to make it easier to read, so I don't have to reference Enums.h
if I want to know what they are referring to.
2020-04-17 15:41:48 -04:00
Misa
e8a07f9c3d Convert menu names to be an enum instead of being stringly-typed
Stringly-typed things are bad, because if you make a typo when typing
out a string, it's not caught at compile-time. And in the case of this
menu system, you'd have to do an excessive amount of testing to uncover
any bugs caused by a typo. Why do that when you can just use an enum and
catch compile-time errors instead?

Also, you can't use switch-case statements on stringly-typed variables.

So every menu name is now in the enum Menu::MenuName, but you can simply
refer to a menu name by just prefixing it with Menu::.

Unfortunately, I've had to change the "continue" menu name to be
"continuemenu", because "continue" is a keyword in C and C++. Also, it
looks like "timetrialcomplete4" is an unused menu name, even though it
was referenced in Render.cpp.
2020-04-17 15:41:48 -04:00
Misa
17a64aee7a Make obj.customcollect a vector of bools
It's already treated as a vector of bools, so might as well formally
declare it as that.
2020-04-09 19:20:31 -04:00
Misa
8507bdc65d Change obj.collect into a vector of bools
It's already treated like a bunch of bools anyway, so might as well just
formalize it.
2020-04-09 19:20:31 -04:00
Misa
2ba9a0e67b Don't use obj.changeflag() to set flags
The way I see it, that function is basically an unnecessary middleman.
2020-04-09 19:20:31 -04:00
Misa
abfae6b4d7 Declare obj.flags a vector of bools instead of ints
It's treated like a bool anyway, so might as well make it one.

This also necessitates updating every single instance where it or an
element inside it is used, too.
2020-04-09 19:20:31 -04:00
Misa
0047dc8d81 Don't use separate variable for number of trinkets in level
Same principle as removing the separate variable to track number of
collected trinkets. This means it's less error-prone as we're no longer
tracking number of trinkets separately.

In the function that counts the number of trinkets, I would've liked to
have used std::count_if(). However, the most optimal way would require
using a lambda, and lambdas are too new for the C++ standard we're
using. So I just bit the bullet and counted them manually.
2020-04-09 19:20:31 -04:00
Misa
85bd7d9a2d Remove map.customtrinkets
This variable's sole purpose is to copy ed.numtrinkets, even though ed
has always been a name that's been accessible globally. So let's not
dupe cope.
2020-04-09 19:20:31 -04:00
Misa
c077e51fb4 Don't use separate variable for number of collected crewmates
Same as previous commit, except for crewmates in custom levels instead.
2020-04-09 19:20:31 -04:00
Misa
9510c3c871 Don't use separate variable for number of collected trinkets
game.trinkets is supposed to be correlated with obj.collect, however why
not just count obj.collect directly?

This turns game.trinkets into a function, game.trinkets(), which will
directly count the number of collected trinkets and return it. This will
fix a few corner cases where the number of trinkets can desync with the
actual collection statuses of trinkets.

In order to keep save compatibility with previous versions of VVVVVV,
the game will still write the <trinkets> variable. However, it will not
read the <trinkets> variable from a save file.
2020-04-09 19:20:31 -04:00
Misa
168fa53f7c Refactor scriptclass txt to not use a separate length-tracker
This removes the variable txtnumlines off of scriptclass, in favor of
using txt.size() instead.
2020-04-03 23:28:47 -04:00
Misa
b1b1474b7b Refactor entities and linecrosskludge to not use the 'active' system
This removes the variables obj.nentity and obj.nlinecrosskludge, as well
as removing the 'active' attribute from the entity class object. Now
every entity you access is guaranteed to be real and you don't have to
check the 'active' variable.

The biggest part of this is changing createentity() to modify a
newly-created entity object and push it back instead of already
modifying an indice in obj.entities.

As well, removing an entity now uses the new obj.removeentity() function
and removeentity_iter() macro.
2020-04-03 23:28:47 -04:00
Misa
d1661c20a8 Remove outdated comments from Script.cpp
Some of these seem to be copy-pasting stuff and then commenting it out
if it doesn't fit the pasted case.
2020-04-03 10:40:50 -04:00
Misa
4657760c2d Fix mixed indentation in Script.cpp and Script.h
Looks like the person who added starting a custom level indented with
spaces instead of tabs.
2020-04-03 10:40:50 -04:00
Misa
1310896191 Remove semi-useless function editorclass::weirdloadthing()
Looks like this function was created because editorclass::load() takes
in a string by reference, not by value, and thus mutates it afterwards,
so if you passed a string in when you didn't want it to be mutated, bad
things would happen.

However, a better workaround for the above issue would simply to
duplicate the string and pass that string instead, thus the original
string wouldn't be affected.
2020-04-03 10:40:50 -04:00
Misa
16c3966ace Remove unused argument from musicclass::playef()
Apparently the 'offset' argument did something in the 1.x Flash
versions, but now it does nothing.
2020-04-03 10:40:50 -04:00
Misa
9bc45c586e Remove global args from editorclass
This removes global arg passing from all functions on editorclass.
Callers have been updated correspondingly. Additionally, all 'dwgfx' has
been replaced with 'graphics' in editor.cpp.
2020-04-03 10:40:50 -04:00
Misa
ea3c778b84 Remove global args from scriptclass
This commit removes all global args from the parameters of each function
on the scriptclass object, and updates all places they are called
accordingly. It also changes all instances of 'dwgfx' to 'graphics' in
Script.cpp.

Interestingly enough, it looks like editor.h depended on Script.h's
class define of the musicclass. I've temporarily placed the class define
in editor.h, but by the end of this patchset it'll be gone.
2020-04-03 10:40:50 -04:00
Misa
35d9bcce05 Remove global args from mapclass
This commit removes the global args being passed around from the
function args on the mapclass object, as well as updating all callers in
other files to not have those args. Furthermore, 'dwgfx' has been
renamed to 'graphics' in Map.cpp.
2020-04-03 10:40:50 -04:00
Misa
cac1a9e3ab Remove global args from entityclass
This commit removes all global args from functions on the entityclass
object, and updates the callers of those functions in other files
accordingly (most significantly, the game level files Finalclass.cpp,
Labclass.cpp, Otherlevel.cpp, Spacestation2.cpp, WarpClass.cpp, due to
them using createentity()), as well as renaming all instances of 'dwgfx'
in Entity.cpp to 'graphics'.
2020-04-03 10:40:50 -04:00
Misa
0e561f23f8 Remove global args from Game
I've decided to call dwgfx/game/map/obj/key/help/music the "global args".
Because they're essentially global variables that are being passed
around in args.

This commit removes global args from all functions on the Game class,
and deals with updating the callsites of said functions accordingly. It
also renames all usages of 'dwgfx' in Game.cpp to 'graphics', since the
global variable is called 'graphics' now.

Interesting to note, I was removing the class defines from Game.h, but
it turns out that Graphics.h depends on the mapclass and entityclass
defines from Game.h. And also Graphics.h spelled mapclass wrong (it
forgot the "class") so I just decided to use that existing line instead.
This is only temporary and after all is said and done, at the end of
this pull request those class defines will be gone.
2020-04-03 10:40:50 -04:00
Misa
1be398319c Make commands, sb, and hooklist not use separate length-trackers
This is a refactor that turns the script-related arrays `ed.sb`, and
`ed.hooklist` into C++ vectors (`script.commands` was already a vector, it was
just misused). The code handling these vectors now looks more like idiomatic
C++ than sloppily-pasted pseudo-ActionScript. This removes the variables
`script.scriptlength`, `ed.sblength`, and `ed.numhooks`, too.

This reduces the amount of code needed to e.g. simply remove something from
any of these vectors. Previously the code had to manually shift the rest of
the elements down one-by-one, and doing it manually is definitely error-prone
and tedious.

But now we can just use fancy functions like `std::vector::erase()` and
`std::remove()` to do it all in one line!

Don't worry, I checked and `std::remove()` is in the C++ standard since at least
1998.

This patch makes it so the `commands` vector gets cleared when
`scriptclass::load()` is ran. Previously, the `commands` vector never actually
properly got cleared, so there could potentially be glitches that rely on the
game indexing past the bounds set by `scriptlength` but still in-bounds in the
eyes of C++, and people could potentially rely on such an exploit...

However, I checked, and I'm pretty sure that no such glitch previously existed
at all, because the only times the vector gets indexed are when `scriptlength`
is either being incremented after starting from 0 (`add()`) or when it's
underneath a `position < scriptlength` conditional.

Furthermore, I'm unaware of anyone who has actually found or used such an
exploit, and I've been in the custom level community for 6 years.

So I think it's fine.
2020-03-24 20:20:53 -04:00
Matt Penny
1b00d12600 Add option to allow custom levels when the editor is disabled 2020-02-09 23:31:44 -05:00
Matt Penny
7d35c5ce4e Add option to compile without the level editor 2020-02-09 23:31:44 -05:00
Info Teddy
a2ab8e82e1 Account for height of textbox in flipme()
The problem with flipme() was that it WAS properly reflecting
("reflecting" as in mirroring over a given line) the text box over the
line y=120, BUT it forgot to account for the height of the text box.
Thus, the text box position would be off by the length of its own
height. And when the text box got taller, this offset would worsen and
worsen.
2020-02-02 20:41:41 -05:00
Info Teddy
4fa62ca0aa Fix text() centering with x=-1
The game uses magic values x=-500 and y=-500 to indicate when a text box
should be centered horizontally or vertically. It does this for x=-1
too, but it's buggy because it only looks at the first line of the text
box to center it. In this commit I fix it so that it will look at all of
the lines of the text box to center it instead.
2020-01-31 13:36:36 -08:00
Info Teddy
4be6d58b82 Reset warp directions when exiting playtesting
This fixes a bug where if warpdir() was used during in-editor
playtesting, the changed warp direction would persist even when leaving
playtesting.

This would be very annoying to correct back every time you playtested
and warpdir() was used, so I've added some kludge to store the actual
warp direction of each room when entering playtesting, and then set the
warp directions back when leaving playtesting.
2020-01-30 22:15:45 -05:00
Info Teddy
47ebbf15ab Don't redraw H/V warp BG if gotorooming to same room in customs
This has two benefits:
 (1) The game uses less resources when it is asked to gotoroom to the
     same room because it is no longer redrawing the warp background
     every single frame, which is very wasteful.
 (2) The warp background no longer freezes or flickers if the player is
     standing inside a gotoroom script box (which calls gotoroom every
     frame or every other frame, because every time the gotoroom happens
     the script box gets reloaded).
2020-01-27 14:46:11 -08:00