2011-06-24 19:16:05 +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.
|
2011-06-24 19:16:05 +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>.
|
2011-06-24 19:16:05 +02:00
|
|
|
*/
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
#include "charselect.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
|
|
|
|
#include "audio/audio.h"
|
2017-02-19 03:16:05 +01:00
|
|
|
#include "common.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "events.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "menu/mainmenu.h"
|
|
|
|
#include "plrmodes.h"
|
|
|
|
#include "portrait.h"
|
|
|
|
#include "progress.h"
|
2019-07-08 02:47:50 +02:00
|
|
|
#include "util/glm.h"
|
2023-07-30 06:13:09 +02:00
|
|
|
#include "util/graphics.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "video.h"
|
2011-06-24 19:16:05 +02:00
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
#define SELECTED_SUBSHOT(m) (((CharMenuContext*)(m)->context)->subshot)
|
2019-04-22 02:07:14 +02:00
|
|
|
#define DESCRIPTION_WIDTH (SCREEN_W / 3 + 40)
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
enum {
|
|
|
|
F_HAPPY,
|
|
|
|
F_NORMAL,
|
2023-06-17 04:19:11 +02:00
|
|
|
F_PUZZLED,
|
2019-08-22 21:43:34 +02:00
|
|
|
F_SMUG,
|
|
|
|
F_SURPRISED,
|
|
|
|
F_UNAMUSED,
|
|
|
|
NUM_FACES,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FACENAME_LEN 32
|
|
|
|
static const char facedefs[NUM_CHARACTERS][NUM_FACES][FACENAME_LEN] = {
|
|
|
|
[PLR_CHAR_REIMU] = {
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, happy),
|
|
|
|
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, normal),
|
2023-06-17 04:19:11 +02:00
|
|
|
[F_PUZZLED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, puzzled),
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, smug),
|
|
|
|
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, surprised),
|
|
|
|
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(reimu, unamused),
|
2019-08-22 21:43:34 +02:00
|
|
|
},
|
|
|
|
[PLR_CHAR_MARISA] = {
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, happy),
|
|
|
|
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, normal),
|
2023-06-17 04:19:11 +02:00
|
|
|
[F_PUZZLED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, puzzled),
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, smug),
|
|
|
|
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, surprised),
|
|
|
|
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(marisa, unamused),
|
2019-08-22 21:43:34 +02:00
|
|
|
},
|
|
|
|
[PLR_CHAR_YOUMU] = {
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_HAPPY] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, happy),
|
|
|
|
[F_NORMAL] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, normal),
|
2023-06-17 04:19:11 +02:00
|
|
|
[F_PUZZLED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, puzzled),
|
2020-04-27 21:58:25 +02:00
|
|
|
[F_SMUG] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, smug),
|
|
|
|
[F_SURPRISED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, surprised),
|
|
|
|
[F_UNAMUSED] = PORTRAIT_STATIC_FACE_SPRITE_NAME(youmu, unamused),
|
2019-08-22 21:43:34 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
typedef struct CharMenuContext {
|
2019-10-15 08:58:28 +02:00
|
|
|
int8_t subshot;
|
|
|
|
int8_t char_draw_order[NUM_CHARACTERS];
|
|
|
|
int8_t prev_selected_char;
|
2019-07-08 02:47:50 +02:00
|
|
|
} CharMenuContext;
|
|
|
|
|
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 set_player_mode(MenuData *m, void *p) {
|
|
|
|
progress.game_settings.character = (CharacterID)(uintptr_t)p;
|
|
|
|
progress.game_settings.shotmode = SELECTED_SUBSHOT(m);
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static void char_menu_input(MenuData*);
|
2017-02-24 22:58:27 +01:00
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
static void update_char_draw_order(MenuData *menu) {
|
|
|
|
CharMenuContext *ctx = menu->context;
|
|
|
|
|
|
|
|
for(int i = 0; i < NUM_CHARACTERS; ++i) {
|
|
|
|
if(ctx->char_draw_order[i] == menu->cursor) {
|
|
|
|
while(i) {
|
|
|
|
ctx->char_draw_order[i] = ctx->char_draw_order[i - 1];
|
|
|
|
ctx->char_draw_order[i - 1] = menu->cursor;
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static void update_char_menu(MenuData *menu) {
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_foreach(&menu->entries, int i, MenuEntry *e, {
|
|
|
|
e->drawdata += 0.05 * ((menu->cursor != i) - e->drawdata);
|
|
|
|
});
|
|
|
|
|
|
|
|
MenuEntry *cursor_entry = dynarray_get_ptr(&menu->entries, menu->cursor);
|
2019-04-21 10:37:50 +02:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
PlayerCharacter *pchar = plrchar_get((CharacterID)(uintptr_t)cursor_entry->arg);
|
2019-04-22 02:07:14 +02:00
|
|
|
assume(pchar != NULL);
|
|
|
|
|
|
|
|
PlayerMode *m = plrmode_find(pchar->id, SELECTED_SUBSHOT(menu));
|
|
|
|
assume(m != NULL);
|
|
|
|
|
2020-06-09 03:33:22 +02:00
|
|
|
Font *font = res_font("standard");
|
2019-04-22 02:07:14 +02:00
|
|
|
char buf[256] = { 0 };
|
|
|
|
text_wrap(font, m->description, DESCRIPTION_WIDTH, buf, sizeof(buf));
|
|
|
|
double height = text_height(font, buf, 0) + font_get_lineskip(font) * 2;
|
|
|
|
|
|
|
|
fapproach_asymptotic_p(&menu->drawdata[0], SELECTED_SUBSHOT(menu) - PLR_SHOT_A, 0.1, 1e-5);
|
2020-04-05 04:51:00 +02:00
|
|
|
fapproach_asymptotic_p(&menu->drawdata[1], 1 - cursor_entry->drawdata, 0.1, 1e-5);
|
2019-04-22 02:07:14 +02:00
|
|
|
fapproach_asymptotic_p(&menu->drawdata[2], height, 0.1, 1e-5);
|
2017-12-26 12:07:40 +01:00
|
|
|
}
|
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
static void end_char_menu(MenuData *m) {
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(m->context);
|
2019-07-08 02:47:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void transition_to_game(double fade) {
|
2023-09-23 21:56:34 +02:00
|
|
|
fade_out(pow(max(0, (fade - 0.5) * 2), 2));
|
2019-07-08 02:47:50 +02:00
|
|
|
}
|
|
|
|
|
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* create_char_menu(void) {
|
|
|
|
MenuData *m = alloc_menu();
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
m->input = char_menu_input;
|
|
|
|
m->draw = draw_char_menu;
|
2017-12-26 12:07:40 +01:00
|
|
|
m->logic = update_char_menu;
|
2019-07-08 02:47:50 +02:00
|
|
|
m->end = end_char_menu;
|
2019-04-20 18:06:27 +02:00
|
|
|
m->transition = TransFadeBlack;
|
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
|
|
|
m->flags = MF_Abortable;
|
2019-07-08 02:47:50 +02:00
|
|
|
|
2023-01-09 04:19:31 +01:00
|
|
|
auto ctx = ALLOC(CharMenuContext, {
|
|
|
|
.subshot = progress.game_settings.shotmode,
|
|
|
|
.prev_selected_char = -1,
|
|
|
|
});
|
2019-07-08 02:47:50 +02:00
|
|
|
m->context = ctx;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
for(uintptr_t i = 0; i < NUM_CHARACTERS; ++i) {
|
2019-07-08 02:47:50 +02:00
|
|
|
MenuEntry *e = add_menu_entry(m, NULL, set_player_mode, (void*)i);
|
|
|
|
e->transition = transition_to_game;
|
|
|
|
e->drawdata = 1;
|
2017-10-10 06:32:27 +02:00
|
|
|
|
2017-10-10 07:09:16 +02:00
|
|
|
if(i == progress.game_settings.character) {
|
2017-10-10 06:32:27 +02:00
|
|
|
m->cursor = i;
|
|
|
|
}
|
2017-10-08 13:30:51 +02:00
|
|
|
}
|
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
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
for(CharacterID c = 0; c < NUM_CHARACTERS; ++c) {
|
|
|
|
ctx->char_draw_order[c] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->drawdata[1] = 1;
|
|
|
|
|
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
|
|
|
return m;
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
|
|
|
|
2012-08-12 16:54:48 +02:00
|
|
|
void draw_char_menu(MenuData *menu) {
|
2019-07-08 02:47:50 +02:00
|
|
|
CharMenuContext *ctx = menu->context;
|
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
r_state_push();
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
static const char *const prefixes[] = {
|
2019-04-22 02:07:14 +02:00
|
|
|
"Intuition",
|
|
|
|
"Science",
|
|
|
|
};
|
|
|
|
|
2019-04-21 10:37:50 +02:00
|
|
|
assert(menu->cursor < 3);
|
2020-04-05 04:51:00 +02:00
|
|
|
PlayerCharacter *selected_char = plrchar_get((CharacterID)(uintptr_t)dynarray_get(&menu->entries, menu->cursor).arg);
|
2019-04-22 14:29:44 +02:00
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
draw_main_menu_bg(menu, SCREEN_W/4+100, 0, 0.1 * (0.5 + 0.5 * menu->drawdata[1]), "menu/mainmenubg", selected_char->menu_texture_name);
|
2017-02-19 03:16:05 +01:00
|
|
|
draw_menu_title(menu, "Select Character");
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2017-12-24 17:38:08 +01:00
|
|
|
CharacterID current_char = 0;
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
dynarray_foreach_idx(&menu->entries, int j, {
|
2019-07-08 02:47:50 +02:00
|
|
|
CharacterID i = ctx->char_draw_order[j];
|
2020-04-05 04:51:00 +02:00
|
|
|
MenuEntry *e = dynarray_get_ptr(&menu->entries, i);
|
2019-07-08 02:47:50 +02:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
PlayerCharacter *pchar = plrchar_get((CharacterID)(uintptr_t)e->arg);
|
2017-10-08 13:30:51 +02:00
|
|
|
assert(pchar != NULL);
|
2019-07-08 02:47:50 +02:00
|
|
|
assert(pchar->id == i);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-27 21:58:25 +02:00
|
|
|
Sprite *spr = portrait_get_base_sprite(pchar->lower_name, NULL); // TODO cache this
|
2017-10-08 13:30:51 +02:00
|
|
|
const char *name = pchar->full_name;
|
|
|
|
const char *title = pchar->title;
|
|
|
|
|
|
|
|
if(menu->cursor == i) {
|
|
|
|
current_char = pchar->id;
|
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
float o = 1 - e->drawdata*2;
|
2019-07-08 02:47:50 +02:00
|
|
|
const char *face;
|
|
|
|
|
|
|
|
if(menu->selected == i) {
|
2019-08-22 21:43:34 +02:00
|
|
|
face = facedefs[i][SELECTED_SUBSHOT(menu) == PLR_SHOT_A ? F_HAPPY : F_SMUG];
|
2019-07-08 02:47:50 +02:00
|
|
|
} else if(fabs(o - 1) < 1e-1) {
|
2019-08-22 21:43:34 +02:00
|
|
|
face = facedefs[i][F_NORMAL];
|
2019-07-08 02:47:50 +02:00
|
|
|
} else if(menu->cursor == i) {
|
2019-08-22 21:43:34 +02:00
|
|
|
face = facedefs[i][F_SURPRISED];
|
2019-07-08 02:47:50 +02:00
|
|
|
} else {
|
2019-08-22 21:43:34 +02:00
|
|
|
face = facedefs[i][F_UNAMUSED];
|
2019-07-08 02:47:50 +02:00
|
|
|
}
|
|
|
|
|
2023-09-23 21:56:34 +02:00
|
|
|
float pofs = max(0.0f, e->drawdata * 1.5f - 0.5f);
|
2019-07-08 02:47:50 +02:00
|
|
|
pofs = glm_ease_back_in(pofs);
|
|
|
|
|
|
|
|
if(i != menu->selected) {
|
|
|
|
pofs = lerp(pofs, 1, menu_fade(menu));
|
|
|
|
}
|
|
|
|
|
|
|
|
float pbrightness = 0.6 + 0.4 * o;
|
|
|
|
|
|
|
|
SpriteParams portrait_params = {
|
|
|
|
.pos = { SCREEN_W/2 + 240 + 320 * pofs, SCREEN_H - spr->h * 0.5 },
|
2019-07-03 19:50:43 +02:00
|
|
|
.sprite_ptr = spr,
|
2024-09-22 21:06:57 +02:00
|
|
|
.shader_ptr = res_shader("sprite_default"),
|
2019-07-08 02:47:50 +02:00
|
|
|
.color = RGBA(pbrightness, pbrightness, pbrightness, 1),
|
2019-07-03 19:50:43 +02:00
|
|
|
// .flip.x = true,
|
2019-07-08 02:47:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
r_draw_sprite(&portrait_params);
|
2020-06-09 03:33:22 +02:00
|
|
|
portrait_params.sprite_ptr = res_sprite(face);
|
2019-07-08 02:47:50 +02:00
|
|
|
r_draw_sprite(&portrait_params);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_push();
|
|
|
|
r_mat_mv_translate(SCREEN_W/4, SCREEN_H/3, 0);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_push();
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
if(e->drawdata != 0) {
|
|
|
|
r_mat_mv_translate(0, -300 * e->drawdata, 0);
|
|
|
|
r_mat_mv_rotate(M_PI * e->drawdata, 1, 0, 0);
|
2012-04-05 14:24:55 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-06-29 23:36:51 +02:00
|
|
|
text_draw(name, &(TextParams) {
|
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.font = "big",
|
2024-09-22 21:06:57 +02:00
|
|
|
.shader_ptr = res_shader("text_default"),
|
2019-07-03 19:50:43 +02:00
|
|
|
.color = RGBA(o, o, o, o),
|
2018-06-29 23:36:51 +02:00
|
|
|
});
|
2018-07-23 19:07:59 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
if(e->drawdata) {
|
|
|
|
o = 1 - e->drawdata * 3;
|
2017-10-08 13:30:51 +02:00
|
|
|
} else {
|
2018-07-23 19:07:59 +02:00
|
|
|
o = 1;
|
2017-10-08 13:30:51 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2018-06-29 23:36:51 +02:00
|
|
|
text_draw(title, &(TextParams) {
|
|
|
|
.align = ALIGN_CENTER,
|
2019-04-21 10:37:50 +02:00
|
|
|
.pos = { 20*(1-o), 30 },
|
2024-09-22 21:06:57 +02:00
|
|
|
.shader_ptr = res_shader("text_default"),
|
2018-07-23 19:07:59 +02:00
|
|
|
.color = RGBA(o, o, o, o),
|
2018-06-29 23:36:51 +02:00
|
|
|
});
|
2018-07-23 19:07:59 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
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_push();
|
|
|
|
r_mat_mv_translate(SCREEN_W/4, SCREEN_H/3, 0);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
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
|
|
|
ShotModeID current_subshot = SELECTED_SUBSHOT(menu);
|
2019-07-03 19:50:43 +02:00
|
|
|
|
2019-04-21 10:37:50 +02:00
|
|
|
float f = menu->drawdata[0]-PLR_SHOT_A;
|
2020-06-09 03:33:22 +02:00
|
|
|
float selbg_ofs = 200 + (100-70)*f-20*f - font_get_lineskip(res_font("standard")) * 0.7;
|
2019-04-22 02:07:14 +02:00
|
|
|
|
2019-04-21 10:37:50 +02:00
|
|
|
r_color4(0, 0, 0, 0.5);
|
|
|
|
r_shader_standard_notex();
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_push();
|
|
|
|
r_mat_mv_translate(-150, selbg_ofs + menu->drawdata[2] * 0.5, 0);
|
|
|
|
r_mat_mv_scale(650, menu->drawdata[2], 1);
|
2019-04-21 10:37:50 +02:00
|
|
|
r_draw_quad();
|
|
|
|
r_shader_standard();
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
2019-04-21 10:37:50 +02:00
|
|
|
|
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
|
|
|
for(ShotModeID shot = PLR_SHOT_A; shot < NUM_SHOT_MODES_PER_CHARACTER; shot++) {
|
|
|
|
PlayerMode *mode = plrmode_find(current_char, shot);
|
|
|
|
assume(mode != NULL);
|
2019-04-21 10:37:50 +02:00
|
|
|
int shotidx = shot-PLR_SHOT_A;
|
|
|
|
|
|
|
|
float o = 1-fabs(f - shotidx);
|
|
|
|
float al = 0.2+o;
|
|
|
|
if(shot == current_subshot && shot == PLR_SHOT_A) {
|
|
|
|
r_color4(0.9*al, 0.6*al, 0.2*al, 1*al);
|
|
|
|
} else if(shot == current_subshot && shot == PLR_SHOT_B) {
|
|
|
|
r_color4(0.2*al, 0.6*al, 0.9*al, 1*al);
|
2017-10-08 13:30:51 +02:00
|
|
|
} else {
|
2019-04-21 10:37:50 +02:00
|
|
|
r_color4(al, al, al, al);
|
2017-10-08 13:30:51 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-04-22 02:07:14 +02:00
|
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "%s: %s", prefixes[shot - PLR_SHOT_A], mode->name);
|
|
|
|
|
2019-04-21 10:37:50 +02:00
|
|
|
double y = 200 + (100-70*f)*shotidx-20*f;
|
2019-04-22 02:07:14 +02:00
|
|
|
text_draw(buf, &(TextParams) {
|
2018-06-29 23:36:51 +02:00
|
|
|
.align = ALIGN_CENTER,
|
2019-04-21 10:37:50 +02:00
|
|
|
.pos = { 0, y},
|
2024-09-22 21:06:57 +02:00
|
|
|
.shader_ptr = res_shader("text_default"),
|
2018-06-29 23:36:51 +02:00
|
|
|
});
|
2019-04-21 10:37:50 +02:00
|
|
|
|
|
|
|
if(shot == current_subshot) {
|
|
|
|
r_color4(o, o, o, o);
|
2019-04-22 02:07:14 +02:00
|
|
|
text_draw_wrapped(mode->description, DESCRIPTION_WIDTH, &(TextParams) {
|
2019-04-21 10:37:50 +02:00
|
|
|
.align = ALIGN_CENTER,
|
|
|
|
.pos = { 0, y + 30 },
|
2024-09-22 21:06:57 +02:00
|
|
|
.shader_ptr = res_shader("text_default"),
|
2019-04-21 10:37:50 +02:00
|
|
|
});
|
|
|
|
}
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
2017-10-08 13:30:51 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
2018-07-23 19:07:59 +02:00
|
|
|
|
|
|
|
float o = 0.3*sin(menu->frames/20.0)+0.5;
|
2020-04-05 04:51:00 +02:00
|
|
|
o *= 1 - dynarray_get(&menu->entries, menu->cursor).drawdata;
|
2019-07-03 19:50:43 +02:00
|
|
|
r_shader("sprite_default");
|
|
|
|
|
|
|
|
r_draw_sprite(&(SpriteParams) {
|
2024-09-22 21:06:57 +02:00
|
|
|
.sprite_ptr = res_sprite("menu/arrow"),
|
2019-07-03 19:50:43 +02:00
|
|
|
.pos = { 30, SCREEN_H/3+10 },
|
|
|
|
.color = RGBA(o, o, o, o),
|
|
|
|
.scale = { 0.5, 0.7 },
|
|
|
|
});
|
|
|
|
|
|
|
|
r_draw_sprite(&(SpriteParams) {
|
2024-09-22 21:06:57 +02:00
|
|
|
.sprite_ptr = res_sprite("menu/arrow"),
|
2019-07-03 19:50:43 +02:00
|
|
|
.pos = { 30 + 340, SCREEN_H/3+10 },
|
|
|
|
.color = RGBA(o, o, o, o),
|
|
|
|
.scale = { 0.5, 0.7 },
|
|
|
|
.flip.x = true,
|
|
|
|
});
|
|
|
|
|
|
|
|
r_state_pop();
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static bool char_menu_input_handler(SDL_Event *event, void *arg) {
|
2012-08-13 17:50:28 +02:00
|
|
|
MenuData *menu = arg;
|
2019-07-08 02:47:50 +02:00
|
|
|
CharMenuContext *ctx = menu->context;
|
2017-09-29 21:03:49 +02:00
|
|
|
TaiseiEvent type = TAISEI_EVENT(event->type);
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
int prev_cursor = menu->cursor;
|
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
if(type == TE_MENU_CURSOR_RIGHT) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("generic_shot");
|
2012-08-13 17:50:28 +02:00
|
|
|
menu->cursor++;
|
2017-09-29 21:03:49 +02:00
|
|
|
} else if(type == TE_MENU_CURSOR_LEFT) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("generic_shot");
|
2012-08-13 17:50:28 +02:00
|
|
|
menu->cursor--;
|
2017-09-29 21:03:49 +02:00
|
|
|
} else if(type == TE_MENU_CURSOR_DOWN) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("generic_shot");
|
2019-07-08 02:47:50 +02:00
|
|
|
ctx->subshot++;
|
2017-09-29 21:03:49 +02:00
|
|
|
} else if(type == TE_MENU_CURSOR_UP) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("generic_shot");
|
2019-07-08 02:47:50 +02:00
|
|
|
ctx->subshot--;
|
2017-09-29 21:03:49 +02:00
|
|
|
} else if(type == TE_MENU_ACCEPT) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("shot_special1");
|
2012-08-13 17:50:28 +02:00
|
|
|
menu->selected = menu->cursor;
|
2019-07-08 02:47:50 +02:00
|
|
|
menu->transition_in_time = FADE_TIME * 1.5;
|
|
|
|
menu->transition_out_time = FADE_TIME * 3;
|
2012-08-13 17:50:28 +02:00
|
|
|
close_menu(menu);
|
2017-09-29 21:03:49 +02:00
|
|
|
} else if(type == TE_MENU_ABORT) {
|
2020-06-22 16:41:03 +02:00
|
|
|
play_sfx_ui("hit");
|
2012-08-13 17:50:28 +02:00
|
|
|
close_menu(menu);
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
|
2020-04-05 04:51:00 +02:00
|
|
|
menu->cursor = (menu->cursor % menu->entries.num_elements) + menu->entries.num_elements * (menu->cursor < 0);
|
2019-07-08 02:47:50 +02:00
|
|
|
ctx->subshot = (ctx->subshot % NUM_SHOT_MODES_PER_CHARACTER) + NUM_SHOT_MODES_PER_CHARACTER * (ctx->subshot < 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
|
|
|
|
2019-07-08 02:47:50 +02:00
|
|
|
if(menu->cursor != prev_cursor) {
|
2020-04-05 04:51:00 +02:00
|
|
|
if(ctx->prev_selected_char != menu->cursor || dynarray_get(&menu->entries, menu->cursor).drawdata > 0.95) {
|
2019-07-08 02:47:50 +02:00
|
|
|
ctx->prev_selected_char = prev_cursor;
|
|
|
|
update_char_draw_order(menu);
|
|
|
|
}
|
|
|
|
}
|
2017-09-29 21:03:49 +02:00
|
|
|
|
|
|
|
return false;
|
2012-08-13 17:50:28 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
static void char_menu_input(MenuData *menu) {
|
2017-09-29 21:03:49 +02:00
|
|
|
events_poll((EventHandler[]){
|
|
|
|
{ .proc = char_menu_input_handler, .arg = menu },
|
2017-12-26 12:07:40 +01:00
|
|
|
{ NULL }
|
2017-09-29 21:03:49 +02:00
|
|
|
}, EFLAG_MENU);
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
2019-08-22 21:43:34 +02:00
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
void preload_char_menu(ResourceGroup *rg) {
|
2020-04-27 21:58:25 +02:00
|
|
|
for(int i = 0; i < NUM_CHARACTERS; ++i) {
|
|
|
|
PlayerCharacter *pchar = plrchar_get(i);
|
2023-03-22 23:41:32 +01:00
|
|
|
portrait_preload_base_sprite(rg, pchar->lower_name, NULL, RESF_DEFAULT);
|
|
|
|
res_group_preload(rg, RES_TEXTURE, RESF_DEFAULT, pchar->menu_texture_name, NULL);
|
2020-04-27 21:58:25 +02:00
|
|
|
}
|
|
|
|
|
2019-08-22 21:43:34 +02:00
|
|
|
char *p = (char*)facedefs;
|
|
|
|
|
|
|
|
for(int i = 0; i < sizeof(facedefs) / FACENAME_LEN; ++i) {
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_SPRITE, RESF_DEFAULT, p + i * FACENAME_LEN, NULL);
|
2019-08-22 21:43:34 +02:00
|
|
|
}
|
2023-06-17 04:19:11 +02:00
|
|
|
portrait_preload_face_sprite(rg, "reimu", "annoyed", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "reimu", "assertive", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "reimu", "irritated", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "reimu", "outraged", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "reimu", "sigh", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "reimu", "unsettled", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "marisa", "sweat_smile", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "marisa", "inquisitive", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "youmu", "eeeeh", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "youmu", "embarrassed", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "youmu", "eyes_closed", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "youmu", "relaxed", RESF_DEFAULT);
|
|
|
|
portrait_preload_face_sprite(rg, "youmu", "sigh", RESF_DEFAULT);
|
2019-08-22 21:43:34 +02:00
|
|
|
}
|