1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-16 16:09:42 +01:00
VVVVVV/desktop_version/src/main.cpp

796 lines
20 KiB
C++
Raw Normal View History

2020-01-01 21:29:24 +01:00
#include <SDL.h>
#include <stdio.h>
2020-01-01 21:29:24 +01:00
#include "DeferCallbacks.h"
#include "editor.h"
#include "Enums.h"
#include "Entity.h"
#include "Exit.h"
#include "FileSystemUtils.h"
2020-01-01 21:29:24 +01:00
#include "Game.h"
#include "Graphics.h"
#include "Input.h"
2020-01-01 21:29:24 +01:00
#include "KeyPoll.h"
#include "Logic.h"
2020-01-01 21:29:24 +01:00
#include "Map.h"
#include "Music.h"
#include "Network.h"
#include "preloader.h"
#include "Render.h"
#include "RenderFixed.h"
2020-01-01 21:29:24 +01:00
#include "Screen.h"
#include "Script.h"
#include "SoundSystem.h"
#include "UtilityClass.h"
2020-01-01 21:29:24 +01:00
scriptclass script;
#if !defined(NO_CUSTOM_LEVELS)
std::vector<edentities> edentity;
editorclass ed;
#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;
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
static bool startinplaytest = false;
static bool savefileplaytest = false;
static int savex = 0;
static int savey = 0;
static int saverx = 0;
static int savery = 0;
static int savegc = 0;
static int savemusic = 0;
static std::string playassets;
static std::string playtestname;
static volatile Uint32 time_ = 0;
static volatile Uint32 timePrev = 0;
static volatile Uint32 accumulator = 0;
static volatile Uint32 f_time = 0;
static volatile Uint32 f_timePrev = 0;
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
enum FuncType
{
Func_null,
Func_fixed,
Func_input,
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
Func_delta
};
struct ImplFunc
{
enum FuncType type;
void (*func)(void);
};
static void runscript(void)
{
script.run();
}
static void teleportermodeinput(void)
{
if (game.useteleporter)
{
teleporterinput();
}
else
{
script.run();
gameinput();
}
}
/* Only gets used in EDITORMODE. I assume the compiler will optimize this away
* if this is a NO_CUSTOM_LEVELS or NO_EDITOR build
*/
static void flipmodeoff(void)
{
graphics.flipmode = false;
}
static void focused_begin(void);
static void focused_end(void);
static const inline struct ImplFunc* get_gamestate_funcs(
const int gamestate,
int* num_implfuncs
) {
switch (gamestate)
{
#define FUNC_LIST_BEGIN(GAMESTATE) \
case GAMESTATE: \
{ \
static const struct ImplFunc implfuncs[] = { \
{Func_fixed, focused_begin},
#define FUNC_LIST_END \
{Func_fixed, focused_end} \
}; \
*num_implfuncs = SDL_arraysize(implfuncs); \
return implfuncs; \
}
FUNC_LIST_BEGIN(GAMEMODE)
{Func_fixed, runscript},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, gamerenderfixed},
{Func_delta, gamerender},
{Func_input, gameinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, gamelogic},
FUNC_LIST_END
FUNC_LIST_BEGIN(TITLEMODE)
{Func_input, titleinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, titlerenderfixed},
{Func_delta, titlerender},
{Func_fixed, titlelogic},
FUNC_LIST_END
FUNC_LIST_BEGIN(MAPMODE)
{Func_fixed, maprenderfixed},
{Func_delta, maprender},
{Func_input, mapinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, maplogic},
FUNC_LIST_END
FUNC_LIST_BEGIN(TELEPORTERMODE)
{Func_fixed, maprenderfixed},
{Func_delta, teleporterrender},
{Func_input, teleportermodeinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, maplogic},
FUNC_LIST_END
FUNC_LIST_BEGIN(GAMECOMPLETE)
{Func_fixed, gamecompleterenderfixed},
{Func_delta, gamecompleterender},
{Func_input, gamecompleteinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, gamecompletelogic},
FUNC_LIST_END
FUNC_LIST_BEGIN(GAMECOMPLETE2)
{Func_delta, gamecompleterender2},
{Func_input, gamecompleteinput2},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, gamecompletelogic2},
FUNC_LIST_END
#if !defined(NO_CUSTOM_LEVELS) && !defined(NO_EDITOR)
FUNC_LIST_BEGIN(EDITORMODE)
{Func_fixed, flipmodeoff},
{Func_input, editorinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, editorrenderfixed},
{Func_delta, editorrender},
{Func_fixed, editorlogic},
FUNC_LIST_END
#endif
FUNC_LIST_BEGIN(PRELOADER)
{Func_input, preloaderinput},
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
{Func_fixed, preloaderrenderfixed},
{Func_delta, preloaderrender},
FUNC_LIST_END
#undef FUNC_LIST_END
#undef FUNC_LIST_BEGIN
}
SDL_assert(0 && "Invalid gamestate!");
return NULL;
}
enum IndexCode
{
Index_none,
Index_end
};
static const struct ImplFunc* gamestate_funcs = NULL;
static int num_gamestate_funcs = 0;
static int gamestate_func_index = -1;
static enum IndexCode increment_gamestate_func_index(void)
{
gamestate_func_index++;
if (gamestate_func_index == num_gamestate_funcs)
{
/* Reached the end of current gamestate order.
* Re-fetch for new order if gamestate changed.
*/
gamestate_funcs = get_gamestate_funcs(
game.gamestate,
&num_gamestate_funcs
);
/* Also run callbacks that were deferred to end of func sequence. */
DEFER_execute_callbacks();
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
gamestate_func_index = 0;
return Index_end;
}
return Index_none;
}
static void unfocused_run(void);
static const struct ImplFunc unfocused_func_list[] = {
{
Func_input, /* we still need polling when unfocused */
unfocused_run
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
};
static const struct ImplFunc* unfocused_funcs = unfocused_func_list;
static int num_unfocused_funcs = SDL_arraysize(unfocused_func_list);
2021-03-21 22:19:08 +01:00
static int unfocused_func_index = 0; // This does not get incremented on start, do NOT use -1!
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
static enum IndexCode increment_unfocused_func_index(void)
{
unfocused_func_index++;
if (unfocused_func_index == num_unfocused_funcs)
{
unfocused_func_index = 0;
return Index_end;
}
return Index_none;
}
static const struct ImplFunc** active_funcs = NULL;
static int* num_active_funcs = NULL;
static int* active_func_index = NULL;
static enum IndexCode (*increment_func_index)(void) = NULL;
enum LoopCode
{
Loop_continue,
Loop_stop
};
static enum LoopCode loop_assign_active_funcs(void)
{
if (key.isActive)
{
active_funcs = &gamestate_funcs;
num_active_funcs = &num_gamestate_funcs;
active_func_index = &gamestate_func_index;
increment_func_index = &increment_gamestate_func_index;
}
else
{
active_funcs = &unfocused_funcs;
num_active_funcs = &num_unfocused_funcs;
active_func_index = &unfocused_func_index;
increment_func_index = &increment_unfocused_func_index;
}
return Loop_continue;
}
static enum LoopCode loop_run_active_funcs(void)
{
while ((*active_funcs)[*active_func_index].type != Func_delta)
{
const struct ImplFunc* implfunc = &(*active_funcs)[*active_func_index];
enum IndexCode index_code;
if (implfunc->type == Func_input && !game.inputdelay)
{
key.Poll();
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
if (implfunc->type != Func_null && implfunc->func != NULL)
{
implfunc->func();
}
index_code = increment_func_index();
if (index_code == Index_end)
{
return Loop_continue;
}
}
/* About to switch over to rendering... but call this first. */
graphics.renderfixedpre();
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
return Loop_stop;
}
static enum LoopCode loop_begin(void);
static enum LoopCode loop_end(void);
static enum LoopCode (*const meta_funcs[])(void) = {
loop_begin,
loop_assign_active_funcs,
loop_run_active_funcs,
loop_end
};
static int meta_func_index = 0;
static void inline fixedloop(void)
{
while (true)
{
enum LoopCode loop_code = meta_funcs[meta_func_index]();
if (loop_code == Loop_stop)
{
break;
}
meta_func_index = (meta_func_index + 1) % SDL_arraysize(meta_funcs);
}
}
static void inline deltaloop(void);
static void cleanup(void);
2020-01-01 21:29:24 +01:00
int main(int argc, char *argv[])
{
char* baseDir = NULL;
char* assetsPath = NULL;
for (int i = 1; i < argc; ++i)
{
Reduce dependency on libc functions During 2.3 development, there's been a gradual shift to using SDL stdlib functions instead of libc functions, but there are still some libc functions (or the same libc function but from the STL) in the code. Well, this patch replaces all the rest of them in one fell swoop. SDL's stdlib can replace most of these, but its SDL_min() and SDL_max() are inadequate - they aren't really functions, they're more like macros with a nasty penchant for double-evaluation. So I just made my own VVV_min() and VVV_max() functions and placed them in Maths.h instead, then replaced all the previous usages of min(), max(), std::min(), std::max(), SDL_min(), and SDL_max() with VVV_min() and VVV_max(). Additionally, there's no SDL_isxdigit(), so I just implemented my own VVV_isxdigit(). SDL has SDL_malloc() and SDL_free(), but they have some refcounting built in to them, so in order to use them with LodePNG, I have to replace the malloc() and free() that LodePNG uses. Which isn't too hard, I did it in a new file called ThirdPartyDeps.c, and LodePNG is now compiled with the LODEPNG_NO_COMPILE_ALLOCATORS definition. Lastly, I also refactored the awful strcpy() and strcat() usages in PLATFORM_migrateSaveData() to use SDL_snprintf() instead. I know save migration is getting axed in 2.4, but it still bothers me to have something like that in the codebase otherwise. Without further ado, here is the full list of functions that the codebase now uses: - SDL_strlcpy() instead of strcpy() - SDL_strlcat() instead of strcat() - SDL_snprintf() instead of sprintf(), strcpy(), or strcat() (see above) - VVV_min() instead of min(), std::min(), or SDL_min() - VVV_max() instead of max(), std::max(), or SDL_max() - VVV_isxdigit() instead of isxdigit() - SDL_strcmp() instead of strcmp() - SDL_strcasecmp() instead of strcasecmp() or Win32 strcmpi() - SDL_strstr() instead of strstr() - SDL_strlen() instead of strlen() - SDL_sscanf() instead of sscanf() - SDL_getenv() instead of getenv() - SDL_malloc() instead of malloc() (replacing in LodePNG as well) - SDL_free() instead of free() (replacing in LodePNG as well)
2021-01-12 01:17:45 +01:00
#define ARG(name) (SDL_strcmp(argv[i], name) == 0)
#define ARG_INNER(code) \
if (i + 1 < argc) \
{ \
code \
} \
else \
{ \
printf("%s option requires one argument.\n", argv[i]); \
VVV_exit(1); \
}
if (ARG("-renderer"))
{
ARG_INNER({
i++;
SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, argv[i], SDL_HINT_OVERRIDE);
})
}
else if (ARG("-basedir"))
{
ARG_INNER({
i++;
baseDir = argv[i];
})
}
else if (ARG("-assets"))
{
ARG_INNER({
i++;
assetsPath = argv[i];
})
}
else if (ARG("-playing") || ARG("-p"))
{
ARG_INNER({
i++;
startinplaytest = true;
playtestname = std::string("levels/");
playtestname.append(argv[i]);
playtestname.append(std::string(".vvvvvv"));
})
}
else if (ARG("-playx") || ARG("-playy") ||
ARG("-playrx") || ARG("-playry") ||
ARG("-playgc") || ARG("-playmusic"))
{
ARG_INNER({
savefileplaytest = true;
int v = help.Int(argv[i+1]);
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;
i++;
})
}
else if (ARG("-playassets"))
{
ARG_INNER({
i++;
// Even if this is a directory, FILESYSTEM_mountAssets() expects '.vvvvvv' on the end
playassets = "levels/" + std::string(argv[i]) + ".vvvvvv";
})
}
#undef ARG_INNER
#undef ARG
else
{
printf("Error: invalid option: %s\n", argv[i]);
VVV_exit(1);
}
}
if(!FILESYSTEM_init(argv[0], baseDir, assetsPath))
2020-01-01 21:29:24 +01:00
{
puts("Unable to initialize filesystem!");
VVV_exit(1);
2020-01-01 21:29:24 +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-01-01 21:29:24 +01:00
NETWORK_init();
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
// This loads music too...
graphics.reloadresources();
2020-01-01 21:29:24 +01:00
game.gamestate = PRELOADER;
game.menustart = false;
// Initialize title screen to cyan
graphics.titlebg.colstate = 10;
map.nexttowercolour();
map.ypos = (700-29) * 8;
graphics.towerbg.bypos = map.ypos / 2;
graphics.titlebg.bypos = map.ypos / 2;
{
// Prioritize unlock.vvv first (2.2 and below),
// but settings have been migrated to settings.vvv (2.3 and up)
ScreenSettings screen_settings;
game.loadstats(&screen_settings);
game.loadsettings(&screen_settings);
gameScreen.init(screen_settings);
}
graphics.screenbuffer = &gameScreen;
graphics.create_buffers(gameScreen.GetFormat());
if (game.skipfakeload)
game.gamestate = TITLEMODE;
2020-01-01 21:29:24 +01:00
if (game.slowdown == 0) game.slowdown = 30;
//Check to see if you've already unlocked some achievements here from before the update
if (game.swnbestrank > 0){
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");
}
if(game.unlock[5]) game.unlockAchievement("vvvvvvgamecomplete");
if(game.unlock[19]) game.unlockAchievement("vvvvvvgamecompleteflip");
if(game.unlock[20]) game.unlockAchievement("vvvvvvmaster");
if (game.bestgamedeaths > -1) {
if (game.bestgamedeaths <= 500) {
game.unlockAchievement("vvvvvvcomplete500");
}
if (game.bestgamedeaths <= 250) {
game.unlockAchievement("vvvvvvcomplete250");
}
if (game.bestgamedeaths <= 100) {
game.unlockAchievement("vvvvvvcomplete100");
}
if (game.bestgamedeaths <= 50) {
game.unlockAchievement("vvvvvvcomplete50");
}
}
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();
#if !defined(NO_CUSTOM_LEVELS)
if (startinplaytest) {
game.levelpage = 0;
game.playcustomlevel = 0;
game.playassets = playassets;
game.menustart = true;
LevelMetaData meta;
if (ed.getLevelMetaData(playtestname, meta)) {
ed.ListOfMetaData.clear();
ed.ListOfMetaData.push_back(meta);
} else {
ed.loadZips();
if (ed.getLevelMetaData(playtestname, meta)) {
ed.ListOfMetaData.clear();
ed.ListOfMetaData.push_back(meta);
} else {
printf("Level not found\n");
VVV_exit(1);
}
}
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.playmusic = savemusic;
game.cliplaytest = true;
script.startgamemode(23);
} else {
script.startgamemode(22);
}
graphics.fademode = 0;
}
#endif
2020-01-01 21:29:24 +01:00
key.isActive = true;
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
gamestate_funcs = get_gamestate_funcs(game.gamestate, &num_gamestate_funcs);
loop_assign_active_funcs();
Call VVV_exit() when SDL_QUIT is received This fixes a regression introduced by #535 where a quit signal (e.g. Ctrl-C) sent to the window while the game was in unfocus pause wouldn't close the game. One problem was that key.quitProgram would only be checked when control flow switched back to the outer loop in main(), which would only happen when the loop order state machine switched to a delta function. As the unfocused func table didn't have any delta functions, this means key.quitProgram would never be checked. So a naïve solution to this would just be to add a no-op delta func entry to the unfocused func table. However, we then run into a separate issue where a delta function at the end of a func list never reassigns the active funcs, causing the game to be stuck in the unfocus pause forever. Active func reassignment only happens after fixed funcs. So then a naïve solution after that would be to simply add a no-op fixed func entry after that. And indeed, that would fix the whole issue. However, I want to do things the right way. And this does not seem like the right way. Even putting aside the separate last-func-being-delta issue, it mandates that every func list needs a delta function. Which seems quite unnecessary to me. Another solution I considered was copy-pasting the key.quitProgram check to the inner loops, or adding some sort of signal propagation to the inner loops - implemented by copy-pasting checks after each loop - so we didn't need to copy-paste key.quitProgram... but that seems really messy, too. So, I realized that we could throw away key.quitProgram, and simply call VVV_exit() directly when we receive an SDL_QUIT event. This fixes the issue, this removes an unnecessary middleman variable, and it's pretty cleanly and simply the right thing to do.
2021-04-02 00:24:14 +02:00
while (true)
2020-01-01 21:29:24 +01:00
{
f_time = SDL_GetTicks();
const Uint32 f_timetaken = f_time - f_timePrev;
if (!game.over30mode && f_timetaken < 34)
{
const volatile Uint32 f_delay = 34 - f_timetaken;
SDL_Delay(f_delay);
f_time = SDL_GetTicks();
}
f_timePrev = f_time;
timePrev = time_;
time_ = SDL_GetTicks();
deltaloop();
}
cleanup();
return 0;
}
static void cleanup(void)
{
/* Order matters! */
game.savestatsandsettings();
gameScreen.destroy();
graphics.grphx.destroy();
graphics.destroy_buffers();
graphics.destroy();
music.destroy();
NETWORK_shutdown();
SDL_Quit();
FILESYSTEM_deinit();
}
void VVV_exit(const int exit_code)
{
cleanup();
exit(exit_code);
}
static void inline deltaloop(void)
{
//timestep limit to 30
const float rawdeltatime = static_cast<float>(time_ - timePrev);
accumulator += rawdeltatime;
2020-01-01 21:29:24 +01:00
Uint32 timesteplimit = game.get_timestep();
while (accumulator >= timesteplimit)
{
enum IndexCode index_code = increment_func_index();
if (index_code == Index_end)
{
loop_assign_active_funcs();
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
accumulator = SDL_fmodf(accumulator, timesteplimit);
/* We are done rendering. */
graphics.renderfixedpost();
fixedloop();
}
const float alpha = game.over30mode ? static_cast<float>(accumulator) / timesteplimit : 1.0f;
graphics.alpha = alpha;
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
if (active_func_index == NULL
|| *active_func_index == -1
|| active_funcs == NULL)
{
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
/* Somehow the first deltatime has been too small and things haven't
* initialized. We'll just no-op for now.
*/
}
else
{
const struct ImplFunc* implfunc = &(*active_funcs)[*active_func_index];
if (implfunc->type == Func_delta && implfunc->func != NULL)
{
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
implfunc->func();
gameScreen.FlipScreen();
}
}
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
static enum LoopCode loop_begin(void)
{
if (game.inputdelay)
{
key.Poll();
}
// Update network per frame.
NETWORK_update();
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
return Loop_continue;
}
static void unfocused_run(void)
{
if (!game.blackout)
{
ClearSurface(graphics.backBuffer);
#define FLIP(YPOS) graphics.flipmode ? 232 - YPOS : YPOS
graphics.bprint(5, FLIP(110), "Game paused", 196 - help.glow, 255 - help.glow, 196 - help.glow, true);
graphics.bprint(5, FLIP(120), "[click to resume]", 196 - help.glow, 255 - help.glow, 196 - help.glow, true);
graphics.bprint(5, FLIP(220), "Press M to mute in game", 164 - help.glow, 196 - help.glow, 164 - help.glow, true);
graphics.bprint(5, FLIP(230), "Press N to mute music only", 164 - help.glow, 196 - help.glow, 164 - help.glow, true);
#undef FLIP
}
graphics.render();
gameScreen.FlipScreen();
//We are minimised, so lets put a bit of a delay to save CPU
SDL_Delay(100);
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
}
static void focused_begin(void)
{
Re-fix resumemusic/musicfadein once again So it looks like facb079b3597b380f876537523cd351a0e637b62 (PR #316) had a few issues. The SDL performance counter doesn't really work that well. Testing reveals that unfocusing and focusing the game again results in the resumemusic() script command resuming the track at the wrong time. Even when not unfocusing the game at all, stopping a track and resuming it resumes it at the wrong time. (Only disabling the unfocus pause fixes this.) Furthermore, there's also the fact that the SDL performance counter keeps incrementing when the game is paused under GDB. So... yeah. Instead of dealing with the SDL performance counter, I'm just going to pause and resume the music directly (so the stopmusic() script command just pauses the music instead). As a result, we no longer can keep constantly calling Mix_PauseMusic() or Mix_ResumeMusic() when focused or unfocused, so I've moved those calls to happen directly when the relevant SDL events are received (the constant calls were originally in VCE, and whoever added them (I'm pretty sure it was Leo) was not the sharpest tool in the shed...). And we are going to switch over to using our own fade system instead of the SDL mixer fade system. In fact, we were already using our own fade system for fadeins after collecting a trinket or a custom level crewmate, but we were still using the mixer system for the rest. This is an inconsistency that I am glad to correct, so we're also doing our own fadeouts now. There is, however, an issue with the fade system where the length it goes for is inaccurate, because it's based on a volume-per-frame second calculation that gets truncated. But that's an issue to fix later - at least what I'm doing right now makes resumemusic() and musicfadein() work better than before.
2021-04-02 21:56:25 +02:00
/* no-op. */
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
static void focused_end(void)
{
game.gameclock();
music.processmusic();
graphics.processfade();
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
static enum LoopCode loop_end(void)
{
//We did editorinput, now it's safe to turn this off
key.linealreadyemptykludge = false;
//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())
{
game.mutebutton = 8;
if (game.muted)
{
game.muted = false;
}
else
{
game.muted = true;
}
}
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())
{
game.musicmutebutton = 8;
game.musicmuted = !game.musicmuted;
}
if (game.musicmutebutton > 0)
{
game.musicmutebutton--;
}
if (game.muted)
{
Mix_VolumeMusic(0) ;
Mix_Volume(-1,0);
}
else
{
Mix_Volume(-1,MIX_MAX_VOLUME * music.user_sound_volume / USER_VOLUME_MAX);
if (game.musicmuted)
{
Mix_VolumeMusic(0);
}
else
{
Mix_VolumeMusic(music.musicVolume * music.user_music_volume / USER_VOLUME_MAX);
}
}
if (key.resetWindow)
{
key.resetWindow = false;
gameScreen.ResizeScreen(-1, -1);
}
Generalize game loop order and fix it to what it was in 2.2 Okay, so the reason why all render functions were moved to the end of the frame in #220 is because it's simpler to call two fixed functions and then a delta function instead of one fixed function, then a delta function, and then another fixed function. This is because fixed functions need special handling inside deltaloop(), and you can't simply duplicate this handling after calling a delta function. Oh, and to make matters worse, it's not always fixed-delta-fixed, sometimes (like in MAPMODE and TELEPORTERMODE) it's delta-fixed-fixed, so we'd need to handle that somehow too. The solution here is to generalize the game loop and factor out each function, instead of hardcoding it. Instead of having hardcoded case-switches directly in the loop, I made a function that returns an array of functions for a given gamestate, along with the number of functions, then the game loop processes it accordingly. In fixedloop(), it iterates over the array and executes each function until it reaches a delta function, at which point it stops. And when it reaches the end of the array, it goes back to the start of the array. But anyway, if it gets to a delta function, it'll stop the loop and finish fixedloop(). Then deltaloop() will call the delta function. And then on the next frame, the function index will be incremented again, so fixedloop() will call the fixed functions again. Actually, the previous game loop was actually made up of one big loop, with a gamestate function loop nested inside it, flanked with code that ran at the start and end of the "big loop". This would be easy to handle with one loop (just include the beginning and end functions with the gamestate functions in the array), except that the gamestate functions could suddenly be swapped out with unfocused functions (the ones that run when you unfocus the window) at any time (well, on frame boundaries, since key.isActive only got checked once, guarding the entire "inner loop" - and I made sure that changing key.isActive wouldn't immediately apply, just like the previous game loop order) - so I had to add yet another layer of indirection, where the gamestate functions could immediately be swapped out with the unfocused functions (while still running the beginning and end code, because that was how the previous loop order worked, after all). This also fixes a regression that the game loop that #220 introduced had, where if the fixed functions switched the gamestate, the game would prematurely start rendering the gamestate function of the new gamestate in the deltaframes, which was a source of some deltaframe glitches. But fixing this is likely to just as well cause deltaframe glitches, so it'd be better to fix this along with fixing the loop order, and only have one round of QA to do in the end, instead of doing one round after each change separately. Fixes #464... but this isn't the end of the patchset. There are bugs that need to be fixed, and kludges that need to be reverted.
2020-11-07 01:08:22 +01:00
return Loop_continue;
2020-01-01 21:29:24 +01:00
}