taisei/src/stage.c

924 lines
23 KiB
C
Raw Normal View History

/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "util.h"
#include "stage.h"
#include <time.h>
#include "global.h"
2012-07-28 22:53:53 +02:00
#include "video.h"
2017-01-24 14:40:57 +01:00
#include "resource/bgm.h"
#include "replay.h"
#include "config.h"
#include "player.h"
#include "menu/ingamemenu.h"
#include "menu/gameovermenu.h"
#include "audio/audio.h"
Implemented a simple and consistent logging subsystem The goal of this change is mainly to clean up Taisei's codebase and improve its console output. I've been frustrated by files littered with inconsistent printf/fprintf/warnx/errx calls for a long time, and now I actually did something about it. All the above functions are now considered deprecated and result in a compile-time warning when used. Instead, the following macros should be used: log_debug(format, ...) log_info(format, ...) log_warn(format, ...) log_err(format, ...) As you can see, all of them have the same printf-like interface. But they have different functionality and purpose: log_debug is intended for very verbose and specific information. It does nothing in release builds, much like assert(), so don't use expressions with side-effects in its arguments. log_info is for various status updates that are expected during normal operation of the program. log_warn is for non-critical failures or other things that may be worth investigating, but don't inherently render the program non-functional. log_err is for when the only choice is to give up. Like errx, it also terminates the program. Unlike errx, it actually calls abort(), which means the cleanup functions are not ran -- but on the other hand, you get a debuggable backtrace. However, if you're trying to catch programming errors, consider using assert() instead. All of them produce output that contains a timestamp, the log level identifier, the calling function's name, and the formatted message. The newline at the end of the format string is not required -- no, it is actually *prohibited*. The logging system will take care of the line breaks by itself, don't litter the code with that shit. Internally, the logging system is based on the SDL_RWops abstraction, and may have multiple, configurable destinations. This makes it easily extensible. Currently, log_debug and log_info are set to write to stdout, log_warn and log_err to stderr, and all of them also to the file log.txt in the Taisei config directory. Consequently, the nasty freopen hacks we used to make Taisei write to log files on Windows are no longer needed -- which is a very good thing, considering they probably would break if the configdir path contains UTF-8 characters. SDL_RWFromFile does not suffer this limitation. As an added bonus, it's also thread-safe. Note about printf and fprintf: in very few cases, the logging system is not a good substitute for these functions. That is, when you care about writing exactly to stdout/stderr and about exactly how the output looks. However, I insist on keeping the deprecation warnings on them to not tempt anyone to use them for logging/debugging out of habit and/or laziness. For this reason, I've added a tsfprintf function to util.c. It is functionally identical to fprintf, except it returns void. Yes, the name is deliberately ugly. Avoid using it if possible, but if you must, only use it to write to stdout or stderr. Do not write to actual files with it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
#include "log.h"
#include "stagetext.h"
#include "stagedraw.h"
2017-12-13 20:05:12 +01:00
#include "stageobjects.h"
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
#include "eventloop/eventloop.h"
2018-08-14 00:48:18 +02:00
#ifdef DEBUG
#define DPSTEST
#include "stages/dpstest.h"
#endif
2017-02-27 15:27:48 +01:00
static size_t numstages = 0;
StageInfo *stages = NULL;
static void add_stage(uint16_t id, StageProcs *procs, StageType type, const char *title, const char *subtitle, AttackInfo *spell, Difficulty diff) {
2017-02-27 15:27:48 +01:00
++numstages;
stages = realloc(stages, numstages * sizeof(StageInfo));
StageInfo *stg = stages + (numstages - 1);
memset(stg, 0, sizeof(StageInfo));
stg->id = id;
stg->procs = procs;
stg->type = type;
stralloc(&stg->title, title);
stralloc(&stg->subtitle, subtitle);
stg->spell = spell;
stg->difficulty = diff;
}
static void end_stages(void) {
add_stage(0, NULL, 0, NULL, NULL, NULL, 0);
2017-02-27 15:27:48 +01:00
}
static void add_spellpractice_stage(StageInfo *s, AttackInfo *a, int *spellnum, uint16_t spellbits, Difficulty diff) {
uint16_t id = spellbits | a->idmap[diff - D_Easy] | (s->id << 8);
char *title = strfmt("Spell %d", ++(*spellnum));
char *subtitle = strjoin(a->name, " ~ ", difficulty_name(diff), NULL);
add_stage(id, s->procs->spellpractice_procs, STAGE_SPELL, title, subtitle, a, diff);
free(title);
free(subtitle);
}
2017-02-27 16:36:43 +01:00
static void add_spellpractice_stages(int *spellnum, bool (*filter)(AttackInfo*), uint16_t spellbits) {
for(int i = 0 ;; ++i) {
2017-02-27 15:27:48 +01:00
StageInfo *s = stages + i;
2018-08-14 00:48:18 +02:00
if(s->type == STAGE_SPELL || !s->spell) {
2017-02-27 16:36:43 +01:00
break;
}
2017-02-27 15:27:48 +01:00
for(AttackInfo *a = s->spell; a->rule; ++a) {
2017-02-27 16:36:43 +01:00
if(!filter(a)) {
continue;
}
2017-02-27 15:27:48 +01:00
for(Difficulty diff = D_Easy; diff < D_Easy + NUM_SELECTABLE_DIFFICULTIES; ++diff) {
if(a->idmap[diff - D_Easy] >= 0) {
add_spellpractice_stage(s, a, spellnum, spellbits, diff);
2017-02-27 15:27:48 +01:00
s = stages + i; // stages just got realloc'd, so we must update the pointer
}
}
}
2017-02-27 15:27:48 +01:00
}
2017-02-27 16:36:43 +01:00
}
static bool spellfilter_normal(AttackInfo *spell) {
return spell->type != AT_ExtraSpell;
}
static bool spellfilter_extra(AttackInfo *spell) {
return spell->type == AT_ExtraSpell;
}
void stage_init_array(void) {
int spellnum = 0;
// id procs type title subtitle spells diff
add_stage(1, &stage1_procs, STAGE_STORY, "Stage 1", "Misty Lake", (AttackInfo*)&stage1_spells, D_Any);
add_stage(2, &stage2_procs, STAGE_STORY, "Stage 2", "Walk Along the Border", (AttackInfo*)&stage2_spells, D_Any);
add_stage(3, &stage3_procs, STAGE_STORY, "Stage 3", "Through the Tunnel of Light", (AttackInfo*)&stage3_spells, D_Any);
add_stage(4, &stage4_procs, STAGE_STORY, "Stage 4", "Forgotten Mansion", (AttackInfo*)&stage4_spells, D_Any);
add_stage(5, &stage5_procs, STAGE_STORY, "Stage 5", "Climbing the Tower of Babel", (AttackInfo*)&stage5_spells, D_Any);
add_stage(6, &stage6_procs, STAGE_STORY, "Stage 6", "Roof of the World", (AttackInfo*)&stage6_spells, D_Any);
2017-02-27 16:36:43 +01:00
2018-08-14 00:48:18 +02:00
#ifdef DPSTEST
add_stage(0x40|0, &stage_dpstest_single_procs, STAGE_SPECIAL, "DPS Test", "Single target", NULL, D_Normal);
add_stage(0x40|1, &stage_dpstest_multi_procs, STAGE_SPECIAL, "DPS Test", "Multiple targets", NULL, D_Normal);
add_stage(0x40|2, &stage_dpstest_boss_procs, STAGE_SPECIAL, "DPS Test", "Boss", NULL, D_Normal);
#endif
2017-02-27 16:36:43 +01:00
// generate spellpractice stages
add_spellpractice_stages(&spellnum, spellfilter_normal, STAGE_SPELL_BIT);
add_spellpractice_stages(&spellnum, spellfilter_extra, STAGE_SPELL_BIT | STAGE_EXTRASPELL_BIT);
2017-02-27 15:27:48 +01:00
#ifdef SPELL_BENCHMARK
add_spellpractice_stage(stages, &stage1_spell_benchmark, &spellnum, STAGE_SPELL_BIT, D_Extra);
#endif
2017-02-27 15:27:48 +01:00
end_stages();
#ifdef DEBUG
2017-02-27 15:27:48 +01:00
for(int i = 0; stages[i].procs; ++i) {
if(stages[i].type == STAGE_SPELL && !(stages[i].id & STAGE_SPELL_BIT)) {
2017-03-13 17:03:51 +01:00
log_fatal("Spell stage has an ID without the spell bit set: 0x%04x", stages[i].id);
}
2017-02-26 13:17:48 +01:00
for(int j = 0; stages[j].procs; ++j) {
if(i != j && stages[i].id == stages[j].id) {
2017-12-31 09:08:07 +01:00
log_fatal("Duplicate ID %X in stages array, indices: %i, %i", stages[i].id, i, j);
}
}
}
2017-02-27 15:27:48 +01:00
#endif
}
2017-02-15 13:53:24 +01:00
void stage_free_array(void) {
2017-02-26 13:17:48 +01:00
for(StageInfo *stg = stages; stg->procs; ++stg) {
2017-02-27 15:27:48 +01:00
free(stg->title);
free(stg->subtitle);
free(stg->progress);
2017-02-15 13:53:24 +01:00
}
2017-02-27 15:27:48 +01:00
free(stages);
2017-02-15 13:53:24 +01:00
}
// NOTE: This returns the stage BY ID, not by the array index!
StageInfo* stage_get(uint16_t n) {
2017-02-26 13:17:48 +01:00
for(StageInfo *stg = stages; stg->procs; ++stg)
if(stg->id == n)
return stg;
return NULL;
}
2017-02-10 23:34:48 +01:00
StageInfo* stage_get_by_spellcard(AttackInfo *spell, Difficulty diff) {
if(!spell)
return NULL;
2017-02-26 13:17:48 +01:00
for(StageInfo *stg = stages; stg->procs; ++stg)
2017-02-10 23:34:48 +01:00
if(stg->spell == spell && stg->difficulty == diff)
return stg;
return NULL;
}
StageProgress* stage_get_progress_from_info(StageInfo *stage, Difficulty diff, bool allocate) {
2017-02-11 12:38:50 +01:00
// D_Any stages will have a separate StageProgress for every selectable difficulty.
// Stages with a fixed difficulty setting (spellpractice, extra stage...) obviously get just one and the diff parameter is ignored.
// This stuff must stay around until progress_save(), which happens on shutdown.
// So do NOT try to free any pointers this function returns, that will fuck everything up.
if(!stage) {
return NULL;
}
bool fixed_diff = (stage->difficulty != D_Any);
if(!fixed_diff && (diff < D_Easy || diff > D_Lunatic)) {
return NULL;
}
if(!stage->progress) {
if(!allocate) {
return NULL;
}
2017-02-11 12:38:50 +01:00
size_t allocsize = sizeof(StageProgress) * (fixed_diff ? 1 : NUM_SELECTABLE_DIFFICULTIES);
stage->progress = malloc(allocsize);
memset(stage->progress, 0, allocsize);
}
return stage->progress + (fixed_diff ? 0 : diff - D_Easy);
}
StageProgress* stage_get_progress(uint16_t id, Difficulty diff, bool allocate) {
return stage_get_progress_from_info(stage_get(id), diff, allocate);
2017-02-11 12:38:50 +01:00
}
2017-02-26 13:17:48 +01:00
static void stage_start(StageInfo *stage) {
global.timer = 0;
global.frames = 0;
global.gameover = 0;
2012-08-07 17:01:26 +02:00
global.shake_view = 0;
global.voltage_threshold = 0;
player_stage_pre_init(&global.plr);
2017-02-26 13:17:48 +01:00
if(stage->type == STAGE_SPELL) {
global.is_practice_mode = true;
global.plr.lives = 0;
2017-02-26 13:17:48 +01:00
global.plr.bombs = 0;
2017-09-11 21:09:30 +02:00
} else if(global.is_practice_mode) {
global.plr.lives = PLR_STGPRACTICE_LIVES;
global.plr.bombs = PLR_STGPRACTICE_BOMBS;
}
if(global.is_practice_mode) {
global.plr.power = config_get_int(CONFIG_PRACTICE_POWER);
}
if(global.plr.power < 0) {
global.plr.power = 0;
} else if(global.plr.power > PLR_MAX_POWER) {
global.plr.power = PLR_MAX_POWER;
2017-02-26 13:17:48 +01:00
}
reset_sounds();
}
2017-10-10 16:06:46 +02:00
static bool ingame_menu_interrupts_bgm(void) {
return global.stage->type != STAGE_SPELL;
}
2017-12-17 01:00:06 +01:00
static void stage_fade_bgm(void) {
fade_bgm((FPS * FADE_TIME) / 2000.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
static void stage_leave_ingame_menu(CallChainResult ccr) {
MenuData *m = ccr.result;
2017-10-10 16:06:46 +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
if(m->state != MS_Dead) {
return;
}
2017-10-10 16:06:46 +02:00
if(global.gameover > 0) {
2017-10-10 16:06:46 +02:00
stop_sounds();
if(ingame_menu_interrupts_bgm() || global.gameover != GAMEOVER_RESTART) {
2017-12-17 01:00:06 +01:00
stage_fade_bgm();
2017-10-10 16:06:46 +02:00
}
} else {
resume_sounds();
resume_bgm();
}
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
CallChain *cc = ccr.ctx;
run_call_chain(cc, NULL);
free(cc);
}
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 stage_enter_ingame_menu(MenuData *m, CallChain next) {
if(ingame_menu_interrupts_bgm()) {
stop_bgm(false);
2018-07-24 20:30:45 +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
pause_sounds();
enter_menu(m, CALLCHAIN(stage_leave_ingame_menu, memdup(&next, sizeof(next))));
}
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
void stage_pause(void) {
if(global.gameover == GAMEOVER_TRANSITIONING) {
return;
}
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
stage_enter_ingame_menu(
(global.replaymode == REPLAY_PLAY
? create_ingame_menu_replay
: create_ingame_menu
)(), NO_CALLCHAIN
);
}
void stage_gameover(void) {
if(global.stage->type == STAGE_SPELL && config_get_int(CONFIG_SPELLSTAGE_AUTORESTART)) {
global.gameover = GAMEOVER_RESTART;
return;
}
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
stage_enter_ingame_menu(create_gameover_menu(), NO_CALLCHAIN);
}
2017-12-28 02:35:53 +01:00
static bool stage_input_common(SDL_Event *event, void *arg) {
TaiseiEvent type = TAISEI_EVENT(event->type);
int32_t code = event->user.code;
switch(type) {
case TE_GAME_KEY_DOWN:
switch(code) {
case KEY_STOP:
stage_finish(GAMEOVER_DEFEAT);
return true;
case KEY_RESTART:
stage_finish(GAMEOVER_RESTART);
return true;
}
break;
case TE_GAME_PAUSE:
stage_pause();
break;
default:
break;
}
return false;
}
static bool stage_input_key_filter(KeyIndex key, bool is_release) {
if(key == KEY_HAHAIWIN) {
IF_DEBUG(
if(!is_release) {
stage_finish(GAMEOVER_WIN);
}
);
return false;
}
IF_NOT_DEBUG(
if(
key == KEY_IDDQD ||
key == KEY_POWERUP ||
key == KEY_POWERDOWN
) {
return false;
}
);
if(stage_is_cleared()) {
if(key == KEY_SHOT) {
if(
global.gameover == GAMEOVER_SCORESCREEN &&
global.frames - global.gameover_time > GAMEOVER_SCORE_DELAY * 2
) {
if(!is_release) {
stage_finish(GAMEOVER_WIN);
}
}
}
if(key == KEY_BOMB || key == KEY_SPECIAL) {
return false;
}
}
return true;
}
static bool stage_input_handler_gameplay(SDL_Event *event, void *arg) {
TaiseiEvent type = TAISEI_EVENT(event->type);
int32_t code = event->user.code;
2017-12-28 02:35:53 +01:00
if(stage_input_common(event, arg)) {
return false;
}
2012-08-13 17:50:28 +02:00
switch(type) {
case TE_GAME_KEY_DOWN:
if(stage_input_key_filter(code, false)) {
player_event_with_replay(&global.plr, EV_PRESS, code);
2017-02-15 18:10:56 +01:00
}
2012-08-13 17:50:28 +02:00
break;
case TE_GAME_KEY_UP:
if(stage_input_key_filter(code, true)) {
player_event_with_replay(&global.plr, EV_RELEASE, code);
}
2012-08-07 05:28:41 +02:00
break;
case TE_GAME_AXIS_LR:
player_event_with_replay(&global.plr, EV_AXIS_LR, (uint16_t)code);
2012-08-15 16:36:39 +02:00
break;
case TE_GAME_AXIS_UD:
player_event_with_replay(&global.plr, EV_AXIS_UD, (uint16_t)code);
2012-08-15 16:36:39 +02:00
break;
2012-08-13 17:50:28 +02:00
default: break;
}
return false;
2012-08-13 17:50:28 +02:00
}
static bool stage_input_handler_replay(SDL_Event *event, void *arg) {
2017-12-28 02:35:53 +01:00
stage_input_common(event, arg);
return false;
2012-08-13 17:50:28 +02:00
}
static void replay_input(void) {
ReplayStage *s = global.replay_stage;
int i;
events_poll((EventHandler[]){
{ .proc = stage_input_handler_replay },
{NULL}
}, EFLAG_GAME);
for(i = s->playpos; i < s->numevents; ++i) {
ReplayEvent *e = s->events + i;
2012-07-29 15:35:40 +02:00
if(e->frame != global.frames)
break;
2012-07-29 15:35:40 +02:00
switch(e->type) {
case EV_OVER:
global.gameover = GAMEOVER_DEFEAT;
break;
case EV_CHECK_DESYNC:
s->desync_check = e->value;
break;
case EV_FPS:
s->fps = e->value;
break;
default: {
2017-10-31 10:48:30 +01:00
player_event(&global.plr, e->type, (int16_t)e->value, NULL, NULL);
break;
}
}
}
2012-08-07 05:28:41 +02:00
s->playpos = i;
player_applymovement(&global.plr);
}
static void stage_input(void) {
events_poll((EventHandler[]){
{ .proc = stage_input_handler_gameplay },
{NULL}
}, EFLAG_GAME);
player_fix_input(&global.plr);
player_applymovement(&global.plr);
}
2017-02-26 13:17:48 +01:00
static void stage_logic(void) {
player_logic(&global.plr);
process_boss(&global.boss);
process_enemies(&global.enemies);
process_projectiles(&global.projs, true);
2011-04-29 10:26:37 +02:00
process_items();
process_lasers();
process_projectiles(&global.particles, false);
process_dialog(&global.dialog);
update_sounds();
global.frames++;
if(!dialog_is_active(global.dialog) && (!global.boss || boss_is_fleeing(global.boss))) {
global.timer++;
}
2017-02-26 20:27:10 +01:00
if(global.replaymode == REPLAY_PLAY &&
global.frames == global.replay_stage->events[global.replay_stage->numevents-1].frame - FADE_TIME &&
global.gameover != GAMEOVER_TRANSITIONING) {
2017-02-26 13:17:48 +01:00
stage_finish(GAMEOVER_DEFEAT);
}
stagetext_update();
}
void stage_clear_hazards_predicate(bool (*predicate)(EntityInterface *ent, void *arg), void *arg, ClearHazardsFlags flags) {
2019-03-26 16:58:38 +01:00
bool force = flags & CLEAR_HAZARDS_FORCE;
if(flags & CLEAR_HAZARDS_BULLETS) {
for(Projectile *p = global.projs.first, *next; p; p = next) {
next = p->next;
2019-03-26 16:58:38 +01:00
if(!force && !projectile_is_clearable(p)) {
continue;
}
if(!predicate || predicate(&p->ent, arg)) {
clear_projectile(p, flags);
}
}
}
if(flags & CLEAR_HAZARDS_LASERS) {
for(Laser *l = global.lasers.first, *next; l; l = next) {
2018-01-06 19:23:38 +01:00
next = l->next;
2019-03-26 16:58:38 +01:00
if(!force && !laser_is_clearable(l)) {
continue;
}
if(!predicate || predicate(&l->ent, arg)) {
clear_laser(l, flags);
}
}
}
}
void stage_clear_hazards(ClearHazardsFlags flags) {
stage_clear_hazards_predicate(NULL, NULL, flags);
}
static bool proximity_predicate(EntityInterface *ent, void *varg) {
Circle *area = varg;
switch(ent->type) {
case ENT_PROJECTILE: {
Projectile *p = ENT_CAST(ent, Projectile);
return cabs(p->pos - area->origin) < area->radius;
}
case ENT_LASER: {
Laser *l = ENT_CAST(ent, Laser);
return laser_intersects_circle(l, *area);
}
default: UNREACHABLE;
}
}
2019-03-26 16:58:38 +01:00
static bool ellipse_predicate(EntityInterface *ent, void *varg) {
Ellipse *e = varg;
switch(ent->type) {
case ENT_PROJECTILE: {
Projectile *p = ENT_CAST(ent, Projectile);
return point_in_ellipse(p->pos, *e);
}
case ENT_LASER: {
Laser *l = ENT_CAST(ent, Laser);
return laser_intersects_ellipse(l, *e);
}
default: UNREACHABLE;
}
}
void stage_clear_hazards_at(complex origin, double radius, ClearHazardsFlags flags) {
Circle area = { origin, radius };
stage_clear_hazards_predicate(proximity_predicate, &area, flags);
}
2019-03-26 16:58:38 +01:00
void stage_clear_hazards_in_ellipse(Ellipse e, ClearHazardsFlags flags) {
stage_clear_hazards_predicate(ellipse_predicate, &e, flags);
}
2017-02-26 13:17:48 +01:00
static void stage_free(void) {
delete_enemies(&global.enemies);
2017-05-05 03:44:05 +02:00
delete_enemies(&global.plr.slaves);
2011-04-29 10:26:37 +02:00
delete_items();
delete_lasers();
delete_projectiles(&global.projs);
delete_projectiles(&global.particles);
if(global.dialog) {
delete_dialog(global.dialog);
global.dialog = NULL;
}
if(global.boss) {
free_boss(global.boss);
global.boss = NULL;
}
Lots of disorganized (mostly) visual overhaul (#156) * WIP some projectile effects * fix segfault * Laser smoothing and glow via post-processing blur magic TODO: make it optional * fix memory corruption * fix memory corruption for realsies now * fix color_get_hsl for out-of-range colors * some more bullet flare tweaks * some lame clear effect workarounds * spawn bullet flares after frame 0; looks better and fixes some problems * New baryon explosion; fix petal_explosion; leanify everything * Add missing bullet flare sprite, rebuild main atlas * improve batching efficiency with bullet spawn flares * forgot git add * Group projectiles/particles by shader where possible * Another take on baryon explosion; make fg framebuffers 16bit * WIP some settings for toasters * remove stupid debug log * microoptimization that probably does nothing anyway * somewhat more intuitive quality settings * Whitelist more particles (MarisaB is on hold) * Whitelist (and fix) some more stage6 particles (mostly ToE) * Add a spell name background * Experimental radial healthbar for bosses * healthbar tweaks * thiccer healthbars in response to feedback * remove healthbar survival timer; just fade out on survivals * Add linear healthbars option; WIP other boss HUD tweaks * Use the proper spell card name format * New font and some random garbage to go along with it * Generate static font outlines for use in text shaders * Use outlines in overlay text shader * Complete boss HUD/healthbar fading logic * fix boss timer limit * stage5 bombs explosion effect * split PFLAG_NOSPAWNZOOM into PFLAG_NOSPAWNFLARE and PFLAG_NOSPAWNFADE; introduce PFLAG_NOSPAWNEFFECTS which disables both (it's just the two values OR'd together) simplify vampiric vapor bullet spawning effect * Remove spawn fade-in from super-fast stage5 fairy projectiles (limiters) * lower particle density in v.vapor in minimal mode * graze effect tweaks * fix text shortening, tweak replay menu layout * stupid debug spam * revisit grazing effects again * dumb debug spam again * improve boss attack timer * overlay effect for boss deaths (similar to the player one) * spice up spellcard declaration (HUD) * don't spawn boss death overlay if fleed * modify Exo2 font to use tabular figures * adjust replay menu for the font change * draw timer & power with standard font (phasing out the numbers font) * WIP new HUD; random fixes/tweaks * hud: move difficulty indicator * hud: move debug stuff around * preloads, mostly * fix youmuA batching conflict * shitty workaround for the shitty screenshake shit * remove extraspell lag by stopping to draw stagebg sooner which is possible because extra spells have a different spellcard_intro timing. Fun fact of the day: the duration of spellcard_intro is always ATTACK_START_DELAY_EXTRA even for normal spells! * new stain particle * i disabled background rendering… * "batch" marisa_b masterspark draws * remove these once a new atlas is generated * make toe quick again * hopefully fix all occurences of changed stain and ScaleFade behavior * tweaking reimu_a and toe boson launch effects * make lhc fast again * softer involnerability effect * fix stage 1 snow on the water bug (and improve performance) translated the time to the future a bit because it only seemed to be an issue for small time values * remove unnecessary spawnflare from toe * tone down extra spell start effect * experimental ReimuB gap shader optimization * fix python3 shebangs * generate simple blur shaders w/ hardcoded kernels * New loading screen * lasers: fix incorrect draw hook registration * add webp support for atlas generator * Use ImageMagick for atlas composition (adds 16-bit support) * Atlas maintenance * make the vampiric vapor bullets less prone to invisibility * Revert a few particles to the quadratic fade curve * experimental baryon effect * improve baryon sprites * disable the baryon effect on minimal postprocessing setting
2019-01-04 23:59:39 +01:00
projectiles_free();
lasers_free();
stagetext_free();
}
2017-02-26 13:17:48 +01:00
static void stage_finalize(void *arg) {
global.gameover = (intptr_t)arg;
2017-02-26 13:17:48 +01:00
}
void stage_finish(int gameover) {
if(global.gameover == GAMEOVER_TRANSITIONING) {
return;
}
int prev_gameover = global.gameover;
global.gameover_time = global.frames;
if(gameover == GAMEOVER_SCORESCREEN) {
global.gameover = GAMEOVER_SCORESCREEN;
} else {
global.gameover = GAMEOVER_TRANSITIONING;
set_transition_callback(TransFadeBlack, FADE_TIME, FADE_TIME*2, stage_finalize, (void*)(intptr_t)gameover);
stage_fade_bgm();
}
if(
global.replaymode != REPLAY_PLAY &&
prev_gameover != GAMEOVER_SCORESCREEN &&
(gameover == GAMEOVER_SCORESCREEN || gameover == GAMEOVER_WIN)
) {
StageProgress *p = stage_get_progress_from_info(global.stage, global.diff, true);
if(p) {
++p->num_cleared;
2019-04-06 20:27:39 +02:00
log_debug("Stage cleared %u times now", p->num_cleared);
}
}
2017-02-26 13:17:48 +01:00
}
static void stage_preload(void) {
difficulty_preload();
projectiles_preload();
player_preload();
items_preload();
boss_preload();
lasers_preload();
enemies_preload();
global.stage->procs->preload();
}
static void display_stage_title(StageInfo *info) {
Premultiplied alpha (#133) * WIP premultiplied alpha * WIP color API rework (doesn't build yet; lots of things left to convert) * convert everything remaining to new Color api except stage*_event.c files * convert the stages to new Color api. builds & runs now; still many rendering errors * fix the bullet shader for premultiplied alpha * fix masterspark, graphs and stage 1 fog clouds * fix marisa_b and most of spellcards * Add deprecation warnings for BLEND_ADD and PFLAG_DRAWADD * fix a segfault in stage 6 undo accidental earlier change * fix text_hud.frag.glsl * fix scuttle bg and remaining stage3 BLEND_ADDs * fix marisa laser opacity * hacky fix for myon The old implementation relied on alpha being stored inside p->color. In premul alpha this doesn’t work and functions like color_set_opacity can’t solve this i think. So I tried messing around with it until it looked somewhat similar. * fix marisa_b stars * remove color_set_opacity i overlooked * more plrmode blending changes * fixup additive blending in stage 1 * various premultiplied alpha fixups for bosses and enemies * stage 2 premul alpha fixups * stage 4 premul alpha fixups * stage 5 premul alpha fixups * stage 6 premul alpha fixups * make lasers also use the PMA blend mode * remove PFLAG_DRAWADD and PFLAG_DRAWSUB * fix remaining PMA issues in menus * lame extraspell bg workaround * fix item alpha * make marisaA lasers look somewhat like in master * fix marisaA bomb background fadeout * fixup various r_color4 calls * fix myon * remove dead code * fix use of BLEND_ADD in player death effect * fix myon shot trails (broken on master as well) * fix myon shot fade-in * extend the sprite shaders custom parameter to a vec4 * fix youmuB stuff and make it look somewhat better. the code looks even worse though.
2018-07-23 19:07:59 +02:00
stagetext_add(info->title, VIEWPORT_W/2 + I * (VIEWPORT_H/2-40), ALIGN_CENTER, get_font("big"), RGB(1, 1, 1), 50, 85, 35, 35);
stagetext_add(info->subtitle, VIEWPORT_W/2 + I * (VIEWPORT_H/2), ALIGN_CENTER, get_font("standard"), RGB(1, 1, 1), 60, 85, 35, 35);
}
void stage_start_bgm(const char *bgm) {
char *old_title = NULL;
if(current_bgm.title && global.stage->type == STAGE_SPELL) {
old_title = strdup(current_bgm.title);
}
start_bgm(bgm);
if(current_bgm.title && current_bgm.started_at >= 0 && (!old_title || strcmp(current_bgm.title, old_title))) {
2017-10-02 02:48:34 +02:00
char txt[strlen(current_bgm.title) + 6];
snprintf(txt, sizeof(txt), "BGM: %s", current_bgm.title);
Premultiplied alpha (#133) * WIP premultiplied alpha * WIP color API rework (doesn't build yet; lots of things left to convert) * convert everything remaining to new Color api except stage*_event.c files * convert the stages to new Color api. builds & runs now; still many rendering errors * fix the bullet shader for premultiplied alpha * fix masterspark, graphs and stage 1 fog clouds * fix marisa_b and most of spellcards * Add deprecation warnings for BLEND_ADD and PFLAG_DRAWADD * fix a segfault in stage 6 undo accidental earlier change * fix text_hud.frag.glsl * fix scuttle bg and remaining stage3 BLEND_ADDs * fix marisa laser opacity * hacky fix for myon The old implementation relied on alpha being stored inside p->color. In premul alpha this doesn’t work and functions like color_set_opacity can’t solve this i think. So I tried messing around with it until it looked somewhat similar. * fix marisa_b stars * remove color_set_opacity i overlooked * more plrmode blending changes * fixup additive blending in stage 1 * various premultiplied alpha fixups for bosses and enemies * stage 2 premul alpha fixups * stage 4 premul alpha fixups * stage 5 premul alpha fixups * stage 6 premul alpha fixups * make lasers also use the PMA blend mode * remove PFLAG_DRAWADD and PFLAG_DRAWSUB * fix remaining PMA issues in menus * lame extraspell bg workaround * fix item alpha * make marisaA lasers look somewhat like in master * fix marisaA bomb background fadeout * fixup various r_color4 calls * fix myon * remove dead code * fix use of BLEND_ADD in player death effect * fix myon shot trails (broken on master as well) * fix myon shot fade-in * extend the sprite shaders custom parameter to a vec4 * fix youmuB stuff and make it look somewhat better. the code looks even worse though.
2018-07-23 19:07:59 +02:00
stagetext_add(txt, VIEWPORT_W-15 + I * (VIEWPORT_H-20), ALIGN_RIGHT, get_font("standard"), RGB(1, 1, 1), 30, 180, 35, 35);
}
free(old_title);
}
void stage_set_voltage_thresholds(uint easy, uint normal, uint hard, uint lunatic) {
switch(global.diff) {
case D_Easy: global.voltage_threshold = easy; return;
case D_Normal: global.voltage_threshold = normal; return;
case D_Hard: global.voltage_threshold = hard; return;
case D_Lunatic: global.voltage_threshold = lunatic; return;
default: UNREACHABLE;
}
}
bool stage_is_cleared(void) {
return global.gameover == GAMEOVER_SCORESCREEN || global.gameover == GAMEOVER_TRANSITIONING;
}
typedef struct StageFrameState {
StageInfo *stage;
int transition_delay;
uint16_t last_replay_fps;
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
CallChain cc;
int logic_calls;
} StageFrameState;
static void stage_update_fps(StageFrameState *fstate) {
if(global.replaymode == REPLAY_RECORD) {
2017-12-26 12:07:40 +01:00
uint16_t replay_fps = (uint16_t)rint(global.fps.logic.fps);
if(replay_fps != fstate->last_replay_fps) {
replay_stage_event(global.replay_stage, global.frames, EV_FPS, replay_fps);
fstate->last_replay_fps = replay_fps;
}
}
}
static void stage_give_clear_bonus(const StageInfo *stage, StageClearBonus *bonus) {
memset(bonus, 0, sizeof(*bonus));
// FIXME: this is clunky...
if(!global.is_practice_mode && stage->type == STAGE_STORY) {
StageInfo *next = stage_get(stage->id + 1);
if(next == NULL || next->type != STAGE_STORY) {
bonus->all_clear = true;
}
}
if(stage->type == STAGE_STORY) {
bonus->base = stage->id * 1000000;
}
if(bonus->all_clear) {
bonus->base += global.plr.point_item_value * 100;
bonus->graze = global.plr.graze * (global.plr.point_item_value / 10);
}
bonus->voltage = imax(0, (int)global.plr.voltage - (int)global.voltage_threshold) * (global.plr.point_item_value / 25);
bonus->lives = global.plr.lives * global.plr.point_item_value * 5;
// TODO: maybe a difficulty multiplier?
bonus->total = bonus->base + bonus->voltage + bonus->lives;
2019-04-07 00:55:13 +02:00
player_add_points(&global.plr, bonus->total, global.plr.pos);
}
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 LogicFrameAction stage_logic_frame(void *arg) {
StageFrameState *fstate = arg;
StageInfo *stage = fstate->stage;
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
++fstate->logic_calls;
stage_update_fps(fstate);
2018-10-02 02:03:51 +02:00
Lots of disorganized (mostly) visual overhaul (#156) * WIP some projectile effects * fix segfault * Laser smoothing and glow via post-processing blur magic TODO: make it optional * fix memory corruption * fix memory corruption for realsies now * fix color_get_hsl for out-of-range colors * some more bullet flare tweaks * some lame clear effect workarounds * spawn bullet flares after frame 0; looks better and fixes some problems * New baryon explosion; fix petal_explosion; leanify everything * Add missing bullet flare sprite, rebuild main atlas * improve batching efficiency with bullet spawn flares * forgot git add * Group projectiles/particles by shader where possible * Another take on baryon explosion; make fg framebuffers 16bit * WIP some settings for toasters * remove stupid debug log * microoptimization that probably does nothing anyway * somewhat more intuitive quality settings * Whitelist more particles (MarisaB is on hold) * Whitelist (and fix) some more stage6 particles (mostly ToE) * Add a spell name background * Experimental radial healthbar for bosses * healthbar tweaks * thiccer healthbars in response to feedback * remove healthbar survival timer; just fade out on survivals * Add linear healthbars option; WIP other boss HUD tweaks * Use the proper spell card name format * New font and some random garbage to go along with it * Generate static font outlines for use in text shaders * Use outlines in overlay text shader * Complete boss HUD/healthbar fading logic * fix boss timer limit * stage5 bombs explosion effect * split PFLAG_NOSPAWNZOOM into PFLAG_NOSPAWNFLARE and PFLAG_NOSPAWNFADE; introduce PFLAG_NOSPAWNEFFECTS which disables both (it's just the two values OR'd together) simplify vampiric vapor bullet spawning effect * Remove spawn fade-in from super-fast stage5 fairy projectiles (limiters) * lower particle density in v.vapor in minimal mode * graze effect tweaks * fix text shortening, tweak replay menu layout * stupid debug spam * revisit grazing effects again * dumb debug spam again * improve boss attack timer * overlay effect for boss deaths (similar to the player one) * spice up spellcard declaration (HUD) * don't spawn boss death overlay if fleed * modify Exo2 font to use tabular figures * adjust replay menu for the font change * draw timer & power with standard font (phasing out the numbers font) * WIP new HUD; random fixes/tweaks * hud: move difficulty indicator * hud: move debug stuff around * preloads, mostly * fix youmuA batching conflict * shitty workaround for the shitty screenshake shit * remove extraspell lag by stopping to draw stagebg sooner which is possible because extra spells have a different spellcard_intro timing. Fun fact of the day: the duration of spellcard_intro is always ATTACK_START_DELAY_EXTRA even for normal spells! * new stain particle * i disabled background rendering… * "batch" marisa_b masterspark draws * remove these once a new atlas is generated * make toe quick again * hopefully fix all occurences of changed stain and ScaleFade behavior * tweaking reimu_a and toe boson launch effects * make lhc fast again * softer involnerability effect * fix stage 1 snow on the water bug (and improve performance) translated the time to the future a bit because it only seemed to be an issue for small time values * remove unnecessary spawnflare from toe * tone down extra spell start effect * experimental ReimuB gap shader optimization * fix python3 shebangs * generate simple blur shaders w/ hardcoded kernels * New loading screen * lasers: fix incorrect draw hook registration * add webp support for atlas generator * Use ImageMagick for atlas composition (adds 16-bit support) * Atlas maintenance * make the vampiric vapor bullets less prone to invisibility * Revert a few particles to the quadratic fade curve * experimental baryon effect * improve baryon sprites * disable the baryon effect on minimal postprocessing setting
2019-01-04 23:59:39 +01:00
if(global.shake_view > 30) {
global.shake_view = 30;
}
2018-10-02 02:03:51 +02:00
if(global.shake_view_fade) {
global.shake_view -= global.shake_view_fade;
if(global.shake_view <= 0) {
global.shake_view = global.shake_view_fade = 0;
}
}
((global.replaymode == REPLAY_PLAY) ? replay_input : stage_input)();
if(global.gameover != GAMEOVER_TRANSITIONING) {
if((!global.boss || boss_is_fleeing(global.boss)) && !dialog_is_active(global.dialog)) {
stage->procs->event();
}
if(global.gameover == GAMEOVER_SCORESCREEN && global.frames - global.gameover_time == GAMEOVER_SCORE_DELAY) {
StageClearBonus b;
stage_give_clear_bonus(stage, &b);
stage_display_clear_screen(&b);
}
2018-07-24 20:30:45 +02:00
if(stage->type == STAGE_SPELL && !global.boss && !fstate->transition_delay) {
fstate->transition_delay = 120;
}
stage->procs->update();
}
replay_stage_check_desync(global.replay_stage, global.frames, (tsrand() ^ global.plr.points) & 0xFFFF, global.replaymode);
stage_logic();
2017-12-28 11:46:08 +01:00
if(fstate->transition_delay) {
2018-07-24 20:30:45 +02:00
if(!--fstate->transition_delay) {
stage_finish(GAMEOVER_WIN);
}
2017-12-28 11:46:08 +01:00
} else {
update_transition();
}
if(global.replaymode == REPLAY_RECORD && global.plr.points > progress.hiscore) {
progress.hiscore = global.plr.points;
}
if(global.gameover > 0) {
2017-12-26 12:07:40 +01:00
return LFRAME_STOP;
}
if(global.frameskip || (global.replaymode == REPLAY_PLAY && gamekeypressed(KEY_SKIP))) {
return LFRAME_SKIP;
}
return LFRAME_WAIT;
}
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 RenderFrameAction stage_render_frame(void *arg) {
2017-12-26 12:07:40 +01:00
StageFrameState *fstate = arg;
StageInfo *stage = fstate->stage;
tsrand_lock(&global.rand_game);
tsrand_switch(&global.rand_visual);
BEGIN_DRAW_CODE();
stage_draw_scene(stage);
END_DRAW_CODE();
tsrand_unlock(&global.rand_game);
tsrand_switch(&global.rand_game);
draw_transition();
2017-12-26 12:07:40 +01:00
return RFRAME_SWAP;
}
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 stage_end_loop(void *ctx);
void stage_enter(StageInfo *stage, CallChain next) {
2017-02-26 13:17:48 +01:00
assert(stage);
assert(stage->procs);
assert(stage->procs->preload);
2017-02-26 13:17:48 +01:00
assert(stage->procs->begin);
assert(stage->procs->end);
assert(stage->procs->draw);
assert(stage->procs->event);
assert(stage->procs->update);
2017-02-26 13:17:48 +01:00
assert(stage->procs->shader_rules);
if(global.gameover == GAMEOVER_WIN) {
global.gameover = 0;
} else if(global.gameover) {
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
run_call_chain(&next, NULL);
return;
}
// I really want to separate all of the game state from the global struct sometime
2017-02-26 13:17:48 +01:00
global.stage = stage;
Lots of disorganized (mostly) visual overhaul (#156) * WIP some projectile effects * fix segfault * Laser smoothing and glow via post-processing blur magic TODO: make it optional * fix memory corruption * fix memory corruption for realsies now * fix color_get_hsl for out-of-range colors * some more bullet flare tweaks * some lame clear effect workarounds * spawn bullet flares after frame 0; looks better and fixes some problems * New baryon explosion; fix petal_explosion; leanify everything * Add missing bullet flare sprite, rebuild main atlas * improve batching efficiency with bullet spawn flares * forgot git add * Group projectiles/particles by shader where possible * Another take on baryon explosion; make fg framebuffers 16bit * WIP some settings for toasters * remove stupid debug log * microoptimization that probably does nothing anyway * somewhat more intuitive quality settings * Whitelist more particles (MarisaB is on hold) * Whitelist (and fix) some more stage6 particles (mostly ToE) * Add a spell name background * Experimental radial healthbar for bosses * healthbar tweaks * thiccer healthbars in response to feedback * remove healthbar survival timer; just fade out on survivals * Add linear healthbars option; WIP other boss HUD tweaks * Use the proper spell card name format * New font and some random garbage to go along with it * Generate static font outlines for use in text shaders * Use outlines in overlay text shader * Complete boss HUD/healthbar fading logic * fix boss timer limit * stage5 bombs explosion effect * split PFLAG_NOSPAWNZOOM into PFLAG_NOSPAWNFLARE and PFLAG_NOSPAWNFADE; introduce PFLAG_NOSPAWNEFFECTS which disables both (it's just the two values OR'd together) simplify vampiric vapor bullet spawning effect * Remove spawn fade-in from super-fast stage5 fairy projectiles (limiters) * lower particle density in v.vapor in minimal mode * graze effect tweaks * fix text shortening, tweak replay menu layout * stupid debug spam * revisit grazing effects again * dumb debug spam again * improve boss attack timer * overlay effect for boss deaths (similar to the player one) * spice up spellcard declaration (HUD) * don't spawn boss death overlay if fleed * modify Exo2 font to use tabular figures * adjust replay menu for the font change * draw timer & power with standard font (phasing out the numbers font) * WIP new HUD; random fixes/tweaks * hud: move difficulty indicator * hud: move debug stuff around * preloads, mostly * fix youmuA batching conflict * shitty workaround for the shitty screenshake shit * remove extraspell lag by stopping to draw stagebg sooner which is possible because extra spells have a different spellcard_intro timing. Fun fact of the day: the duration of spellcard_intro is always ATTACK_START_DELAY_EXTRA even for normal spells! * new stain particle * i disabled background rendering… * "batch" marisa_b masterspark draws * remove these once a new atlas is generated * make toe quick again * hopefully fix all occurences of changed stain and ScaleFade behavior * tweaking reimu_a and toe boson launch effects * make lhc fast again * softer involnerability effect * fix stage 1 snow on the water bug (and improve performance) translated the time to the future a bit because it only seemed to be an issue for small time values * remove unnecessary spawnflare from toe * tone down extra spell start effect * experimental ReimuB gap shader optimization * fix python3 shebangs * generate simple blur shaders w/ hardcoded kernels * New loading screen * lasers: fix incorrect draw hook registration * add webp support for atlas generator * Use ImageMagick for atlas composition (adds 16-bit support) * Atlas maintenance * make the vampiric vapor bullets less prone to invisibility * Revert a few particles to the quadratic fade curve * experimental baryon effect * improve baryon sprites * disable the baryon effect on minimal postprocessing setting
2019-01-04 23:59:39 +01:00
ent_init();
2017-12-13 20:05:12 +01:00
stage_objpools_alloc();
stage_preload();
stage_draw_init();
tsrand_switch(&global.rand_game);
2017-02-26 13:17:48 +01:00
stage_start(stage);
if(global.replaymode == REPLAY_RECORD) {
uint64_t start_time = (uint64_t)time(0);
uint64_t seed = makeseed();
tsrand_seed_p(&global.rand_game, seed);
global.replay_stage = replay_create_stage(&global.replay, stage, start_time, seed, global.diff, &global.plr);
// make sure our player state is consistent with what goes into the replay
player_init(&global.plr);
replay_stage_sync_player_state(global.replay_stage, &global.plr);
log_debug("Start time: %"PRIu64, start_time);
log_debug("Random seed: 0x%"PRIx64, seed);
StageProgress *p = stage_get_progress_from_info(stage, global.diff, true);
if(p) {
log_debug("You played this stage %u times", p->num_played);
log_debug("You cleared this stage %u times", p->num_cleared);
++p->num_played;
p->unlocked = true;
}
} else {
ReplayStage *stg = global.replay_stage;
assert(stg != NULL);
assert(stage_get(stg->stage) == stage);
tsrand_seed_p(&global.rand_game, stg->rng_seed);
log_debug("REPLAY_PLAY mode: %d events, stage: \"%s\"", stg->numevents, stage->title);
log_debug("Start time: %"PRIu64, stg->start_time);
log_debug("Random seed: 0x%"PRIx64, stg->rng_seed);
2017-02-15 18:34:47 +01:00
global.diff = stg->diff;
player_init(&global.plr);
2017-02-15 18:34:47 +01:00
replay_stage_sync_player_state(stg, &global.plr);
2012-08-07 05:28:41 +02:00
stg->playpos = 0;
}
2017-02-26 13:17:48 +01:00
stage->procs->begin();
2018-10-23 05:49:19 +02:00
player_stage_post_init(&global.plr);
if(global.stage->type != STAGE_SPELL) {
display_stage_title(stage);
}
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
StageFrameState *fstate = calloc(1 , sizeof(*fstate));
fstate->stage = stage;
fstate->cc = next;
eventloop_enter(fstate, stage_logic_frame, stage_render_frame, stage_end_loop, FPS);
}
void stage_end_loop(void* ctx) {
StageFrameState *s = ctx;
if(global.replaymode == REPLAY_RECORD) {
replay_stage_event(global.replay_stage, global.frames, EV_OVER, 0);
global.replay_stage->plr_points_final = global.plr.points;
if(global.gameover == GAMEOVER_WIN) {
global.replay_stage->flags |= REPLAY_SFLAG_CLEAR;
}
}
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
s->stage->procs->end();
stage_draw_shutdown();
2017-02-26 13:17:48 +01:00
stage_free();
2017-11-25 16:51:43 +01:00
player_free(&global.plr);
2012-07-27 19:11:45 +02:00
tsrand_switch(&global.rand_visual);
free_all_refs();
ent_shutdown();
2017-12-13 20:05:12 +01:00
stage_objpools_free();
stop_sounds();
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
taisei_commit_persistent_data();
if(taisei_quit_requested()) {
global.gameover = GAMEOVER_ABORT;
}
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
run_call_chain(&s->cc, NULL);
free(s);
}
2019-03-11 00:21:43 +01:00
void stage_unlock_bgm(const char *bgm) {
if(global.replaymode != REPLAY_PLAY && !global.plr.continues_used) {
progress_unlock_bgm(bgm);
}
}