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

61 commits

Author SHA1 Message Date
Misa
3e380e23fb Rename editor.h to CustomLevels.h
This accompanies the editor.cpp -> CustomLevels.cpp change; I'll be
splitting out the editor functions in the next commit. The name of the
include guard has been changed as well, but not anything else.
2021-09-01 15:30:02 -07:00
Misa
01ae5c6c70 Allow custom levels to use 2 billion tile numbers once again
Ever since tilesheets got expanded, custom levels could use as many
tiles as they wanted, as long as it fit under the 32-bit signed integer
limit.

Until 6c85fae339 happened and they were
reduced to 32,767 tiles.

So I'm being generous again and changing the type of the contents array
(in mapclass and editorclass) back to int. This won't affect the
existing tilemaps of the main game, they'll still stay short arrays. But
it means level makers can use 2 billion tiles once again.
2021-08-22 21:30:53 -07:00
Misa
d2153aee87 Refactor warp dir switching to separate function
This is so the same code can be used to go in reverse instead of
copy-pasting it.
2021-08-22 20:51:21 -07:00
Misa
0489293885 Remove default arguments from ed switch_* funcs
I don't like default arguments and if we're going to be moving to C
we'll need to remove them anyway.
2021-08-22 20:44:18 -07:00
Misa
945d5f244a Refactor room properties to use setter and getter funcs
This replaces all raw ed.level accesses with new setter and getter
funcs, which makes it easier to add bounds checks later. And I've also
removed all the manually-written bounds checks, since they will go into
the new getter and setter.

To get the room properties of a specific room, you use
editorclass::getroomprop(), which returns a pointer to the room
properties - then you just read off of that pointer. To set a room
property, you use editorclass::setroom<PROP>(), where <PROP> is the name
of the property. These are maintained using X macros to avoid
copy-pasting. editorclass::getroompropidx() is a helper function and
shouldn't be used directly.
2021-03-24 15:55:34 -04:00
Misa
344c93e754 Refactor tiles to use setter and getter functions
This makes it easier to add bounds checks to all accesses of
ed.contents.

