2020-01-01 21:29:24 +01:00
|
|
|
#include <SDL.h>
|
2020-07-19 21:43:29 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "editor.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Enums.h"
|
|
|
|
#include "Entity.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "FileSystemUtils.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Game.h"
|
|
|
|
#include "Graphics.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "Input.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "KeyPoll.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "Logic.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Map.h"
|
2020-07-19 21:05:41 +02:00
|
|
|
#include "Music.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "Network.h"
|
|
|
|
#include "preloader.h"
|
|
|
|
#include "Render.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
#include "Screen.h"
|
|
|
|
#include "Script.h"
|
2020-07-19 21:43:29 +02:00
|
|
|
#include "SoundSystem.h"
|
|
|
|
#include "UtilityClass.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
scriptclass script;
|
|
|
|
|
2020-02-10 03:21:19 +01:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS)
|
2020-04-02 23:43:50 +02:00
|
|
|
std::vector<edentities> edentity;
|
|
|
|
editorclass ed;
|
2020-02-10 01:53:01 +01:00
|
|
|
#endif
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Allow using help/graphics/music/game/key/map/obj everywhere
This commit makes `help`, `graphics`, `music`, `game`, `key`, `map`, and
`obj` essentially static global objects that can be used everywhere.
This is useful in case we ever need to add a new function in the future,
so we don't have to bother with passing a new argument in which means we
have to pass a new argument in to the function that calls that function
which means having to pass a new argument into the function that calls
THAT function, etc. which is a real headache when working on fan mods of
the source code.
Note that this changes NONE of the existing function signatures, it
merely just makes those variables accessible everywhere in the same way
`script` and `ed` are.
Also note that some classes had to be initialized after the filesystem
was initialized, but C++ would keep initializing them before the
filesystem got initialized, because I *had* to put them at the top of
`main.cpp`, or else they wouldn't be global variables.
The only way to work around this was to use entityclass's initialization
style (which I'm pretty sure entityclass of all things doesn't need to
be initialized this way), where you actually initialize the class in an
`init()` function, and so then you do `graphics.init()` after the
filesystem initialization, AFTER doing `Graphics graphics` up at the
top.
I've had to do this for `graphics` (but only because its child
GraphicsResources `grphx` needs to be initialized this way), `music`,
and `game`. I don't think this will affect anything. Other than that,
`help`, `key`, and `map` are still using the C++-intended method of
having ClassName::ClassName() functions.
2020-01-29 08:35:03 +01:00
|
|
|
UtilityClass help;
|
|
|
|
Graphics graphics;
|
|
|
|
musicclass music;
|
|
|
|
Game game;
|
|
|
|
KeyPoll key;
|
|
|
|
mapclass map;
|
|
|
|
entityclass obj;
|
2020-06-13 20:44:01 +02:00
|
|
|
Screen gameScreen;
|
Allow using help/graphics/music/game/key/map/obj everywhere
This commit makes `help`, `graphics`, `music`, `game`, `key`, `map`, and
`obj` essentially static global objects that can be used everywhere.
This is useful in case we ever need to add a new function in the future,
so we don't have to bother with passing a new argument in which means we
have to pass a new argument in to the function that calls that function
which means having to pass a new argument into the function that calls
THAT function, etc. which is a real headache when working on fan mods of
the source code.
Note that this changes NONE of the existing function signatures, it
merely just makes those variables accessible everywhere in the same way
`script` and `ed` are.
Also note that some classes had to be initialized after the filesystem
was initialized, but C++ would keep initializing them before the
filesystem got initialized, because I *had* to put them at the top of
`main.cpp`, or else they wouldn't be global variables.
The only way to work around this was to use entityclass's initialization
style (which I'm pretty sure entityclass of all things doesn't need to
be initialized this way), where you actually initialize the class in an
`init()` function, and so then you do `graphics.init()` after the
filesystem initialization, AFTER doing `Graphics graphics` up at the
top.
I've had to do this for `graphics` (but only because its child
GraphicsResources `grphx` needs to be initialized this way), `music`,
and `game`. I don't think this will affect anything. Other than that,
`help`, `key`, and `map` are still using the C++-intended method of
having ClassName::ClassName() functions.
2020-01-29 08:35:03 +01:00
|
|
|
|
2020-04-09 21:03:24 +02:00
|
|
|
bool startinplaytest = false;
|
|
|
|
bool savefileplaytest = false;
|
|
|
|
int savex = 0;
|
|
|
|
int savey = 0;
|
|
|
|
int saverx = 0;
|
|
|
|
int savery = 0;
|
|
|
|
int savegc = 0;
|
|
|
|
int savemusic = 0;
|
2020-06-22 00:56:31 +02:00
|
|
|
std::string playassets;
|
2020-04-09 21:03:24 +02:00
|
|
|
|
|
|
|
std::string playtestname;
|
|
|
|
|
2020-06-14 20:45:36 +02:00
|
|
|
volatile Uint32 time_ = 0;
|
|
|
|
volatile Uint32 timePrev = 0;
|
|
|
|
volatile Uint32 accumulator = 0;
|
|
|
|
volatile Uint32 f_time = 0;
|
|
|
|
volatile Uint32 f_timePrev = 0;
|
|
|
|
|
2020-06-15 21:54:08 +02:00
|
|
|
void inline deltaloop();
|
|
|
|
void inline fixedloop();
|
2020-06-14 20:45:36 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2020-02-09 00:49:03 +01:00
|
|
|
char* baseDir = NULL;
|
|
|
|
char* assetsPath = NULL;
|
2020-02-03 00:28:26 +01:00
|
|
|
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
for (int i = 1; i < argc; ++i)
|
|
|
|
{
|
|
|
|
#define ARG(name) (strcmp(argv[i], name) == 0)
|
|
|
|
#define ARG_INNER(code) \
|
|
|
|
if (i + 1 < argc) \
|
|
|
|
{ \
|
|
|
|
code \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
printf("%s option requires one argument.\n", argv[i]); \
|
|
|
|
return 1; \
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ARG("-renderer"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, argv[i], SDL_HINT_OVERRIDE);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
else if (ARG("-basedir"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
baseDir = argv[i];
|
|
|
|
})
|
|
|
|
}
|
|
|
|
else if (ARG("-assets"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
assetsPath = argv[i];
|
|
|
|
})
|
|
|
|
}
|
|
|
|
else if (ARG("-playing") || ARG("-p"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
2020-04-09 21:03:24 +02:00
|
|
|
startinplaytest = true;
|
|
|
|
playtestname = std::string("levels/");
|
|
|
|
playtestname.append(argv[i]);
|
|
|
|
playtestname.append(std::string(".vvvvvv"));
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
else if (ARG("-playx") || ARG("-playy") ||
|
|
|
|
ARG("-playrx") || ARG("-playry") ||
|
|
|
|
ARG("-playgc") || ARG("-playmusic"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-04-09 21:03:24 +02:00
|
|
|
savefileplaytest = true;
|
2020-08-07 06:31:29 +02:00
|
|
|
int v = help.Int(argv[i+1]);
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
if (ARG("-playx")) savex = v;
|
|
|
|
else if (ARG("-playy")) savey = v;
|
|
|
|
else if (ARG("-playrx")) saverx = v;
|
|
|
|
else if (ARG("-playry")) savery = v;
|
|
|
|
else if (ARG("-playgc")) savegc = v;
|
|
|
|
else if (ARG("-playmusic")) savemusic = v;
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
else if (ARG("-playassets"))
|
|
|
|
{
|
|
|
|
ARG_INNER({
|
2020-06-22 03:37:44 +02:00
|
|
|
i++;
|
|
|
|
// Even if this is a directory, FILESYSTEM_mountassets() expects '.vvvvvv' on the end
|
2020-06-22 00:56:31 +02:00
|
|
|
playassets = "levels/" + std::string(argv[i]) + ".vvvvvv";
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
})
|
2020-04-09 21:03:24 +02:00
|
|
|
}
|
De-duplicate and add safety checks to CLI args, fix brace style
The command-line argument parsing code has a lot of copy-paste. This
copy-paste would get even worse if I added safety checks to make sure
you couldn't index argv out-of-bounds by having an argument like
`-renderer` without having anything after it, i.e. you'd be doing the
command `./VVVVVV -renderer`.
Previously, only the playtest arguments (apart from the recently-added
`playassets`) had this safety check, but the message it printed whenever
the safety check failed was always "-playing option requires one
argument" regardless of whatever argument actually failed to be parsed.
So I fixed it so that all arguments actually output the correct
corresponding failed argument instead.
Also, `strcmp(argv[i], <name>) == 0` is really kind of noisy, even if
you understand what it does perfectly well.
So I refactored it with a bunch of macros. ARG() just does the strcmp()
char* comparison, and ARG_INNER() does the safety check and returns 1,
along with printing a message, if the safety check fails.
2020-06-22 01:15:28 +02:00
|
|
|
#undef ARG_INNER
|
2020-07-16 03:27:54 +02:00
|
|
|
#undef ARG
|
2020-08-03 06:30:08 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("Error: invalid option: %s\n", argv[i]);
|
|
|
|
return 1;
|
|
|
|
}
|
2020-02-03 00:28:26 +01:00
|
|
|
}
|
|
|
|
|
2020-02-09 00:49:03 +01:00
|
|
|
if(!FILESYSTEM_init(argv[0], baseDir, assetsPath))
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
2020-06-22 01:24:51 +02:00
|
|
|
puts("Unable to initialize filesystem!");
|
2020-02-03 00:28:26 +01:00
|
|
|
return 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-02-19 18:44:36 +01:00
|
|
|
SDL_Init(
|
|
|
|
SDL_INIT_VIDEO |
|
|
|
|
SDL_INIT_AUDIO |
|
|
|
|
SDL_INIT_JOYSTICK |
|
|
|
|
SDL_INIT_GAMECONTROLLER
|
|
|
|
);
|
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 08:43:25 +02:00
|
|
|
if (SDL_IsTextInputActive() == SDL_TRUE)
|
|
|
|
{
|
|
|
|
SDL_StopTextInput();
|
|
|
|
}
|
2020-02-19 18:44:36 +01:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
NETWORK_init();
|
|
|
|
|
2020-04-02 23:43:50 +02:00
|
|
|
printf("\t\t\n");
|
|
|
|
printf("\t\t\n");
|
|
|
|
printf("\t\t VVVVVV\n");
|
|
|
|
printf("\t\t\n");
|
|
|
|
printf("\t\t\n");
|
|
|
|
printf("\t\t 8888888888888888 \n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t888888 8888 88\n");
|
|
|
|
printf("\t\t888888 8888 88\n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t888888 88\n");
|
|
|
|
printf("\t\t88888888 8888\n");
|
|
|
|
printf("\t\t 8888888888888888 \n");
|
|
|
|
printf("\t\t 88888888 \n");
|
|
|
|
printf("\t\t 8888888888888888 \n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t88888888888888888888\n");
|
|
|
|
printf("\t\t8888 88888888 8888\n");
|
|
|
|
printf("\t\t8888 88888888 8888\n");
|
|
|
|
printf("\t\t 888888888888 \n");
|
|
|
|
printf("\t\t 8888 8888 \n");
|
|
|
|
printf("\t\t 888888 888888 \n");
|
|
|
|
printf("\t\t 888888 888888 \n");
|
|
|
|
printf("\t\t 888888 888888 \n");
|
|
|
|
printf("\t\t\n");
|
|
|
|
printf("\t\t\n");
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
//Set up screen
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Load Ini
|
|
|
|
|
|
|
|
|
Allow using help/graphics/music/game/key/map/obj everywhere
This commit makes `help`, `graphics`, `music`, `game`, `key`, `map`, and
`obj` essentially static global objects that can be used everywhere.
This is useful in case we ever need to add a new function in the future,
so we don't have to bother with passing a new argument in which means we
have to pass a new argument in to the function that calls that function
which means having to pass a new argument into the function that calls
THAT function, etc. which is a real headache when working on fan mods of
the source code.
Note that this changes NONE of the existing function signatures, it
merely just makes those variables accessible everywhere in the same way
`script` and `ed` are.
Also note that some classes had to be initialized after the filesystem
was initialized, but C++ would keep initializing them before the
filesystem got initialized, because I *had* to put them at the top of
`main.cpp`, or else they wouldn't be global variables.
The only way to work around this was to use entityclass's initialization
style (which I'm pretty sure entityclass of all things doesn't need to
be initialized this way), where you actually initialize the class in an
`init()` function, and so then you do `graphics.init()` after the
filesystem initialization, AFTER doing `Graphics graphics` up at the
top.
I've had to do this for `graphics` (but only because its child
GraphicsResources `grphx` needs to be initialized this way), `music`,
and `game`. I don't think this will affect anything. Other than that,
`help`, `key`, and `map` are still using the C++-intended method of
having ClassName::ClassName() functions.
2020-01-29 08:35:03 +01:00
|
|
|
graphics.init();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
Allow using help/graphics/music/game/key/map/obj everywhere
This commit makes `help`, `graphics`, `music`, `game`, `key`, `map`, and
`obj` essentially static global objects that can be used everywhere.
This is useful in case we ever need to add a new function in the future,
so we don't have to bother with passing a new argument in which means we
have to pass a new argument in to the function that calls that function
which means having to pass a new argument into the function that calls
THAT function, etc. which is a real headache when working on fan mods of
the source code.
Note that this changes NONE of the existing function signatures, it
merely just makes those variables accessible everywhere in the same way
`script` and `ed` are.
Also note that some classes had to be initialized after the filesystem
was initialized, but C++ would keep initializing them before the
filesystem got initialized, because I *had* to put them at the top of
`main.cpp`, or else they wouldn't be global variables.
The only way to work around this was to use entityclass's initialization
style (which I'm pretty sure entityclass of all things doesn't need to
be initialized this way), where you actually initialize the class in an
`init()` function, and so then you do `graphics.init()` after the
filesystem initialization, AFTER doing `Graphics graphics` up at the
top.
I've had to do this for `graphics` (but only because its child
GraphicsResources `grphx` needs to be initialized this way), `music`,
and `game`. I don't think this will affect anything. Other than that,
`help`, `key`, and `map` are still using the C++-intended method of
having ClassName::ClassName() functions.
2020-01-29 08:35:03 +01:00
|
|
|
game.init();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-06-12 22:24:21 +02:00
|
|
|
// This loads music too...
|
2020-06-01 01:31:02 +02:00
|
|
|
graphics.reloadresources();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
game.gamestate = PRELOADER;
|
|
|
|
|
|
|
|
game.menustart = false;
|
|
|
|
game.mainmenu = 0;
|
|
|
|
|
|
|
|
map.ypos = (700-29) * 8;
|
2020-11-03 00:05:24 +01:00
|
|
|
graphics.towerbg.bypos = map.ypos / 2;
|
2020-11-03 00:23:53 +01:00
|
|
|
graphics.titlebg.bypos = map.ypos / 2;
|
2020-07-08 20:30:57 +02:00
|
|
|
|
|
|
|
//Moved screensetting init here from main menu V2.1
|
|
|
|
int width = 320;
|
|
|
|
int height = 240;
|
|
|
|
bool vsync = false;
|
|
|
|
game.loadstats(&width, &height, &vsync);
|
|
|
|
gameScreen.init(
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
game.fullscreen,
|
|
|
|
vsync,
|
|
|
|
game.stretchMode,
|
|
|
|
game.useLinearFilter,
|
|
|
|
game.fullScreenEffect_badSignal
|
|
|
|
);
|
|
|
|
graphics.screenbuffer = &gameScreen;
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
const SDL_PixelFormat* fmt = gameScreen.GetFormat();
|
2020-07-08 20:30:57 +02:00
|
|
|
#define CREATE_SURFACE(w, h) \
|
|
|
|
SDL_CreateRGBSurface( \
|
|
|
|
SDL_SWSURFACE, \
|
|
|
|
w, h, \
|
|
|
|
fmt->BitsPerPixel, \
|
|
|
|
fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask \
|
|
|
|
)
|
|
|
|
graphics.backBuffer = CREATE_SURFACE(320, 240);
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.backBuffer, SDL_BLENDMODE_NONE);
|
2020-07-08 20:30:57 +02:00
|
|
|
|
|
|
|
graphics.footerbuffer = CREATE_SURFACE(320, 10);
|
2020-01-25 05:32:55 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.footerbuffer, SDL_BLENDMODE_BLEND);
|
2020-01-25 08:30:51 +01:00
|
|
|
SDL_SetSurfaceAlphaMod(graphics.footerbuffer, 127);
|
2020-01-25 05:32:55 +01:00
|
|
|
FillRect(graphics.footerbuffer, SDL_MapRGB(fmt, 0, 0, 0));
|
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-13 00:04:35 +02:00
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
graphics.ghostbuffer = CREATE_SURFACE(320, 240);
|
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-13 00:04:35 +02:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.ghostbuffer, SDL_BLENDMODE_BLEND);
|
|
|
|
SDL_SetSurfaceAlphaMod(graphics.ghostbuffer, 127);
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
graphics.Makebfont();
|
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
graphics.foregroundBuffer = CREATE_SURFACE(320, 240);
|
Ax OverlaySurfaceKeyed(), set proper foregroundBuffer blend mode
So, earlier in the development of 2.0, Simon Roth (I presume)
encountered a problem: Oh no, all my backgrounds aren't appearing! And
this is because my foregroundBuffer, which contains all the drawn tiles,
is drawing complete black over it!
So he had a solution that seems ingenius, but is actually really really
hacky and super 100% NOT the proper solution. Just, take the
foregroundBuffer, iterate over each pixel, and DON'T draw any pixel
that's 0xDEADBEEF. 0xDEADBEEF is a special signal meaning "don't draw
this pixel". It is called a 'key'.
Unfortunately, this causes a bug where translucent pixels on tiles
(pixels between 0% and 100% opacity) didn't get drawn correctly. They
would be drawn against this weird blue color.
Now, in #103, I came across this weird constant and decided "hey, this
looks awfully like that weird blue color I came across, maybe if I set
it to 0x00000000, i.e. complete and transparent black, the issue will be
fixed". And it DID appear to be fixed. However, I didn't look too
closely, nor did I test it that much, and all that ended up doing was
drawing the pixels against black, which more subtly disguised the
problem with translucent pixels.
So, after some investigation, I noticed that BlitSurfaceColoured() was
drawing translucent pixels just fine. And I thought at the time that
there was something wrong with BlitSurfaceStandard(), or something.
Further along later I realized that all drawn tiles were passing through
this weird OverlaySurfaceKeyed() function. And removing it in favor of a
straight SDL_BlitSurface() produced the bug I mentioned above: Oh no,
all the backgrounds don't show up, because my foregroundBuffer is
drawing pure black over them!
Well... just... set the proper blend mode for foregroundBuffer. It
should be SDL_BLENDMODE_BLEND instead of SDL_BLENDMODE_NONE.
Then you don't have to worry about your transparency at all. If you did
it right, you won't have to resort this hacky color-keying business.
*sigh*
2020-08-04 09:24:04 +02:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.foregroundBuffer, SDL_BLENDMODE_BLEND);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
graphics.menubuffer = CREATE_SURFACE(320, 240);
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.menubuffer, SDL_BLENDMODE_NONE);
|
|
|
|
|
2020-11-03 00:05:24 +01:00
|
|
|
graphics.towerbg.buffer = CREATE_SURFACE(320 + 16, 240 + 16);
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.towerbg.buffer, SDL_BLENDMODE_NONE);
|
2020-07-08 20:30:57 +02:00
|
|
|
|
2020-11-03 00:05:24 +01:00
|
|
|
graphics.towerbg.buffer_lerp = CREATE_SURFACE(320 + 16, 240 + 16);
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.towerbg.buffer_lerp, SDL_BLENDMODE_NONE);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-11-03 00:23:53 +01:00
|
|
|
graphics.titlebg.buffer = CREATE_SURFACE(320 + 16, 240 + 16);
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.titlebg.buffer, SDL_BLENDMODE_NONE);
|
|
|
|
|
|
|
|
graphics.titlebg.buffer_lerp = CREATE_SURFACE(320 + 16, 240 + 16);
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.titlebg.buffer_lerp, SDL_BLENDMODE_NONE);
|
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
graphics.tempBuffer = CREATE_SURFACE(320, 240);
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.tempBuffer, SDL_BLENDMODE_NONE);
|
|
|
|
|
2020-07-08 20:30:57 +02:00
|
|
|
#undef CREATE_SURFACE
|
2020-05-04 22:19:47 +02:00
|
|
|
|
2020-01-13 02:45:44 +01:00
|
|
|
if (game.skipfakeload)
|
|
|
|
game.gamestate = TITLEMODE;
|
2020-04-02 23:43:50 +02:00
|
|
|
if(game.usingmmmmmm==0) music.usingmmmmmm=false;
|
|
|
|
if(game.usingmmmmmm==1) music.usingmmmmmm=true;
|
2020-01-01 21:29:24 +01:00
|
|
|
if (game.slowdown == 0) game.slowdown = 30;
|
|
|
|
|
|
|
|
switch(game.slowdown){
|
2020-04-02 23:43:50 +02:00
|
|
|
case 30: game.gameframerate=34; break;
|
|
|
|
case 24: game.gameframerate=41; break;
|
|
|
|
case 18: game.gameframerate=55; break;
|
|
|
|
case 12: game.gameframerate=83; break;
|
|
|
|
default: game.gameframerate=34; break;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 23:43:50 +02:00
|
|
|
//Check to see if you've already unlocked some achievements here from before the update
|
|
|
|
if (game.swnbestrank > 0){
|
2020-08-01 21:49:07 +02:00
|
|
|
if(game.swnbestrank >= 1) game.unlockAchievement("vvvvvvsupgrav5");
|
|
|
|
if(game.swnbestrank >= 2) game.unlockAchievement("vvvvvvsupgrav10");
|
|
|
|
if(game.swnbestrank >= 3) game.unlockAchievement("vvvvvvsupgrav15");
|
|
|
|
if(game.swnbestrank >= 4) game.unlockAchievement("vvvvvvsupgrav20");
|
|
|
|
if(game.swnbestrank >= 5) game.unlockAchievement("vvvvvvsupgrav30");
|
|
|
|
if(game.swnbestrank >= 6) game.unlockAchievement("vvvvvvsupgrav60");
|
2020-04-02 23:43:50 +02:00
|
|
|
}
|
|
|
|
|
2020-08-01 21:49:07 +02:00
|
|
|
if(game.unlock[5]) game.unlockAchievement("vvvvvvgamecomplete");
|
|
|
|
if(game.unlock[19]) game.unlockAchievement("vvvvvvgamecompleteflip");
|
|
|
|
if(game.unlock[20]) game.unlockAchievement("vvvvvvmaster");
|
2020-04-02 23:43:50 +02:00
|
|
|
|
|
|
|
if (game.bestgamedeaths > -1) {
|
|
|
|
if (game.bestgamedeaths <= 500) {
|
2020-08-01 21:49:07 +02:00
|
|
|
game.unlockAchievement("vvvvvvcomplete500");
|
2020-04-02 23:43:50 +02:00
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 250) {
|
2020-08-01 21:49:07 +02:00
|
|
|
game.unlockAchievement("vvvvvvcomplete250");
|
2020-04-02 23:43:50 +02:00
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 100) {
|
2020-08-01 21:49:07 +02:00
|
|
|
game.unlockAchievement("vvvvvvcomplete100");
|
2020-04-02 23:43:50 +02:00
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 50) {
|
2020-08-01 21:49:07 +02:00
|
|
|
game.unlockAchievement("vvvvvvcomplete50");
|
2020-04-02 23:43:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-01 21:49:07 +02:00
|
|
|
if(game.bestrank[0]>=3) game.unlockAchievement("vvvvvvtimetrial_station1_fixed");
|
|
|
|
if(game.bestrank[1]>=3) game.unlockAchievement("vvvvvvtimetrial_lab_fixed");
|
|
|
|
if(game.bestrank[2]>=3) game.unlockAchievement("vvvvvvtimetrial_tower_fixed");
|
|
|
|
if(game.bestrank[3]>=3) game.unlockAchievement("vvvvvvtimetrial_station2_fixed");
|
|
|
|
if(game.bestrank[4]>=3) game.unlockAchievement("vvvvvvtimetrial_warp_fixed");
|
|
|
|
if(game.bestrank[5]>=3) game.unlockAchievement("vvvvvvtimetrial_final_fixed");
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
obj.init();
|
|
|
|
|
2020-04-16 01:51:58 +02:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS)
|
2020-04-09 21:03:24 +02:00
|
|
|
if (startinplaytest) {
|
|
|
|
game.levelpage = 0;
|
|
|
|
game.playcustomlevel = 0;
|
2020-06-22 00:56:31 +02:00
|
|
|
game.playassets = playassets;
|
2020-08-13 14:59:14 +02:00
|
|
|
game.menustart = true;
|
2020-04-09 21:03:24 +02:00
|
|
|
|
2020-06-03 20:14:58 +02:00
|
|
|
ed.directoryList.clear();
|
|
|
|
ed.directoryList.push_back(playtestname);
|
2020-04-09 21:03:24 +02:00
|
|
|
|
|
|
|
LevelMetaData meta;
|
|
|
|
if (ed.getLevelMetaData(playtestname, meta)) {
|
2020-06-03 20:14:58 +02:00
|
|
|
ed.ListOfMetaData.clear();
|
|
|
|
ed.ListOfMetaData.push_back(meta);
|
2020-04-09 21:03:24 +02:00
|
|
|
} else {
|
2020-06-03 20:05:09 +02:00
|
|
|
ed.loadZips();
|
|
|
|
if (ed.getLevelMetaData(playtestname, meta)) {
|
2020-06-03 20:14:58 +02:00
|
|
|
ed.ListOfMetaData.clear();
|
|
|
|
ed.ListOfMetaData.push_back(meta);
|
2020-06-03 20:05:09 +02:00
|
|
|
} else {
|
|
|
|
printf("Level not found\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2020-04-09 21:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
game.loadcustomlevelstats();
|
|
|
|
game.customleveltitle=ed.ListOfMetaData[game.playcustomlevel].title;
|
|
|
|
game.customlevelfilename=ed.ListOfMetaData[game.playcustomlevel].filename;
|
|
|
|
if (savefileplaytest) {
|
|
|
|
game.playx = savex;
|
|
|
|
game.playy = savey;
|
|
|
|
game.playrx = saverx;
|
|
|
|
game.playry = savery;
|
|
|
|
game.playgc = savegc;
|
2020-07-11 20:55:26 +02:00
|
|
|
game.playmusic = savemusic;
|
2020-04-09 21:03:24 +02:00
|
|
|
game.cliplaytest = true;
|
|
|
|
script.startgamemode(23);
|
|
|
|
} else {
|
|
|
|
script.startgamemode(22);
|
|
|
|
}
|
|
|
|
|
|
|
|
graphics.fademode = 0;
|
|
|
|
}
|
2020-04-16 01:51:58 +02:00
|
|
|
#endif
|
2020-04-09 21:03:24 +02:00
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
key.isActive = true;
|
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-13 00:04:35 +02:00
|
|
|
game.gametimer = 0;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
while(!key.quitProgram)
|
|
|
|
{
|
2020-05-04 21:52:57 +02:00
|
|
|
f_time = SDL_GetTicks();
|
2020-07-14 06:17:20 +02:00
|
|
|
|
|
|
|
const Uint32 f_timetaken = f_time - f_timePrev;
|
|
|
|
if (!game.over30mode && f_timetaken < 34)
|
2020-05-04 21:52:57 +02:00
|
|
|
{
|
2020-07-14 06:17:20 +02:00
|
|
|
const volatile Uint32 f_delay = 34 - f_timetaken;
|
|
|
|
SDL_Delay(f_delay);
|
|
|
|
f_time = SDL_GetTicks();
|
2020-05-04 21:52:57 +02:00
|
|
|
}
|
|
|
|
|
2020-07-14 06:17:20 +02:00
|
|
|
f_timePrev = f_time;
|
|
|
|
|
|
|
|
timePrev = time_;
|
|
|
|
time_ = SDL_GetTicks();
|
|
|
|
|
|
|
|
deltaloop();
|
2020-06-14 20:45:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
game.savestats();
|
|
|
|
NETWORK_shutdown();
|
|
|
|
SDL_Quit();
|
|
|
|
FILESYSTEM_deinit();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-15 21:54:08 +02:00
|
|
|
void inline deltaloop()
|
2020-06-14 20:53:41 +02:00
|
|
|
{
|
2020-06-14 20:55:19 +02:00
|
|
|
//timestep limit to 30
|
|
|
|
const float rawdeltatime = static_cast<float>(time_ - timePrev);
|
|
|
|
accumulator += rawdeltatime;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-06-14 20:55:19 +02:00
|
|
|
Uint32 timesteplimit;
|
|
|
|
if (game.gamestate == EDITORMODE)
|
|
|
|
{
|
|
|
|
timesteplimit = 24;
|
|
|
|
}
|
2020-07-01 12:04:17 +02:00
|
|
|
else if (game.gamestate == GAMEMODE)
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
|
|
|
timesteplimit = game.gameframerate;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
timesteplimit = 34;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (accumulator >= timesteplimit)
|
|
|
|
{
|
|
|
|
accumulator = fmodf(accumulator, timesteplimit);
|
|
|
|
|
2020-06-14 20:57:33 +02:00
|
|
|
fixedloop();
|
|
|
|
}
|
|
|
|
const float alpha = game.over30mode ? static_cast<float>(accumulator) / timesteplimit : 1.0f;
|
|
|
|
graphics.alpha = alpha;
|
|
|
|
|
2020-06-27 02:45:53 +02:00
|
|
|
if (key.isActive)
|
2020-06-14 20:57:33 +02:00
|
|
|
{
|
|
|
|
switch (game.gamestate)
|
|
|
|
{
|
|
|
|
case PRELOADER:
|
|
|
|
preloaderrender();
|
|
|
|
break;
|
2020-06-20 00:23:28 +02:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS) && !defined(NO_EDITOR)
|
2020-06-14 20:57:33 +02:00
|
|
|
case EDITORMODE:
|
|
|
|
graphics.flipmode = false;
|
|
|
|
editorrender();
|
|
|
|
break;
|
2020-06-14 21:03:40 +02:00
|
|
|
#endif
|
2020-06-14 20:57:33 +02:00
|
|
|
case TITLEMODE:
|
|
|
|
titlerender();
|
|
|
|
break;
|
|
|
|
case GAMEMODE:
|
|
|
|
gamerender();
|
|
|
|
break;
|
|
|
|
case MAPMODE:
|
|
|
|
maprender();
|
|
|
|
break;
|
|
|
|
case TELEPORTERMODE:
|
|
|
|
teleporterrender();
|
|
|
|
break;
|
|
|
|
case GAMECOMPLETE:
|
|
|
|
gamecompleterender();
|
|
|
|
break;
|
|
|
|
case GAMECOMPLETE2:
|
|
|
|
gamecompleterender2();
|
|
|
|
break;
|
|
|
|
case CLICKTOSTART:
|
|
|
|
help.updateglow();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gameScreen.FlipScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 21:54:08 +02:00
|
|
|
void inline fixedloop()
|
2020-06-14 20:57:33 +02:00
|
|
|
{
|
2020-06-14 20:58:27 +02:00
|
|
|
// Update network per frame.
|
|
|
|
NETWORK_update();
|
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
key.Poll();
|
|
|
|
if(key.toggleFullscreen)
|
|
|
|
{
|
|
|
|
gameScreen.toggleFullScreen();
|
|
|
|
game.fullscreen = !game.fullscreen;
|
|
|
|
key.toggleFullscreen = false;
|
|
|
|
|
|
|
|
key.keymap.clear(); //we lost the input due to a new window.
|
2020-06-30 06:06:19 +02:00
|
|
|
if (game.glitchrunnermode)
|
|
|
|
{
|
|
|
|
game.press_left = false;
|
|
|
|
game.press_right = false;
|
|
|
|
game.press_action = true;
|
|
|
|
game.press_map = false;
|
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
2020-06-27 02:45:53 +02:00
|
|
|
if(!key.isActive)
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
Mix_Pause(-1);
|
|
|
|
Mix_PauseMusic();
|
|
|
|
|
|
|
|
if (!game.blackout)
|
|
|
|
{
|
|
|
|
FillRect(graphics.backBuffer, 0x00000000);
|
|
|
|
graphics.bprint(5, 110, "Game paused", 196 - help.glow, 255 - help.glow, 196 - help.glow, true);
|
|
|
|
graphics.bprint(5, 120, "[click to resume]", 196 - help.glow, 255 - help.glow, 196 - help.glow, true);
|
|
|
|
graphics.bprint(5, 220, "Press M to mute in game", 164 - help.glow, 196 - help.glow, 164 - help.glow, true);
|
|
|
|
graphics.bprint(5, 230, "Press N to mute music only", 164 - help.glow, 196 - help.glow, 164 - help.glow, true);
|
2020-06-14 20:49:23 +02:00
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
graphics.render();
|
|
|
|
gameScreen.FlipScreen();
|
|
|
|
//We are minimised, so lets put a bit of a delay to save CPU
|
|
|
|
SDL_Delay(100);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Mix_Resume(-1);
|
|
|
|
Mix_ResumeMusic();
|
|
|
|
game.gametimer++;
|
|
|
|
graphics.cutscenebarstimer();
|
2020-06-14 20:55:19 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
switch(game.gamestate)
|
2020-06-14 20:49:23 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
case PRELOADER:
|
|
|
|
preloaderlogic();
|
|
|
|
break;
|
2020-06-20 00:23:28 +02:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS) && !defined(NO_EDITOR)
|
2020-06-14 20:57:57 +02:00
|
|
|
case EDITORMODE:
|
|
|
|
//Input
|
|
|
|
editorinput();
|
|
|
|
////Logic
|
|
|
|
editorlogic();
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case TITLEMODE:
|
|
|
|
//Input
|
|
|
|
titleinput();
|
|
|
|
////Logic
|
|
|
|
titlelogic();
|
|
|
|
break;
|
|
|
|
case GAMEMODE:
|
Fix the two-frame-delay when entering a room with an "init" script
This patch is very kludge-y, but at least it fixes a semi-noticeable
visual issue in custom levels that use internal scripts to spawn
entities when loading a room.
Basically, the problem here is that when the game checks for script
boxes and sets newscript, newscript has already been processed for that
frame, and when the game does load a script, script.run() has already
been processed for that frame.
That issue can be fixed, but it turns out that due to my over-30-FPS
game loop changes, there's now ANOTHER visible frame of delay between
room load and entity creation, because the render function gets called
in between the script being loaded at the end of gamelogic() and the
script actually getting run.
So... I have to temporary move script.run() to the end of gamelogic()
(in map.twoframedelayfix()), and make sure it doesn't get run next
frame, because double-evaluations are bad. To do that, I have to
introduce the kludge variable script.dontrunnextframe, which does
exactly as it says.
And with all that work, the two-frame (now three-frame) delay is fixed.
2020-06-28 02:08:57 +02:00
|
|
|
// WARNING: If updating this code, don't forget to update Map.cpp mapclass::twoframedelayfix()
|
|
|
|
|
|
|
|
// Ugh, I hate this kludge variable but it's the only way to do it
|
|
|
|
if (script.dontrunnextframe)
|
|
|
|
{
|
|
|
|
script.dontrunnextframe = false;
|
|
|
|
}
|
|
|
|
else if (script.running)
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
script.run();
|
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
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-10 05:58:58 +02:00
|
|
|
//Update old lerp positions of entities - has to be done BEFORE gameinput!
|
2020-06-14 20:57:57 +02:00
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
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-10 05:58:58 +02:00
|
|
|
obj.entities[i].lerpoldxp = obj.entities[i].xp;
|
|
|
|
obj.entities[i].lerpoldyp = obj.entities[i].yp;
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
2020-06-14 20:49:23 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
gameinput();
|
|
|
|
gamelogic();
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
case MAPMODE:
|
|
|
|
mapinput();
|
|
|
|
maplogic();
|
|
|
|
break;
|
|
|
|
case TELEPORTERMODE:
|
|
|
|
if(game.useteleporter)
|
|
|
|
{
|
|
|
|
teleporterinput();
|
|
|
|
}
|
|
|
|
else
|
Don't print useless false error message when toggling fullscreen
Whenever you would press Alt+Enter, or Alt+F, or on macOS Command+Enter,
or on macOS Command+F, or F11, the game would print this useless error
message to console, every single time: "Error: failed: " and it would
concatenate SDL_GetError() after it, but most of the time SDL_GetError()
is blank, so it would print just that.
Instead, what the fullscreen shortcut will now do is check the result of
the relevant SDL functions, BEFORE it decides to print an error message.
And when it DOES print an error message, it will be less vague and will
say instead "Error: toggling fullscreen failed: <output of
SDL_GetError()>".
This means Screen::ResizeScreen() and Screen::toggleFullScreen() are now
int-returning functions. Ideally, every function interfacing with SDL
would return an error code, but that's too much for this simple patch.
Additionally, I took the opportunity to clean up the surrounding
formatting of the code a bit, most notably dedenting the
keypress-clearing stuff by one tab level, converting the
shortcut-handling code to spaces, and removing commented-out code.
2020-03-13 08:23:48 +01:00
|
|
|
{
|
2020-06-14 20:55:19 +02:00
|
|
|
if (script.running)
|
2020-06-14 20:49:23 +02:00
|
|
|
{
|
2020-06-14 20:55:19 +02:00
|
|
|
script.run();
|
2020-06-14 20:49:23 +02:00
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
gameinput();
|
2020-06-14 20:49:23 +02:00
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
maplogic();
|
|
|
|
break;
|
|
|
|
case GAMECOMPLETE:
|
|
|
|
//Input
|
|
|
|
gamecompleteinput();
|
|
|
|
//Logic
|
|
|
|
gamecompletelogic();
|
|
|
|
break;
|
|
|
|
case GAMECOMPLETE2:
|
|
|
|
//Input
|
|
|
|
gamecompleteinput2();
|
|
|
|
//Logic
|
|
|
|
gamecompletelogic2();
|
|
|
|
break;
|
|
|
|
case CLICKTOSTART:
|
|
|
|
break;
|
|
|
|
default:
|
Fix frame-ordering backspacing empty line bug in script editor
There is a long-standing bug with the script editor where if you delete
the last character of a line, it IMMEDIATELY deletes the line you're on,
and then moves your cursor back to the previous line. This is annoying,
to say the least.
The reason for this is that, in the sequence of events that happens in
one frame (known as frame ordering), the code that backspaces one
character from the line when you press Backspace is ran BEFORE the code
to remove an empty line if you backspace it is ran. The former is
located in key.Poll(), and the latter is located in editorinput().
Thus, when you press Backspace, the game first runs key.Poll(), sees
that you've pressed Backspace, and dutifully removes the last character
from a line. The line is now empty. Then, when the game gets around to
the "Are you pressing Backspace on an empty line?" check in
editorinput(), it thinks that you're pressing Backspace on an empty
line, and then does the usual line-removing stuff.
And actually, when it does the check in editorinput(), it ACTUALLY asks
"Are you pressing Backspace on THIS frame and was the line empty LAST
frame?" because it's checking against its own copy of the input buffer,
before copying the input buffer to its own local copy. So the problem
only happens if you press and hold Backspace for more than 1 frame.
It's a small consolation prize for this annoyance, getting to
tap-tap-tap Backspace in the hopes that you only press it for 1 frame,
while in the middle of something more important to do like, oh I don't
know, writing a script.
So there are two potential solutions here:
(1) Just change the frame ordering around.
This is risky to say the least, because I'm not sure what behavior
depends on exactly which frame order. It's not like it's key.Poll()
and then IMMEDIATELY afterwards editorinput() is run, it's more
like key.Poll(), some things that obviously depend on key.Poll()
running before them, and THEN editorinput(). Also, editorinput() is
only one possible thing that could be ran afterwards, on the next
frame we could be running something else entirely instead.
(2) Add a kludge variable to signal when the line is ALREADY empty so
the game doesn't re-check the already-empty line and conclude that
you're already immediately backspacing an empty line.
I went with (2) for this commit, and I've added the kludge variable
key.linealreadyemptykludge.
However, that by itself isn't enough to fix it. It only adds about a
frame or so of delay before the game goes right back to saying "Oh,
you're ALREADY somehow pressing backspace again? I'll just delete this
line real quick" and the behavior is basically the same as before,
except now you have to hit Backspace for TWO frames or less instead of
one in order to not have it happen.
What we need is to have a delay set as well, when the game deletes the
last line of a char. So I set ed.keydelay to 6 as well if editorinput()
sses that key.linealreadyemptykludge is on.
2020-01-19 03:17:46 +01:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
break;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
2020-06-14 20:49:23 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
//Screen effects timers
|
2020-06-27 02:45:53 +02:00
|
|
|
if (key.isActive && game.flashlight > 0)
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
game.flashlight--;
|
|
|
|
}
|
2020-06-27 02:45:53 +02:00
|
|
|
if (key.isActive && game.screenshake > 0)
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
game.screenshake--;
|
|
|
|
graphics.updatescreenshake();
|
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if (graphics.screenbuffer->badSignalEffect)
|
|
|
|
{
|
|
|
|
UpdateFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
//We did editorinput, now it's safe to turn this off
|
|
|
|
key.linealreadyemptykludge = false;
|
2020-06-14 20:49:23 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if (game.savemystats)
|
|
|
|
{
|
|
|
|
game.savemystats = false;
|
|
|
|
game.savestats();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Mute button
|
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 08:43:25 +02:00
|
|
|
if (key.isDown(KEYBOARD_m) && game.mutebutton<=0 && !key.textentry())
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
game.mutebutton = 8;
|
|
|
|
if (game.muted)
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
game.muted = false;
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
else
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
game.muted = true;
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
}
|
|
|
|
if(game.mutebutton>0)
|
|
|
|
{
|
|
|
|
game.mutebutton--;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
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 08:43:25 +02:00
|
|
|
if (key.isDown(KEYBOARD_n) && game.musicmutebutton <= 0 && !key.textentry())
|
2020-06-14 20:57:57 +02:00
|
|
|
{
|
|
|
|
game.musicmutebutton = 8;
|
|
|
|
game.musicmuted = !game.musicmuted;
|
|
|
|
}
|
|
|
|
if (game.musicmutebutton > 0)
|
|
|
|
{
|
|
|
|
game.musicmutebutton--;
|
|
|
|
}
|
2020-04-28 23:44:53 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if (game.muted)
|
|
|
|
{
|
|
|
|
Mix_VolumeMusic(0) ;
|
|
|
|
Mix_Volume(-1,0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Mix_Volume(-1,MIX_MAX_VOLUME);
|
|
|
|
|
2020-10-11 08:42:02 +02:00
|
|
|
if (game.musicmuted)
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
Mix_VolumeMusic(0);
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-11 08:42:02 +02:00
|
|
|
Mix_VolumeMusic(music.musicVolume);
|
2020-06-14 20:55:19 +02:00
|
|
|
}
|
2020-06-14 20:57:57 +02:00
|
|
|
}
|
2020-04-29 00:02:37 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if (key.resetWindow)
|
|
|
|
{
|
|
|
|
key.resetWindow = false;
|
|
|
|
gameScreen.ResizeScreen(-1, -1);
|
|
|
|
}
|
2020-06-14 20:49:23 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
music.processmusic();
|
|
|
|
graphics.processfade();
|
|
|
|
game.gameclock();
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|