2012-07-16 19:26:41 +02:00
|
|
|
/*
|
2019-08-03 19:43:48 +02:00
|
|
|
* This software is licensed under the terms of the MIT License.
|
2017-02-11 04:52:08 +01:00
|
|
|
* See COPYING for further information.
|
2012-07-16 19:26:41 +02:00
|
|
|
* ---
|
2024-05-16 23:30:41 +02:00
|
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
2012-07-16 19:26:41 +02:00
|
|
|
*/
|
|
|
|
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "replayview.h"
|
|
|
|
|
|
|
|
#include "common.h"
|
2012-07-16 19:26:41 +02:00
|
|
|
#include "menu.h"
|
|
|
|
#include "options.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
|
|
|
|
#include "audio/audio.h"
|
2012-07-29 22:39:52 +02:00
|
|
|
#include "plrmodes.h"
|
2021-05-30 03:26:21 +02:00
|
|
|
#include "replay/struct.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "resource/font.h"
|
|
|
|
#include "stageinfo.h"
|
|
|
|
#include "video.h"
|
2012-07-16 19:26:41 +02:00
|
|
|
|
2017-02-09 07:01:38 +01:00
|
|
|
// Type of MenuData.context
|
|
|
|
typedef struct ReplayviewContext {
|
|
|
|
MenuData *submenu;
|
2018-05-08 10:21:01 +02:00
|
|
|
MenuData *next_submenu;
|
2017-02-24 22:58:27 +01:00
|
|
|
double sub_fade;
|
2017-02-09 07:01:38 +01:00
|
|
|
} ReplayviewContext;
|
|
|
|
|
|
|
|
// Type of MenuEntry.arg (which should be renamed to context, probably...)
|
|
|
|
typedef struct ReplayviewItemContext {
|
|
|
|
Replay *replay;
|
|
|
|
char *replayname;
|
|
|
|
} ReplayviewItemContext;
|
2012-07-16 19:26:41 +02:00
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
static MenuData *replayview_sub_messagebox(MenuData *parent, const char *message);
|
2018-05-08 10:21:01 +02:00
|
|
|
|
|
|
|
static void replayview_set_submenu(MenuData *parent, MenuData *submenu) {
|
|
|
|
ReplayviewContext *ctx = parent->context;
|
|
|
|
|
|
|
|
if(ctx->submenu) {
|
|
|
|
ctx->submenu->state = MS_Dead;
|
|
|
|
ctx->next_submenu = submenu;
|
|
|
|
} else {
|
|
|
|
ctx->submenu = submenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(submenu != NULL) {
|
|
|
|
// submenu->context = ctx;
|
|
|
|
assert(submenu->context == ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
Replay *rpy;
|
|
|
|
int stgnum;
|
|
|
|
} startrpy_arg_t;
|
|
|
|
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
static void on_replay_finished(CallChainResult ccr) {
|
2023-06-15 03:28:39 +02:00
|
|
|
replay_destroy_events(ccr.ctx);
|
2020-06-22 16:41:03 +02:00
|
|
|
audio_bgm_play(res_bgm("menu"), true, 0, 0);
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
}
|
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
static void really_start_replay(CallChainResult ccr) {
|
|
|
|
startrpy_arg_t *argp = ccr.ctx;
|
|
|
|
auto arg = *argp;
|
|
|
|
mem_free(argp);
|
2023-04-07 07:49:15 +02:00
|
|
|
|
|
|
|
if(!TRANSITION_RESULT_CANCELED(ccr)) {
|
2023-06-15 03:28:39 +02:00
|
|
|
replay_play(arg.rpy, arg.stgnum, false, CALLCHAIN(on_replay_finished, arg.rpy));
|
2023-04-07 07:49:15 +02:00
|
|
|
}
|
2018-05-08 10:21:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void start_replay(MenuData *menu, void *arg) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewItemContext *ictx = arg;
|
2017-02-16 17:55:46 +01:00
|
|
|
ReplayviewContext *mctx = menu->context;
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2018-01-09 02:33:05 +01:00
|
|
|
int stagenum = 0;
|
|
|
|
|
|
|
|
if(mctx->submenu) {
|
|
|
|
stagenum = mctx->submenu->cursor;
|
|
|
|
}
|
|
|
|
|
2017-02-09 07:01:38 +01:00
|
|
|
Replay *rpy = ictx->replay;
|
|
|
|
|
|
|
|
if(!replay_load(rpy, ictx->replayname, REPLAY_READ_EVENTS)) {
|
2018-05-08 10:21:01 +02:00
|
|
|
replayview_set_submenu(menu, replayview_sub_messagebox(menu, "Failed to load replay events"));
|
2017-02-09 07:01:38 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-09 12:56:52 +01:00
|
|
|
ReplayStage *stg = dynarray_get_ptr(&rpy->stages, stagenum);
|
2018-05-08 10:21:01 +02:00
|
|
|
char buf[64];
|
|
|
|
|
2020-05-16 22:41:54 +02:00
|
|
|
if(!stageinfo_get_by_id(stg->stage)) {
|
2018-05-08 10:21:01 +02:00
|
|
|
replay_destroy_events(rpy);
|
|
|
|
snprintf(buf, sizeof(buf), "Can't replay this stage: unknown stage ID %X", stg->stage);
|
|
|
|
replayview_set_submenu(menu, replayview_sub_messagebox(menu, buf));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!plrmode_find(stg->plr_char, stg->plr_shot)) {
|
|
|
|
replay_destroy_events(rpy);
|
|
|
|
snprintf(buf, sizeof(buf), "Can't replay this stage: unknown player character/mode %X/%X", stg->plr_char, stg->plr_shot);
|
|
|
|
replayview_set_submenu(menu, replayview_sub_messagebox(menu, buf));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
set_transition(TransFadeBlack, FADE_TIME, FADE_TIME,
|
|
|
|
CALLCHAIN(really_start_replay, ALLOC(startrpy_arg_t, {
|
2018-05-08 10:21:01 +02:00
|
|
|
.rpy = ictx->replay,
|
|
|
|
.stgnum = stagenum
|
2023-04-07 05:47:47 +02:00
|
|
|
}))
|
2018-05-08 10:21:01 +02:00
|
|
|
);
|
2012-08-07 05:28:41 +02:00
|
|
|
}
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
static void replayview_draw_stagemenu(MenuData*);
|
2018-05-08 10:21:01 +02:00
|
|
|
static void replayview_draw_messagebox(MenuData*);
|
2017-02-24 22:58:27 +01:00
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
static MenuData *replayview_sub_stageselect(MenuData *parent, ReplayviewItemContext *ictx) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
MenuData *m = alloc_menu();
|
2017-02-09 07:01:38 +01:00
|
|
|
Replay *rpy = ictx->replay;
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
m->draw = replayview_draw_stagemenu;
|
2012-08-12 17:28:43 +02:00
|
|
|
m->flags = MF_Transient | MF_Abortable;
|
2017-02-24 22:58:27 +01:00
|
|
|
m->transition = NULL;
|
2018-05-08 10:21:01 +02:00
|
|
|
m->context = parent->context;
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2022-01-09 12:56:52 +01:00
|
|
|
dynarray_foreach_elem(&rpy->stages, ReplayStage *rstg, {
|
2022-01-18 03:22:19 +01:00
|
|
|
uint16_t stage_id = rstg->stage;
|
|
|
|
StageInfo *stg = stageinfo_get_by_id(stage_id);
|
|
|
|
|
|
|
|
if(LIKELY(stg != NULL)) {
|
|
|
|
add_menu_entry(m, stg->title, start_replay, ictx);
|
|
|
|
} else {
|
|
|
|
char label[64];
|
|
|
|
snprintf(label, sizeof(label), "Unknown stage %X", stage_id);
|
|
|
|
add_menu_entry(m, label, menu_action_close, NULL);
|
|
|
|
}
|
2022-01-09 12:56:52 +01:00
|
|
|
});
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2012-08-07 05:28:41 +02:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
static MenuData *replayview_sub_messagebox(MenuData *parent, const char *message) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
MenuData *m = alloc_menu();
|
2018-05-08 10:21:01 +02:00
|
|
|
m->draw = replayview_draw_messagebox;
|
|
|
|
m->flags = MF_Transient | MF_Abortable;
|
|
|
|
m->transition = NULL;
|
|
|
|
m->context = parent->context;
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
add_menu_entry(m, message, menu_action_close, NULL);
|
2018-05-08 10:21:01 +02:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replayview_run(MenuData *menu, void *arg) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewItemContext *ctx = arg;
|
|
|
|
Replay *rpy = ctx->replay;
|
|
|
|
|
2022-01-09 12:56:52 +01:00
|
|
|
if(rpy->stages.num_elements > 1) {
|
2018-05-08 10:21:01 +02:00
|
|
|
replayview_set_submenu(menu, replayview_sub_stageselect(menu, ctx));
|
2017-02-09 07:01:38 +01:00
|
|
|
} else {
|
2017-02-16 17:55:46 +01:00
|
|
|
start_replay(menu, ctx);
|
2017-02-09 07:01:38 +01:00
|
|
|
}
|
2012-07-16 19:26:41 +02:00
|
|
|
}
|
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
static void replayview_freearg(void *a) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewItemContext *ctx = a;
|
2021-05-30 03:26:21 +02:00
|
|
|
replay_reset(ctx->replay);
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(ctx->replay);
|
|
|
|
mem_free(ctx->replayname);
|
|
|
|
mem_free(ctx);
|
2012-07-29 22:39:52 +02:00
|
|
|
}
|
|
|
|
|
2018-05-08 10:21:01 +02:00
|
|
|
static void replayview_draw_submenu_bg(float width, float height, float alpha) {
|
2019-10-12 14:02:15 +02:00
|
|
|
r_state_push();
|
|
|
|
r_mat_mv_push();
|
|
|
|
r_mat_mv_translate(SCREEN_W*0.5, SCREEN_H*0.5, 0);
|
|
|
|
r_mat_mv_scale(width, height, 1);
|
2018-07-23 19:07:59 +02:00
|
|
|
alpha *= 0.7;
|
|
|
|
r_color4(0.1 * alpha, 0.1 * alpha, 0.1 * alpha, alpha);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_shader_standard_notex();
|
|
|
|
r_draw_quad();
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
|
|
|
r_state_pop();
|
2018-05-08 10:21:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void replayview_draw_messagebox(MenuData* m) {
|
|
|
|
// this context is shared with the parent menu
|
|
|
|
ReplayviewContext *ctx = m->context;
|
2020-04-05 04:51:00 +02:00
|
|
|
MenuEntry *e = dynarray_get_ptr(&m->entries, 0);
|
2018-05-08 10:21:01 +02:00
|
|
|
float alpha = 1 - ctx->sub_fade;
|
2020-06-09 03:33:22 +02:00
|
|
|
float height = font_get_lineskip(res_font("standard")) * 2;
|
|
|
|
float width = text_width(res_font("standard"), e->name, 0) + 64;
|
2018-05-08 10:21:01 +02:00
|
|
|
replayview_draw_submenu_bg(width, height, alpha);
|
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
text_draw(e->name, &(TextParams) {
|
2018-07-23 19:07:59 +02:00
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.color = RGBA_MUL_ALPHA(0.9, 0.6, 0.2, alpha),
|
2019-10-12 14:02:15 +02:00
|
|
|
.pos = { SCREEN_W*0.5, SCREEN_H*0.5 },
|
|
|
|
.shader = "text_default",
|
2018-07-23 19:07:59 +02:00
|
|
|
});
|
2018-05-08 10:21:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void replayview_draw_stagemenu(MenuData *m) {
|
|
|
|
// this context is shared with the parent menu
|
|
|
|
ReplayviewContext *ctx = m->context;
|
|
|
|
float alpha = 1 - ctx->sub_fade;
|
2020-04-05 04:51:00 +02:00
|
|
|
float height = (1 + m->entries.num_elements) * 20;
|
2022-01-18 03:22:19 +01:00
|
|
|
float width = 180;
|
2018-05-08 10:21:01 +02:00
|
|
|
|
|
|
|
replayview_draw_submenu_bg(width, height, alpha);
|
2018-04-12 16:08:48 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_push();
|
2020-04-05 04:51:00 +02:00
|
|
|
r_mat_mv_translate(SCREEN_W*0.5, (SCREEN_H - (m->entries.num_elements - 1) * 20) * 0.5, 0);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_foreach(&m->entries, int i, MenuEntry *e, {
|
2018-01-18 17:40:42 +01:00
|
|
|
float a = e->drawdata;
|
2018-07-23 19:07:59 +02:00
|
|
|
Color clr;
|
|
|
|
|
2017-02-09 07:01:38 +01:00
|
|
|
if(e->action == NULL) {
|
2018-07-23 19:07:59 +02:00
|
|
|
clr = *RGBA_MUL_ALPHA(0.5, 0.5, 0.5, 0.5 * alpha);
|
2017-02-09 07:01:38 +01:00
|
|
|
} else {
|
2012-08-07 05:28:41 +02:00
|
|
|
float ia = 1-a;
|
2018-07-23 19:07:59 +02:00
|
|
|
clr = *RGBA_MUL_ALPHA(0.9 + ia * 0.1, 0.6 + ia * 0.4, 0.2 + ia * 0.8, (0.7 + 0.3 * a) * alpha);
|
2012-08-07 05:28:41 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
text_draw(e->name, &(TextParams) {
|
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.pos = { 0, 20*i },
|
|
|
|
.color = &clr,
|
2019-10-12 14:02:15 +02:00
|
|
|
.shader = "text_default",
|
2018-07-23 19:07:59 +02:00
|
|
|
});
|
2020-04-05 04:51:00 +02:00
|
|
|
});
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
2012-08-07 05:28:41 +02:00
|
|
|
}
|
|
|
|
|
2022-02-16 04:09:30 +01:00
|
|
|
static void replayview_drawitem(MenuEntry *e, int item, int cnt, void *ctx) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewItemContext *ictx = e->arg;
|
|
|
|
|
|
|
|
if(!ictx) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Replay *rpy = ictx->replay;
|
|
|
|
|
2019-01-04 23:59:39 +01:00
|
|
|
float sizes[] = { 1.2, 2.2, 0.5, 0.55, 0.55 };
|
|
|
|
int columns = sizeof(sizes)/sizeof(float), i, j;
|
|
|
|
float base_size = (SCREEN_W - 110.0) / columns;
|
|
|
|
|
2022-01-09 12:56:52 +01:00
|
|
|
ReplayStage *first_stage = dynarray_get_ptr(&rpy->stages, 0);
|
|
|
|
time_t t = first_stage->start_time;
|
|
|
|
struct tm *timeinfo = localtime(&t);
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
for(i = 0; i < columns; ++i) {
|
|
|
|
char tmp[128];
|
2019-01-04 23:59:39 +01:00
|
|
|
float csize = sizes[i] * base_size;
|
2017-04-07 01:54:59 +02:00
|
|
|
float o = 0;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
for(j = 0; j < i; ++j)
|
2019-01-04 23:59:39 +01:00
|
|
|
o += sizes[j] * base_size;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-06-29 23:36:51 +02:00
|
|
|
Alignment a = ALIGN_CENTER;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
switch(i) {
|
|
|
|
case 0:
|
2018-06-29 23:36:51 +02:00
|
|
|
a = ALIGN_LEFT;
|
2019-01-04 23:59:39 +01:00
|
|
|
strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M", timeinfo);
|
2012-07-29 22:39:52 +02:00
|
|
|
break;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
case 1:
|
2019-01-04 23:59:39 +01:00
|
|
|
a = ALIGN_LEFT;
|
2017-02-16 07:29:18 +01:00
|
|
|
strlcpy(tmp, rpy->playername, sizeof(tmp));
|
2012-07-29 22:39:52 +02:00
|
|
|
break;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-05-08 09:06:33 +02:00
|
|
|
case 2: {
|
2019-01-04 23:59:39 +01:00
|
|
|
a = ALIGN_RIGHT;
|
2022-01-09 12:56:52 +01:00
|
|
|
PlayerMode *plrmode = plrmode_find(first_stage->plr_char, first_stage->plr_shot);
|
2018-05-08 09:06:33 +02:00
|
|
|
|
|
|
|
if(plrmode == NULL) {
|
|
|
|
strlcpy(tmp, "?????", sizeof(tmp));
|
|
|
|
} else {
|
2019-04-16 07:30:45 +02:00
|
|
|
plrmode_repr(tmp, sizeof(tmp), plrmode, false);
|
2018-05-08 09:06:33 +02:00
|
|
|
}
|
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
break;
|
2018-05-08 09:06:33 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
case 3:
|
2019-01-04 23:59:39 +01:00
|
|
|
a = ALIGN_CENTER;
|
2022-01-09 12:56:52 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s", difficulty_name(first_stage->diff));
|
2012-07-29 22:39:52 +02:00
|
|
|
break;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
case 4:
|
2019-01-04 23:59:39 +01:00
|
|
|
a = ALIGN_LEFT;
|
2022-01-09 12:56:52 +01:00
|
|
|
if(rpy->stages.num_elements == 1) {
|
|
|
|
StageInfo *stg = stageinfo_get_by_id(first_stage->stage);
|
2017-02-10 11:39:42 +01:00
|
|
|
|
|
|
|
if(stg) {
|
|
|
|
snprintf(tmp, sizeof(tmp), "%s", stg->title);
|
|
|
|
} else {
|
|
|
|
snprintf(tmp, sizeof(tmp), "?????");
|
|
|
|
}
|
|
|
|
} else {
|
2022-01-09 12:56:52 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%i stages", rpy->stages.num_elements);
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
2012-07-29 22:39:52 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-29 22:39:52 +02:00
|
|
|
switch(a) {
|
2020-06-09 03:33:22 +02:00
|
|
|
case ALIGN_CENTER: o += csize * 0.5 - text_width(res_font("standard"), tmp, 0) * 0.5; break;
|
|
|
|
case ALIGN_RIGHT: o += csize - text_width(res_font("standard"), tmp, 0); break;
|
2018-06-29 23:36:51 +02:00
|
|
|
default: break;
|
2012-07-29 22:39:52 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
text_draw(tmp, &(TextParams) {
|
|
|
|
.pos = { o + 10, 20 * item },
|
|
|
|
.shader = "text_default",
|
2019-01-04 23:59:39 +01:00
|
|
|
.max_width = csize,
|
2018-07-23 19:07:59 +02:00
|
|
|
});
|
2012-07-29 22:39:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
static void replayview_logic(MenuData *m) {
|
|
|
|
ReplayviewContext *ctx = m->context;
|
|
|
|
|
|
|
|
if(ctx->submenu) {
|
|
|
|
MenuData *sm = ctx->submenu;
|
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_foreach(&sm->entries, int i, MenuEntry *e, {
|
2018-01-21 10:59:27 +01:00
|
|
|
e->drawdata += 0.2 * ((i == sm->cursor) - e->drawdata);
|
2020-04-05 04:51:00 +02:00
|
|
|
});
|
2018-01-18 17:40:42 +01:00
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
if(sm->state == MS_Dead) {
|
|
|
|
if(ctx->sub_fade == 1.0) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
free_menu(sm);
|
2018-05-08 10:21:01 +02:00
|
|
|
ctx->submenu = ctx->next_submenu;
|
|
|
|
ctx->next_submenu = NULL;
|
2017-02-24 22:58:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->sub_fade = approach(ctx->sub_fade, 1.0, 1.0/FADE_TIME);
|
|
|
|
} else {
|
|
|
|
ctx->sub_fade = approach(ctx->sub_fade, 0.0, 1.0/FADE_TIME);
|
|
|
|
}
|
|
|
|
}
|
2017-12-26 12:07:40 +01:00
|
|
|
|
2019-01-04 23:59:39 +01:00
|
|
|
m->drawdata[0] = SCREEN_W/2 - 50;
|
|
|
|
m->drawdata[1] = (SCREEN_W - 100)*1.75;
|
2017-12-26 12:07:40 +01:00
|
|
|
m->drawdata[2] += (20*m->cursor - m->drawdata[2])/10.0;
|
2018-01-18 17:40:42 +01:00
|
|
|
|
|
|
|
animate_menu_list_entries(m);
|
2017-02-24 22:58:27 +01:00
|
|
|
}
|
|
|
|
|
2012-08-12 16:54:48 +02:00
|
|
|
static void replayview_draw(MenuData *m) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewContext *ctx = m->context;
|
|
|
|
|
2012-08-12 16:54:48 +02:00
|
|
|
draw_options_menu_bg(m);
|
|
|
|
draw_menu_title(m, "Replays");
|
|
|
|
|
2022-02-16 04:09:30 +01:00
|
|
|
draw_menu_list(m, 50, 100, replayview_drawitem, SCREEN_H, NULL);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-02-09 07:01:38 +01:00
|
|
|
if(ctx->submenu) {
|
2017-02-24 22:58:27 +01:00
|
|
|
ctx->submenu->draw(ctx->submenu);
|
2012-08-12 16:54:48 +02:00
|
|
|
}
|
2018-07-23 19:07:59 +02:00
|
|
|
|
2018-06-29 23:36:51 +02:00
|
|
|
r_shader_standard();
|
2012-08-12 16:54:48 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static int replayview_cmp(const void *a, const void *b) {
|
2017-02-09 07:01:38 +01:00
|
|
|
ReplayviewItemContext *actx = ((MenuEntry*)a)->arg;
|
|
|
|
ReplayviewItemContext *bctx = ((MenuEntry*)b)->arg;
|
|
|
|
|
|
|
|
Replay *arpy = actx->replay;
|
|
|
|
Replay *brpy = bctx->replay;
|
|
|
|
|
2022-01-09 12:56:52 +01:00
|
|
|
return dynarray_get(&brpy->stages, 0).start_time - dynarray_get(&arpy->stages, 0).start_time;
|
2012-07-16 19:26:41 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static int fill_replayview_menu(MenuData *m) {
|
2017-04-18 21:48:18 +02:00
|
|
|
VFSDir *dir = vfs_dir_open("storage/replays");
|
|
|
|
const char *filename;
|
2012-07-16 19:59:55 +02:00
|
|
|
int rpys = 0;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-16 19:26:41 +02:00
|
|
|
if(!dir) {
|
2017-04-18 21:48:18 +02:00
|
|
|
log_warn("VFS error: %s", vfs_get_error());
|
2012-07-16 19:59:55 +02:00
|
|
|
return -1;
|
2012-07-16 19:26:41 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2012-07-16 19:26:41 +02:00
|
|
|
char ext[5];
|
|
|
|
snprintf(ext, 5, ".%s", REPLAY_EXTENSION);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-04-18 21:48:18 +02:00
|
|
|
while((filename = vfs_dir_read(dir))) {
|
|
|
|
if(!strendswith(filename, ext))
|
2012-07-16 19:26:41 +02:00
|
|
|
continue;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2023-01-09 04:19:31 +01:00
|
|
|
auto rpy = ALLOC(Replay);
|
2017-04-18 21:48:18 +02:00
|
|
|
if(!replay_load(rpy, filename, REPLAY_READ_META)) {
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(rpy);
|
2012-08-05 21:49:48 +02:00
|
|
|
continue;
|
2012-08-05 08:51:42 +02:00
|
|
|
}
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2023-01-09 04:19:31 +01:00
|
|
|
auto ictx = ALLOC(ReplayviewItemContext, {
|
|
|
|
.replay = rpy,
|
2024-09-05 06:38:58 +02:00
|
|
|
.replayname = mem_strdup(filename),
|
2023-01-09 04:19:31 +01:00
|
|
|
});
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2018-05-08 10:21:01 +02:00
|
|
|
add_menu_entry(m, " ", replayview_run, ictx)->transition = /*rpy->numstages < 2 ? TransFadeBlack :*/ NULL;
|
2012-07-16 19:59:55 +02:00
|
|
|
++rpys;
|
2012-07-16 19:26:41 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-04-18 21:48:18 +02:00
|
|
|
vfs_dir_close(dir);
|
2017-03-28 01:25:45 +02:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_qsort(&m->entries, replayview_cmp);
|
2017-03-28 01:25:45 +02:00
|
|
|
|
2012-07-16 19:59:55 +02:00
|
|
|
return rpys;
|
2012-07-16 19:26:41 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static void replayview_menu_input(MenuData *m) {
|
2017-02-24 22:58:27 +01:00
|
|
|
ReplayviewContext *ctx = (ReplayviewContext*)m->context;
|
|
|
|
MenuData *sub = ctx->submenu;
|
|
|
|
|
|
|
|
if(transition.state == TRANS_FADE_IN) {
|
|
|
|
menu_no_input(m);
|
|
|
|
} else {
|
|
|
|
if(sub && sub->state != MS_Dead) {
|
|
|
|
sub->input(sub);
|
|
|
|
} else {
|
|
|
|
menu_input(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static void replayview_free(MenuData *m) {
|
2017-02-24 22:58:27 +01:00
|
|
|
if(m->context) {
|
2018-05-08 10:21:01 +02:00
|
|
|
ReplayviewContext *ctx = m->context;
|
|
|
|
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
free_menu(ctx->next_submenu);
|
|
|
|
free_menu(ctx->submenu);
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(m->context);
|
2017-02-24 22:58:27 +01:00
|
|
|
m->context = NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_foreach_elem(&m->entries, MenuEntry *e, {
|
|
|
|
if(e->action == replayview_run) {
|
|
|
|
replayview_freearg(e->arg);
|
2017-02-24 22:58:27 +01:00
|
|
|
}
|
2020-04-05 04:51:00 +02:00
|
|
|
});
|
2017-02-24 22:58:27 +01:00
|
|
|
}
|
|
|
|
|
2023-04-07 05:47:47 +02:00
|
|
|
MenuData *create_replayview_menu(void) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
MenuData *m = alloc_menu();
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
m->logic = replayview_logic;
|
|
|
|
m->input = replayview_menu_input;
|
|
|
|
m->draw = replayview_draw;
|
|
|
|
m->end = replayview_free;
|
2019-04-20 18:06:27 +02:00
|
|
|
m->transition = TransFadeBlack;
|
2023-01-09 04:19:31 +01:00
|
|
|
m->context = ALLOC(ReplayviewContext, {
|
|
|
|
.sub_fade = 1.0,
|
|
|
|
});
|
2012-08-12 16:54:48 +02:00
|
|
|
m->flags = MF_Abortable;
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2012-07-16 19:59:55 +02:00
|
|
|
int r = fill_replayview_menu(m);
|
2017-02-09 07:01:38 +01:00
|
|
|
|
2012-07-16 19:59:55 +02:00
|
|
|
if(!r) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
|
|
|
add_menu_entry(m, "No replays available. Play the game and record some!", menu_action_close, NULL);
|
2012-07-16 19:59:55 +02:00
|
|
|
} else if(r < 0) {
|
Emscripten compatibility (#161)
* Major refactoring of the main loop(s) and control flow (WIP)
run_at_fps() is gone 🦀
Instead of nested blocking event loops, there is now an eventloop API
that manages an explicit stack of scenes. This makes Taisei a lot more
portable to async environments where spinning a loop forever without
yielding control simply is not an option, and that is the entire point
of this change.
A prime example of such an environment is the Web (via emscripten).
Taisei was able to run there through a terrible hack: inserting
emscripten_sleep calls into the loop, which would yield to the browser.
This has several major drawbacks: first of all, every function that
could possibly call emscripten_sleep must be compiled into a special
kind of bytecode, which then has to be interpreted at runtime, *much*
slower than JITed WebAssembly. And that includes *everything* down the
call stack, too! For more information, see
https://emscripten.org/docs/porting/emterpreter.html
Even though that method worked well enough for experimenting, despite
suboptimal performance, there is another obvious drawback:
emscripten_sleep is implemented via setTimeout(), which can be very
imprecise and is generally not reliable for fluid animation. Browsers
actually have an API specifically for that use case:
window.requestAnimationFrame(), but Taisei's original blocking control
flow style is simply not compatible with it. Emscripten exposes this API
with its emscripten_set_main_loop(), which the eventloop backend now
uses on that platform.
Unfortunately, C is still C, with no fancy closures or coroutines.
With blocking calls into menu/scene loops gone, the control flow is
reimplemented via so-called (pun intended) "call chains". That is
basically an euphemism for callback hell. With manual memory management
and zero type-safety. Not that the menu system wasn't shitty enough
already. I'll just keep telling myself that this is all temporary and
will be replaced with scripts in v1.4.
* improve build system for emscripten + various fixes
* squish menu bugs
* improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS
Note that stock freetype does not work without
EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the
"emscripten" branch here:
https://github.com/taisei-project/freetype2/tree/emscripten
* Enable -Wcast-function-type
Calling functions through incompatible pointers is nasal demons and
doesn't work in WASM.
* webgl: workaround a crash on some browsers
* emscripten improvements:
* Persist state (config, progress, replays, ...) in local IndexDB
* Simpler HTML shell (temporary)
* Enable more optimizations
* fix build if validate_glsl=false
* emscripten: improve asset packaging, with local cache
Note that even though there are rules to build audio bundles, audio
does *not* work yet. It looks like SDL2_mixer can not work without
threads, which is a problem. Yet another reason to write an OpenAL
backend - emscripten supports that natively.
* emscripten: customize the html shell
* emscripten: force "show log" checkbox unchecked initially
* emscripten: remove quit shortcut from main menu (since there's no quit)
* emscripten: log area fixes
* emscripten/webgl: workaround for fullscreen viewport issue
* emscripten: implement frameskip
* emscripter: improve framerate limiter
* align List to at least 8 bytes (shut up warnings)
* fix non-emscripten builds
* improve fullscreen handling, mainly for emscripten
* Workaround to make audio work in chromium
emscripten-core/emscripten#6511
* emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00 |