To do this, I've added editorclass::gettile(), editorclass::settile(),
and editorclass::getabstile() (with a helper function of
editorclass::gettileidx() that really shouldn't be used directly), and
replaced all raw accesses of ed.contents with those functions
appropriately.

This also makes the code more readable, as a side effect.
2021-03-24 15:55:34 -04:00
Misa
6a3a1fe147
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.

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

I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 17:23:59 -05:00
Misa
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
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
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
945961c1fb Remove unused function editorclass::placetile()
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
d910c5118d Move all fixed-timestep render updates to new file RenderFixed.cpp
As part of fixing #464, I'll need to move these pieces of code around
easily. In #220 I just kind of shoved them awkwardly in whatever
fixed function would be last called in the gamestate loop, which I
shouldn't have done as I've now had to make formal fixed-render
functions anyway. Because these fixed functions need to be called
directly before a render function, and I'm fixing the order to put
render functions in their proper place, so I need to be able to move
these around easily, and making them function calls instead of inlined
makes them easier to manipulate.
2020-12-18 12:01:02 -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
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
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
d7040b23d0 Axe manual state trackers and use SDL_IsTextInputActive()
After looking at pull request #446, I got a bit annoyed that we have TWO
variables, key.textentrymode and ed.textentry, that we rolled ourselves
to track the state of something SDL already provides us a function to
easily query: SDL_IsTextInputActive(). We don't need to have either of
these two variables, and we shouldn't.

So that's what I do in this patch. Both variables have been axed in
favor of using this function, and I just made a wrapper out of it, named
key.textentry().

For bonus points, this gets rid of the ugly NO_CUSTOM_LEVELS and
NO_EDITOR ifdef in main.cpp, since text entry is enabled when entering
the script list and disabled when exiting it. This makes the code there
easier to read, too.

Furthermore, apparently key.textentrymode was initialized to *true*
instead of false... for whatever reason. But that's gone now, too.

Now, you'd think there wouldn't be any downside to using
SDL_IsTextInputActive(). After all, it's a function that SDL itself
provides, right?

Wrong. For whatever reason, it seems like text input is active *from the
start of the program*, meaning that what would happen is I would go into
the editor, and find that I can't move around nor place tiles nor
anything else. Then I would press Esc, and then suddenly become able to
do those things I wanted to do before.

I have no idea why the above happens, but all I can do is to just insert
an SDL_StopTextInput() immediately after the SDL_Init() in main.cpp. Of
course, I have to surround it with an SDL_IsTextInputActive() check to
make sure I don't do anything extraneous by stopping input when it's
already stopped.
2020-08-13 17:51:38 -04:00
Misa
52f7a587fe Separate includes into sections and alphabetize them
Okay, so basically here's the include layout that this game now
consistently uses:

[The "main" header file, if any (e.g. Graphics.h for Graphics.cpp)]
[blank line]
[All system includes, such as tinyxml2/physfs/utfcpp/SDL]
[blank line]
[All project includes, such as Game.h/Entity.h/etc.]

And if applicable, another blank line, and then some special-case
include screwy stuff (take a look at editor.cpp or FileSystemUtils.cpp,
for example, they have ifdefs and defines with their includes).
2020-07-19 21:37:40 -04:00
Misa
b108e28c5a Remove unnecessary EditorData constructor stub
It's useless anyway because `std::string`s get initialized
automatically.
2020-07-19 21:37:40 -04:00
Misa
58308f9826 Remove unused time stuff from editor.cpp
It was never used and didn't do anything. It looks like it was intended
to be used but I guess time ran out or something, but it's too late now
and level files don't have timestamps or anything, so might as well just
remove it.

Good thing too, because asctime() is apparently deprecated.
2020-07-19 21:37:40 -04:00
Misa
b5ff65c84e Remove unnecessary includes from header files
Including a header file inside another header file means a bunch of
files are going to be unnecessarily recompiled whenever that inner
header file is changed. So I minimized the amount of header files
included in a header file, and only included the ones that were
necessary (system includes don't count, I'm only talking about includes
from within this project). Then the includes are only in the .cpp files
themselves.

This also minimizes problems such as a NO_CUSTOM_LEVELS build failing
because some file depended on an include that got included in editor.h,
which is another benefit of removing unnecessary includes from header
files.
2020-07-19 21:37:40 -04:00
Misa
6c85fae339 Change all tilemaps to be short[1200] instead of int[1200]
This removes around megabyte from the binary, so a stripped -Og binary
went from 4.0 megabytes to 2.9 megabytes, and an unstripped -O0 binary
went from 8.1 megabytes to 7.1 megabytes, which means I can now finally
upload an unstripped -O0 binary to Discord without having to give money
to Discord for their dumb Nitro thing or whatever.
2020-07-19 16:25:53 -04:00
Misa
a80502bdc9 Turn ed.contents/vmult into arrays
They're always the same size, so there's no need for them to be vectors.

Also made the number of elements in ed.level/kludgewarpdir controllable
by maxwidth/maxheight.

I removed editorclass::saveconvertor() because I didn't want to convert
it to treat ed.contents like an array, because it's unused so I'd have
no way of testing it, plus it's also unused so it doesn't matter. Might
as well get rid of it.
2020-07-06 11:19:24 -04:00
Misa
9dcda17978 Turn map.contents into a plain array
map.contents always has 1200 tiles in it, there's no reason it should be
a vector.

This is a big commit because it requires changing all the level classes
to return a pointer to an array instead of returning a vector. Which
took a while for me to figure out, but eventually I did it. I tested to
make sure and there's no problems.
2020-07-06 11:19:24 -04:00
Misa
6b23244366 Move temp variable off of editorclass
Again, basically no reason for it to exist on the class itself.

The usage of the variable was replaced with temp2 instead of temp
because there was already a temp variable in the function it was used
in.
2020-07-06 11:19:24 -04:00
Misa
450663594f Add G keybind to go to room
Ved has this useful feature where instead of having to manually travel
to a room whose coordinates you know, you can just press G and type in
coordinates to go there.

VCE added this, but I changed the text to be "x,y" instead of "(x,y)"
because otherwise it could confuse someone into thinking they need to
type parentheses when in reality they don't need to and typing them will
just make it not work.

Also I made sure to add an error message if the user types in an invalid
format. Failing silently would just confuse people, and maybe they'll
start thinking the feature doesn't work or something like that. VCE
doesn't have this helpful error message.

Lastly, VCE has a bug where if you use the shortcut to go from one
horizontally/vertically warping room to another, the background of the
previous room will still be there and scroll off with the background of
the room you went to, instead of just having the new background only.
This is because they forgot a 'graphics.backgrounddrawn = false;'. But
don't worry, *I* didn't forget about it.
2020-07-02 01:06:50 -04:00
Misa
76d8dc5bf2 Refactor/de-duplicate entity text input
This is basically FIQ's patch from VCE, except he never upstreamed it
because he said something along the lines of it seeming to not fit the
purpose of upstream.

But anyway, it basically just de-duplicates all the text input and text
finishing handling code and cuts down on the large amount of copy-paste
in the editor functions. It makes things way more maintainable.

Interesting note, it seems like FIQ had the intent to refactor the text
input in editor settings (i.e. the level metadata details), but never
got around to it in VCE. Maybe we'll finish that job for him later.
2020-07-02 01:06:50 -04:00
Misa
e8cf521ed7 Abstract tileset/tilecol/enemy switching to functions
This results in me having to copy-paste less code around, because
editorinput() is big enough as it is.
2020-07-01 23:49:23 -04:00
Misa
5201f67909 Add being able to override the one-way recolor
Disabling the one-way recolor if assets are mounted is needed to make
existing levels not look bad, but what about levels that want to use the
recolor anyway?

The best solution here is to just introduce another bool into the XML,
and make the re-color opt-in and only present if assets are mounted if
that tag is present.
2020-06-30 18:06:14 -04:00
Misa
e84194db55 Re-color one-way tiles to match their tileset
One-ways have always had this problem where they're always yellow. That
means unless you specifically use yellow, it'll never match the tileset.

The best way to fix this without requiring new graphics or changing
existing ones is to simply re-tint the one-way with the given color of
the room. That way, the black part of the tile is still black, but the
yellow is now some other color.
2020-06-30 18:06:14 -04:00
Misa
c6e800db6f Add '#if !defined(NO_EDITOR)' guards around editorinput/render/logic
These functions aren't needed in a NO_EDITOR build, so it's useful to
reduce the binary size this way.
2020-06-19 18:35:03 -04:00
Misa
f151cff34d Fix editor ghost colors updating too fast
Like actual entities, editor ghost colors were updating every render
frame instead of logic frame. So just like actual entities, I added a
realcol attribute to them that editorrender() uses instead, and added
code to update said realcol attribute in editorlogic(). That way the
colors don't go by too quickly (especially the death color).
2020-06-19 09:05:48 -04:00
Misa
49fbe18d34 Make sure sprite colors in the editor don't update more than 30 FPS
This adds Graphics::crewcolourreal(), which is like the
entityclass::crewcolour() that the editor already uses, except for the
real color instead of the color ID. Also, editorclass now has an
attribute `entcolreal` so enemy colors don't update more than 30 frames
a second.
2020-06-19 09:05:48 -04:00
Misa
9256b4da56 Smoothly interpolate "[Press ENTER to return to editor]" fadeout
Now it'll be real smooth at 60 FPS. Or above. Or whichever one you want
above 30.
2020-06-19 09:05:48 -04:00
Misa
ca9f44c3b8 Smoothly interpolate editor notedelay
This makes editor notes fade out smoothly. And even though the notedelay
only gets decremented by one every editor-frame (the editor runs at
1000/24 FPS fixed-timestep here), it actually gets multiplied by 4, so a
floating-point interpolated value would make a difference here.
2020-06-19 09:05:48 -04:00
AllyTally
5e43a44d9a Add 7x7, 9x9, full horizontal and vertical brush sizes 2020-06-17 19:13:48 -04:00
AllyTally
5c80a4c25e Remove another header initialization 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
628eb7b7bf Fix mixed indentation in editor.h
Some of the file was indented with two spaces and the rest indented with
tabs. It feels like two different people worked on the file, one more
than the other. Since most of it uses two spaces, I'll just replace the
tabs with two spaces.
2020-06-11 22:13:52 -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
Dav999-v
ae45391ec0 Add editor saving/loading error messages
Previously, the editor would always say it saved or loaded a level,
even if it was not successful. For example, because a file to load does
not exist, a file to save has illegal characters in its name or the
name is too long to be stored. Now failure is reported. Also, when
quitting the editor and saving before quitting is unsuccessful, the
editor will abort quitting.
2020-06-02 09:46:42 -04:00
Misa
9205421090 Clean up editorclass externs into one location
Again, like the previous commit, it should just be put in the header
file of its respective class instead of being a mess everywhere.
2020-05-22 09:46:12 -04:00
Misa
3a5dd5a616 Clean up all scriptclass externs into one location
I have the feeling that none of the devs understood what extern did, and
they kind of just sprinkled it everywhere until things started working.
But like all other classes, it should just be one line in the class's
respective header file, and shouldn't be so messy.
2020-05-22 09:46:12 -04:00
Misa
4301a70f2d Remove unnecessary middleman ed.swapmap
When the game loads a room in a custom level, previously it would load
the tilemap of that room into ed.swapmap, and then mapclass::loadlevel()
would manually go through each element in ed.swapmap to set each tile in
`contents`. Why do that, when you can just return the vector from
editorclass::loadlevel() and set it directly? ed.swapmap is really
unnecessary.
2020-05-21 23:28:15 -04:00
Misa
fb9791a4c7 Remove now-useless function editorclass::countstuff()
Previously, it existed solely to count the number of trinkets and
crewmates when loading a level, because we were keeping track of the
amount of them manually, incrementing and decrementing every time a
trinket or crewmate was added or removed, but loading a new level
represented a case that could potentially not be an increment or
decrement.

However, since the amount tracking is now handled automatically, this
function now does nothing, and can be safely removed.
2020-04-09 19:20:31 -04:00
Misa
89b6b67a77 Don't use separate variable for number of crewmates in level
Same as previous commit, this time for crewmates.
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
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
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