2020-01-01 21:29:24 +01:00
|
|
|
#include <SDL.h>
|
|
|
|
#include "SoundSystem.h"
|
|
|
|
|
|
|
|
#include "UtilityClass.h"
|
|
|
|
#include "Game.h"
|
|
|
|
#include "Graphics.h"
|
|
|
|
#include "KeyPoll.h"
|
2020-04-04 07:53:31 +02:00
|
|
|
#include "Render.h"
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
#include "Tower.h"
|
|
|
|
#include "WarpClass.h"
|
|
|
|
#include "Labclass.h"
|
|
|
|
#include "Finalclass.h"
|
|
|
|
#include "Map.h"
|
|
|
|
|
|
|
|
#include "Screen.h"
|
|
|
|
|
|
|
|
#include "Script.h"
|
|
|
|
|
|
|
|
#include "Logic.h"
|
|
|
|
|
|
|
|
#include "Input.h"
|
|
|
|
#include "editor.h"
|
|
|
|
#include "preloader.h"
|
|
|
|
|
|
|
|
#include "FileSystemUtils.h"
|
|
|
|
#include "Network.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
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;
|
|
|
|
volatile Uint32 f_accumulator = 0;
|
|
|
|
|
2020-06-15 21:54:08 +02:00
|
|
|
void inline gameloop();
|
|
|
|
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-06-22 03:37:44 +02:00
|
|
|
int v = std::atoi(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
|
|
|
|
#undef ARG_IS
|
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
|
|
|
|
);
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
NETWORK_init();
|
|
|
|
|
2020-06-13 20:44:01 +02:00
|
|
|
gameScreen.init();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
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
|
|
|
|
|
|
|
const SDL_PixelFormat* fmt = gameScreen.GetFormat();
|
2020-03-12 08:56:06 +01:00
|
|
|
graphics.backBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.backBuffer, SDL_BLENDMODE_NONE);
|
2020-03-12 08:56:06 +01:00
|
|
|
graphics.footerbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 10, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
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
|
|
|
|
|
|
|
graphics.ghostbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.ghostbuffer, SDL_BLENDMODE_BLEND);
|
|
|
|
SDL_SetSurfaceAlphaMod(graphics.ghostbuffer, 127);
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
graphics.Makebfont();
|
|
|
|
|
2020-01-11 01:37:23 +01:00
|
|
|
graphics.foregroundBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320 ,240 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask );
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.foregroundBuffer, SDL_BLENDMODE_NONE);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
graphics.screenbuffer = &gameScreen;
|
|
|
|
|
|
|
|
graphics.menubuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320 ,240 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask );
|
|
|
|
SDL_SetSurfaceBlendMode(graphics.menubuffer, SDL_BLENDMODE_NONE);
|
|
|
|
|
2020-05-02 22:11:36 +02:00
|
|
|
graphics.towerbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320+16 ,240+16 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask );
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.towerbuffer, SDL_BLENDMODE_NONE);
|
2020-05-02 22:11:36 +02:00
|
|
|
graphics.towerbuffer_lerp = SDL_CreateRGBSurface(SDL_SWSURFACE, 320+16, 240+16, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
2020-04-29 20:03:17 +02:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.towerbuffer, SDL_BLENDMODE_NONE);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-04-02 23:43:50 +02:00
|
|
|
graphics.tempBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE ,320 ,240 ,fmt->BitsPerPixel,fmt->Rmask,fmt->Gmask,fmt->Bmask,fmt->Amask );
|
2020-01-01 21:29:24 +01:00
|
|
|
SDL_SetSurfaceBlendMode(graphics.tempBuffer, SDL_BLENDMODE_NONE);
|
|
|
|
|
2020-04-03 04:19:35 +02:00
|
|
|
game.gamestate = PRELOADER;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
game.menustart = false;
|
|
|
|
game.mainmenu = 0;
|
|
|
|
|
|
|
|
map.ypos = (700-29) * 8;
|
|
|
|
map.bypos = map.ypos / 2;
|
|
|
|
|
|
|
|
//Moved screensetting init here from main menu V2.1
|
2020-03-31 02:16:02 +02:00
|
|
|
game.loadstats();
|
Work around SDL2 bug where VSync hint only applies on renderer creation
Ugh, this is terrible and stupid and I hate myself for it.
Anyway, since the SDL2 VSync hint only applies when the renderer is
created, we have to re-create the renderer whenever VSync is toggled.
However, this also means we need to re-create m_screenTexture as well,
AND call ResizeScreen() after that or else the letterbox/integer modes
won't be applied.
Unfortunately, this means that in main(), gameScreen.init() will create
a renderer only to be destroyed later by graphics.processVsync().
There's not much we can do about this. Fixing this would require putting
graphics.processVsync() before gameScreen.init(). However, in order to
know whether the user has VSync set, we would have to call
game.loadstats() first, but wait, we can't, because game.loadstats()
mutates gameScreen! Gahhhhhh!!!!
@leo60228 suggested to fix that problem (
https://github.com/TerryCavanagh/VVVVVV/pull/220#issuecomment-624217939
) by adding NULL checks to game.loadstats() and then calling it twice,
but then you're trading wastefully creating a renderer only to be
destroyed, for wastefully opening and parsing unlock.vvv twice instead
of once. In either case, you're doing something twice and wasting work.
2020-06-19 23:12:56 +02:00
|
|
|
|
|
|
|
// FIXME: Thanks to having to work around an SDL2 bug, this destroys the
|
|
|
|
// renderer created by Screen::init(), which is a bit wasteful!
|
|
|
|
// This is annoying to fix because we'd have to call gameScreen.init() after
|
|
|
|
// game.loadstats(), but game.loadstats() assumes gameScreen.init() is already called!
|
2020-05-04 22:19:47 +02:00
|
|
|
graphics.processVsync();
|
|
|
|
|
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){
|
|
|
|
if(game.swnbestrank >= 1) NETWORK_unlockAchievement("vvvvvvsupgrav5");
|
|
|
|
if(game.swnbestrank >= 2) NETWORK_unlockAchievement("vvvvvvsupgrav10");
|
|
|
|
if(game.swnbestrank >= 3) NETWORK_unlockAchievement("vvvvvvsupgrav15");
|
|
|
|
if(game.swnbestrank >= 4) NETWORK_unlockAchievement("vvvvvvsupgrav20");
|
|
|
|
if(game.swnbestrank >= 5) NETWORK_unlockAchievement("vvvvvvsupgrav30");
|
|
|
|
if(game.swnbestrank >= 6) NETWORK_unlockAchievement("vvvvvvsupgrav60");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(game.unlock[5]) NETWORK_unlockAchievement("vvvvvvgamecomplete");
|
|
|
|
if(game.unlock[19]) NETWORK_unlockAchievement("vvvvvvgamecompleteflip");
|
|
|
|
if(game.unlock[20]) NETWORK_unlockAchievement("vvvvvvmaster");
|
|
|
|
|
|
|
|
if (game.bestgamedeaths > -1) {
|
|
|
|
if (game.bestgamedeaths <= 500) {
|
|
|
|
NETWORK_unlockAchievement("vvvvvvcomplete500");
|
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 250) {
|
|
|
|
NETWORK_unlockAchievement("vvvvvvcomplete250");
|
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 100) {
|
|
|
|
NETWORK_unlockAchievement("vvvvvvcomplete100");
|
|
|
|
}
|
|
|
|
if (game.bestgamedeaths <= 50) {
|
|
|
|
NETWORK_unlockAchievement("vvvvvvcomplete50");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(game.bestrank[0]>=3) NETWORK_unlockAchievement("vvvvvvtimetrial_station1_fixed");
|
|
|
|
if(game.bestrank[1]>=3) NETWORK_unlockAchievement("vvvvvvtimetrial_lab_fixed");
|
|
|
|
if(game.bestrank[2]>=3) NETWORK_unlockAchievement("vvvvvvtimetrial_tower_fixed");
|
|
|
|
if(game.bestrank[3]>=3) NETWORK_unlockAchievement("vvvvvvtimetrial_station2_fixed");
|
|
|
|
if(game.bestrank[4]>=3) NETWORK_unlockAchievement("vvvvvvtimetrial_warp_fixed");
|
|
|
|
if(game.bestrank[5]>=3) NETWORK_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-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;
|
|
|
|
game.cliplaytest = true;
|
|
|
|
music.play(savemusic);
|
|
|
|
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_timePrev = f_time;
|
|
|
|
f_time = SDL_GetTicks();
|
|
|
|
const float f_rawdeltatime = static_cast<float>(f_time - f_timePrev);
|
|
|
|
if (!game.over30mode)
|
|
|
|
{
|
|
|
|
f_accumulator += f_rawdeltatime;
|
|
|
|
}
|
|
|
|
|
2020-06-14 20:45:36 +02:00
|
|
|
gameloop();
|
|
|
|
}
|
|
|
|
|
|
|
|
game.savestats();
|
|
|
|
NETWORK_shutdown();
|
|
|
|
SDL_Quit();
|
|
|
|
FILESYSTEM_deinit();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-15 21:54:08 +02:00
|
|
|
void inline gameloop()
|
2020-06-14 20:45:36 +02:00
|
|
|
{
|
2020-06-14 20:49:23 +02:00
|
|
|
while ((game.over30mode || f_accumulator >= 34) && !key.quitProgram)
|
|
|
|
{
|
|
|
|
if (game.over30mode)
|
2020-05-04 21:52:57 +02:00
|
|
|
{
|
2020-06-14 20:49:23 +02:00
|
|
|
f_accumulator = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
f_accumulator = fmodf(f_accumulator, 34);
|
|
|
|
}
|
2020-05-04 21:52:57 +02:00
|
|
|
|
2020-06-14 20:49:23 +02:00
|
|
|
timePrev = time_;
|
|
|
|
time_ = SDL_GetTicks();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
2020-06-14 20:53:41 +02:00
|
|
|
deltaloop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if(!gameScreen.isWindowed)
|
2020-06-14 20:49:23 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if(game.gamestate == EDITORMODE)
|
|
|
|
{
|
|
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
}
|
2020-06-14 20:55:19 +02:00
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
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
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
//Update old positions of entities - has to be done BEFORE gameinput!
|
|
|
|
for (size_t i = 0; i < obj.entities.size(); i++)
|
2020-06-14 20:55:19 +02:00
|
|
|
{
|
2020-06-14 20:57:57 +02:00
|
|
|
obj.entities[i].oldxp = obj.entities[i].xp;
|
|
|
|
obj.entities[i].oldyp = 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
|
2020-06-20 00:23:28 +02:00
|
|
|
#if !defined(NO_CUSTOM_LEVELS) && !defined(NO_EDITOR)
|
2020-06-14 20:57:57 +02:00
|
|
|
bool inEditor = ed.textentry || ed.scripthelppage == 1;
|
2020-04-02 23:43:50 +02:00
|
|
|
#else
|
2020-06-14 20:57:57 +02:00
|
|
|
bool inEditor = false;
|
2020-04-02 23:43:50 +02:00
|
|
|
#endif
|
2020-06-14 20:57:57 +02:00
|
|
|
if (key.isDown(KEYBOARD_m) && game.mutebutton<=0 && !inEditor)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2020-06-14 20:57:57 +02:00
|
|
|
if (key.isDown(KEYBOARD_n) && game.musicmutebutton <= 0 && !inEditor)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (game.musicmuted || game.completestop)
|
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-06-14 20:57:57 +02:00
|
|
|
Mix_VolumeMusic(MIX_MAX_VOLUME);
|
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
|
|
|
}
|