taisei/src/credits.c

627 lines
15 KiB
C
Raw Normal View History

2012-08-05 03:36:55 +02:00
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
2012-08-05 03:36:55 +02:00
* ---
2024-05-16 23:30:41 +02:00
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
2012-08-05 03:36:55 +02:00
*/
#include "credits.h"
#include "audio/audio.h"
#include "dynarray.h"
#include "eventloop/eventloop.h"
#include "events.h"
2012-08-05 03:36:55 +02:00
#include "global.h"
#include "menu/menu.h"
#include "renderer/api.h"
#include "replay/demoplayer.h"
#include "resource/font.h"
#include "stageutils.h"
#include "transition.h"
2021-05-25 20:39:06 +02:00
#include "util/fbmgr.h"
#include "util/glm.h"
#include "util/graphics.h"
#include "video.h"
2012-08-05 03:36:55 +02:00
typedef struct CreditsEntry {
char **data;
int lines;
int time;
} CreditsEntry;
static struct {
DYNAMIC_ARRAY(CreditsEntry) entries;
2012-08-05 03:36:55 +02:00
float panelalpha;
2012-08-05 23:27:34 +02:00
int end;
bool skipable;
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;
2021-05-25 20:39:06 +02:00
ManagedFramebufferGroup *mfb_group;
Framebuffer *fb;
struct {
PBRModel tower;
} models;
Texture *env_map;
2012-08-05 03:36:55 +02:00
} credits;
#define CREDITS_ENTRY_FADEIN 200.0
#define CREDITS_ENTRY_FADEOUT 100.0
2019-04-24 10:43:31 +02:00
#define CREDITS_FADEOUT 180
// ideally, this should be timed so that:
// (ENTRY_TIME * numEntries) + (HEADER_TIME * numHeaders) ~= 7190
#define ENTRY_TIME 412
#define HEADER_TIME 300
#define YUKKURI_TIME 200
static void credits_add(char *data, int time);
static void credits_fill(void) {
2017-10-23 12:48:30 +02:00
// In case the shortened URLs break,
// Tuck V's YouTube: https://www.youtube.com/channel/UCaw73cuHLnFCSpjOtt_9pyg
2018-01-21 18:35:26 +01:00
// InsideI's bandcamp: https://vnutriya.bandcamp.com/
2017-10-23 12:48:30 +02:00
dynarray_ensure_capacity(&credits.entries, 24);
credits_add("Taisei Project\nbrought to you by…", HEADER_TIME);
credits_add((
"laochailan\n"
"Lukas Weber\n"
"laochailan@web.de\n\n"
"Programming, game design,"
"\ngraphics"
), ENTRY_TIME);
credits_add((
"Akari\n"
"Andrei Alexeyev\n"
2019-07-03 20:00:56 +02:00
"akari@taisei-project.org\n\n"
"Programming, game design"
), ENTRY_TIME);
credits_add((
"Tuck V\n"
"Discord: @Tuck#1679\n"
"YouTube: https://is.gd/exafez\n\n"
"Original soundtrack"
), ENTRY_TIME);
credits_add((
"afensorm\n"
"https://gensokyo.social/@afensorm\n"
"https://pixiv.me/afens\n\n"
"Character art"
), ENTRY_TIME);
credits_add((
2020-04-02 18:40:55 +02:00
"mia\n"
"Mia Herkt\n"
"mia@hong-mailing.de\n\n"
"Hosting, packaging, editing,\n"
"spiritual guidance"
), ENTRY_TIME);
credits_add((
2023-01-14 15:53:29 +01:00
"Alice D.\n"
2023-01-14 21:19:08 +01:00
"https://unstable.systems/@AmyZenunim\n"
2023-01-14 15:53:29 +01:00
"https://github.com/StarWitch\n\n"
"Lead story writer\n"
2023-01-14 15:53:29 +01:00
"macOS QA, debugging, CI"
), ENTRY_TIME);
credits_add((
"Adam\n"
"https://twitter.com/adam_dnh\n\n"
"Dialogue writing (Iku, Elly)"
), ENTRY_TIME);
credits_add((
"Haru\n"
"https://venusers.tumblr.com/\n"
"https://twitter.com/violet_fantasia\n\n"
"Dialogue writing (Hina, Kurumi)"
), ENTRY_TIME);
credits_add((
"makise-homura\n"
"Igor Molchanov\n"
"akemi_homura@kurisa.ch\n\n"
"Code contributions\n"
"Elbrus compatible™"
), ENTRY_TIME);
credits_add((
"aiju\n"
2022-02-06 21:21:04 +01:00
"Emily Schmidt\n"
"http://aiju.de/\n\n"
"I don't remember\n"
2022-02-06 21:21:04 +01:00
"what she did"
), ENTRY_TIME);
credits_add("Special Thanks", HEADER_TIME);
credits_add((
"ZUN\n"
"for Tōhō Project\n"
"http://www16.big.or.jp/~zun/"
), ENTRY_TIME);
2018-11-02 23:26:12 +01:00
credits_add((
"InsideI\n"
"Mikhail Novik\n"
"Bandcamp: https://is.gd/owojix\n\n"
"Various sound effects"
), ENTRY_TIME);
credits_add((
"Free Software\n"
"Basis Universal\n" "https://github.com/BinomialLLC/basis_universal\n\n"
"Blender\n" "https://www.blender.org/\n\n"
"cglm\n" "https://github.com/recp/cglm\n\n"
"FreeType\n" "https://freetype.org/\n\n"
"Inkscape\n" "https://inkscape.org/\n"
"Krita\n" "https://krita.org/\n\n"
2019-09-24 14:58:36 +02:00
"libpng\n" "http://libpng.org/\n\n"
"libwebp\n" "https://git.io/WebP\n\n"
), ENTRY_TIME);
credits_add((
"\n"
2019-09-24 14:58:36 +02:00
"libzip\n" "https://libzip.org/\n\n"
"Meson build system\n" "https://mesonbuild.com/\n\n"
"Ogg Opus\n" "https://www.opus-codec.org/\n\n"
"SPIRV-Cross\n" "https://github.com/KhronosGroup/SPIRV-Cross\n\n"
"shaderc\n" "https://github.com/google/shaderc\n\n"
"Simple DirectMedia Layer\n" "https://libsdl.org/\n\n"
"zlib\n" "https://zlib.net/\n\n"
"Zstandard\n" "https://facebook.github.io/zstd/\n\n"
"and many other projects"
), ENTRY_TIME);
credits_add((
"…and You!\n"
"for playing"
), ENTRY_TIME);
credits_add((
"Visit Us\n"
"https://taisei-project.org/\n\n"
"And join our IRC channel\n"
"#taisei-project at irc.freenode.net\n\n"
"Or our Discord server\n"
"https://discord.gg/JEHCMzW"
), ENTRY_TIME);
// Yukkuri Kyouko!
credits_add("*\nAnd don't forget to take it easy!", YUKKURI_TIME);
2012-08-05 23:27:34 +02:00
}
static void credits_add(char *data, int time) {
2012-08-05 03:36:55 +02:00
CreditsEntry *e;
char *c, buf[256];
int l = 0, i = 0;
assert(time > CREDITS_ENTRY_FADEOUT);
e = dynarray_append(&credits.entries, {
.time = time - CREDITS_ENTRY_FADEOUT,
.lines = 1,
});
2012-08-05 03:36:55 +02:00
for(c = data; *c; ++c)
if(*c == '\n') e->lines++;
e->data = ALLOC_ARRAY(e->lines, typeof(*e->data));
2012-08-05 03:36:55 +02:00
for(c = data; *c; ++c) {
if(*c == '\n') {
buf[i] = 0;
e->data[l] = mem_strdup(buf);
2012-08-05 03:36:55 +02:00
i = 0;
++l;
} else {
buf[i++] = *c;
}
}
2012-08-05 03:36:55 +02:00
buf[i] = 0;
e->data[l] = mem_strdup(buf);
credits.end += time;
2012-08-05 03:36:55 +02:00
}
2021-05-25 20:39:06 +02:00
static void credits_skysphere_draw(vec3 pos) {
r_state_push();
r_disable(RCAP_DEPTH_TEST);
r_cull(CULL_FRONT);
r_shader("stage6_sky");
r_uniform_sampler("skybox", "stage6/sky");
r_mat_mv_push();
2021-05-25 20:39:06 +02:00
r_mat_mv_translate_v(stage_3d_context.cam.pos);
r_mat_mv_scale(50, 50, 50);
r_draw_model("cube");
r_mat_mv_pop();
2021-05-25 20:39:06 +02:00
r_enable(RCAP_DEPTH_TEST);
r_state_pop();
}
2012-08-05 04:39:19 +02:00
2021-05-25 20:39:06 +02:00
static void credits_bg_setup_pbr_lighting(Camera3D *cam) {
PointLight3D lights[] = {
{ {0, 100, 100}, { 1000, 1000, 1000 } },
};
camera3d_set_point_light_uniforms(cam, ARRAY_SIZE(lights), lights);
}
static void credits_bg_setup_pbr_env(Camera3D *cam, PBREnvironment *env) {
credits_bg_setup_pbr_lighting(cam);
glm_vec3_broadcast(1.0f, env->ambient_color);
glm_vec3_broadcast(1.0f, env->environment_color);
2021-05-25 20:39:06 +02:00
camera3d_apply_inverse_transforms(cam, env->cam_inverse_transform);
env->environment_map = credits.env_map;
2012-08-05 03:36:55 +02:00
}
2021-05-25 20:39:06 +02:00
static void credits_towerwall_draw(vec3 pos) {
r_state_push();
r_shader("pbr");
r_enable(RCAP_DEPTH_TEST);
2021-01-31 19:39:22 +01:00
2021-05-25 20:39:06 +02:00
r_mat_mv_push();
r_mat_mv_translate_v(pos);
2021-01-31 19:39:22 +01:00
2021-05-25 20:39:06 +02:00
PBREnvironment env = { 0 };
credits_bg_setup_pbr_env(&stage_3d_context.cam, &env);
pbr_draw_model(&credits.models.tower, &env);
r_shader("envmap_reflect");
r_uniform_sampler("envmap", env.environment_map);
r_uniform_mat4("inv_camera_transform", env.cam_inverse_transform);
r_draw_model("credits/metal_columns");
r_mat_mv_pop();
r_state_pop();
}
2021-01-31 19:39:22 +01:00
2021-05-25 20:39:06 +02:00
static void resize_fb(void *userdata, IntExtent *out_dimensions, FloatRect *out_viewport) {
float w, h;
video_get_viewport_size(&w, &h);
out_dimensions->w = w;
out_dimensions->h = h;
out_viewport->w = w;
out_viewport->h = h;
out_viewport->x = 0;
out_viewport->y = 0;
2021-01-31 19:39:22 +01:00
}
static void credits_init(void) {
2012-08-05 03:36:55 +02:00
memset(&credits, 0, sizeof(credits));
2021-05-25 20:39:06 +02:00
stage3d_init(&stage_3d_context, 32);
FBAttachmentConfig a[2];
memset(a, 0, sizeof(a));
for(int i = 0; i < ARRAY_SIZE(a); i++) {
a[i].tex_params.filter.min = TEX_FILTER_LINEAR;
a[i].tex_params.filter.mag = TEX_FILTER_LINEAR;
a[i].tex_params.wrap.s = TEX_WRAP_MIRROR;
a[i].tex_params.wrap.s = TEX_WRAP_MIRROR;
}
a[0].attachment = FRAMEBUFFER_ATTACH_COLOR0;
a[0].tex_params.type = TEX_TYPE_RGBA_8;
a[1].attachment = FRAMEBUFFER_ATTACH_DEPTH;
a[1].tex_params.type = TEX_TYPE_DEPTH;
2021-05-25 20:39:06 +02:00
FramebufferConfig fbconf = { 0 };
fbconf.attachments = a;
fbconf.num_attachments = ARRAY_SIZE(a);
fbconf.resize_strategy.resize_func = resize_fb;
2021-05-25 20:39:06 +02:00
credits.mfb_group = fbmgr_group_create();
credits.fb = fbmgr_group_framebuffer_create(credits.mfb_group, "BG", &fbconf);
stage_3d_context.cam.far = 1000;
2021-02-22 14:22:53 +01:00
stage_3d_context.cam.pos[0] = 0;
2021-05-25 20:39:06 +02:00
stage_3d_context.cam.pos[1] = -19;
stage_3d_context.cam.vel[2] = -0.5;
stage_3d_context.cam.rot.v[0] = 30;
stage_3d_context.cam.rot.v[2] = -20;
2012-08-05 03:36:55 +02:00
global.frames = 0;
2021-05-25 20:39:06 +02:00
credits.env_map = res_texture("stage6/sky");
pbr_load_model(&credits.models.tower, "credits/tower", "credits/tower");
2012-08-05 03:36:55 +02:00
credits_fill();
credits.end += 200 + CREDITS_ENTRY_FADEOUT;
// Should be >1, because if we get here, that means we have achieved an
// ending just now, which this counter includes.
// That is unless we're in `taisei --credits`. But in that case, skipping is
// presumably not desired anyway.
credits.skipable = progress_times_any_ending_achieved() > 1;
2023-06-08 01:51:01 +02:00
progress_unlock_bgm("credits");
audio_bgm_play(res_bgm("credits"), false, 0, 0);
2012-08-05 03:36:55 +02:00
}
static double entry_height(CreditsEntry *e, double *head, double *body) {
double total = *head = *body = 0;
if(!e->lines) {
return total;
}
if(e->lines > 0) {
if(*(e->data[0]) == '*') {
total += *head = res_sprite("kyoukkuri")->h;
} else {
total += *head = font_get_lineskip(res_font("big"));
}
if(e->lines > 1) {
total += *body += (e->lines - 0.5) * font_get_lineskip(res_font("standard"));
}
}
return total;
}
static float yukkuri_jump(float t) {
float k = 0.2;
float l = 1 - k;
if(t > 1 || t < 0) {
return 0;
}
if(t > k) {
t = (t - k) / l;
float b = glm_ease_bounce_out(t);
float e = glm_ease_sine_out(b);
return 1 - lerp(b, e, glm_ease_sine_out(t));
}
return glm_ease_quad_out(t / k);
}
static void credits_draw_entry(CreditsEntry *e) {
int time = global.frames - 200;
float fadein = 1, fadeout = 1;
for(CreditsEntry *o = credits.entries.data; o != e; ++o) {
time -= o->time + CREDITS_ENTRY_FADEOUT;
}
double h_total, h_head, h_body;
h_total = entry_height(e, &h_head, &h_body);
// random asspull approximation to make stuff not overlap too much
int ofs = (1 - pow(1 - h_total / SCREEN_H, 2)) * SCREEN_H * 0.095;
time -= ofs;
if(time < 0) {
2012-08-05 03:36:55 +02:00
return;
}
ofs *= 2;
if(time <= CREDITS_ENTRY_FADEIN) {
2012-08-05 03:36:55 +02:00
fadein = time / CREDITS_ENTRY_FADEIN;
}
if(time - e->time - CREDITS_ENTRY_FADEIN + ofs > 0) {
fadeout = max(0, 1 - (time - e->time - CREDITS_ENTRY_FADEIN + ofs) / CREDITS_ENTRY_FADEOUT);
}
if(!fadein || !fadeout) {
2012-08-05 03:36:55 +02:00
return;
}
Sprite *yukkuri_spr = NULL;
if(*e->data[0] == '*') {
yukkuri_spr = res_sprite("kyoukkuri");
}
r_state_push();
r_mat_mv_push();
if(fadein < 1) {
r_mat_mv_translate(0, SCREEN_W * pow(1 - fadein, 2) * 0.5, 0);
} else if(fadeout < 1) {
r_mat_mv_translate(0, SCREEN_W * pow(1 - fadeout, 2) * -0.5, 0);
}
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
r_color(RGBA_MUL_ALPHA(1, 1, 1, fadein * fadeout));
r_mat_mv_translate(0, h_body * -0.5, 0);
for(int i = 0; i < e->lines; ++i) {
if(yukkuri_spr && !i) {
float t = ((global.frames) % 90) / 59.0;
float elevation = yukkuri_jump(t);
float squeeze = (elevation - yukkuri_jump(t - 0.03)) * 0.4;
float halfheight = yukkuri_spr->h * 0.5;
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = yukkuri_spr,
.pos.y = -60 * elevation * fadein + halfheight * squeeze,
.shader_ptr = res_shader("sprite_default"),
.scale.x = 1.0 - squeeze,
.scale.y = 1.0 + squeeze,
});
r_mat_mv_translate(0, halfheight, 0);
} else {
Font *font = res_font(i ? "standard" : "big");
r_shader("text_default");
text_draw(e->data[i], &(TextParams) {
.align = ALIGN_CENTER,
.font_ptr = font,
});
r_shader_standard();
r_mat_mv_translate(0, font_get_lineskip(font), 0);
}
}
r_mat_mv_pop();
r_state_pop();
2012-08-05 03:36:55 +02:00
}
2021-05-25 20:39:06 +02:00
static uint credits_towerwall_pos(Stage3D *s3d, vec3 cam, float maxrange) {
vec3 orig = {0, 0, 0};
vec3 step = {0, 0, -25};
return stage3d_pos_ray_nearfirst(s3d, cam, orig, step, maxrange, 0);
}
static uint credits_skysphere_pos(Stage3D *s3d, vec3 cam, float maxrange) {
return stage3d_pos_single(s3d, cam, cam, maxrange);
}
static void credits_draw(void) {
r_clear(BUFFER_ALL, RGBA(0, 0, 0, 1), 1);
2021-05-25 20:39:06 +02:00
// colorfill(1, 1, 1, 1); // don't use r_clear for this, it screws up letterboxing
r_mat_mv_push();
2021-05-25 20:39:06 +02:00
r_state_push();
r_framebuffer(credits.fb);
r_clear(BUFFER_ALL, RGBA(0, 0, 0, 1), 1);
2020-02-22 07:56:41 +01:00
2021-05-25 20:39:06 +02:00
r_enable(RCAP_DEPTH_TEST);
stage3d_draw(&stage_3d_context, 500, 2, (Stage3DSegment[]) { credits_skysphere_draw, credits_skysphere_pos, credits_towerwall_draw, credits_towerwall_pos });
r_state_pop();
draw_framebuffer_tex(credits.fb, SCREEN_W, SCREEN_H);
r_mat_mv_pop();
set_ortho(SCREEN_W, SCREEN_H);
float pane_pos = SCREEN_W/4*3 - 6;
r_mat_mv_push();
r_color4(0, 0, 0, credits.panelalpha * 0.7);
r_mat_mv_translate(pane_pos, SCREEN_H/2, 0);
r_mat_mv_scale(385, SCREEN_H, 1);
2018-11-02 23:27:51 +01:00
r_shader_standard_notex();
r_draw_quad();
r_color4(1, 1, 1, 1);
r_mat_mv_pop();
r_mat_mv_push();
r_mat_mv_translate(pane_pos, SCREEN_H/2, 0);
2018-11-02 23:27:51 +01:00
r_shader_standard();
dynarray_foreach_elem(&credits.entries, CreditsEntry *e, {
credits_draw_entry(e);
});
r_mat_mv_pop();
draw_transition();
2012-08-05 03:36:55 +02:00
}
static void credits_finish(CallChainResult ccr) {
2017-02-25 14:23:22 +01:00
credits.end = 0;
set_transition(TransLoader, 0, FADE_TIME*10, NO_CALLCHAIN);
2017-02-25 14:23:22 +01:00
}
static void credits_process(void) {
2021-05-25 20:39:06 +02:00
stage3d_update(&stage_3d_context);
//stage_3d_context.cam.pos[2] = 10-0.1*global.frames;
//stage_3d_context.cam.pos[1] = 500 + 100 * psin(global.frames / 100.0) * psin(global.frames / 200.0 + M_PI);
//stage_3d_context.cam.pos[0] = 25 * sin(global.frames / 75.7) * cos(global.frames / 99.3);
2023-02-24 02:23:06 +01:00
if(global.frames >= 100 && global.frames <= 200) {
2012-08-05 03:36:55 +02:00
credits.panelalpha += 0.01;
2023-02-24 02:23:06 +01:00
}
2012-08-05 23:27:34 +02:00
if(global.frames >= credits.end - CREDITS_ENTRY_FADEOUT) {
credits.panelalpha -= 1 / 120.0;
}
2012-08-14 17:56:53 +02:00
if(global.frames == credits.end) {
set_transition(TransFadeWhite, CREDITS_FADEOUT, CREDITS_FADEOUT, CALLCHAIN(credits_finish, NULL));
2012-08-05 23:27:34 +02:00
}
2012-08-05 03:36:55 +02:00
}
static void credits_free(void) {
2021-05-25 20:39:06 +02:00
fbmgr_group_destroy(credits.mfb_group);
dynarray_foreach_elem(&credits.entries, CreditsEntry *e, {
for(int i = 0; i < e->lines; ++i) {
mem_free(e->data[i]);
}
mem_free(e->data);
});
dynarray_free_data(&credits.entries);
stage3d_shutdown(&stage_3d_context);
2012-08-05 03:36:55 +02:00
}
void credits_preload(ResourceGroup *rg) {
res_group_preload(rg, RES_BGM, RESF_OPTIONAL, "credits", NULL);
res_group_preload(rg, RES_SHADER_PROGRAM, RESF_DEFAULT,
2021-05-25 20:39:06 +02:00
"pbr",
"envmap_reflect",
"stage6_sky",
NULL);
res_group_preload(rg, RES_SPRITE, RESF_DEFAULT, "kyoukkuri", NULL);
res_group_preload(rg, RES_TEXTURE, RESF_DEFAULT,
2019-04-22 23:09:58 +02:00
"loading", // for transition
2021-05-25 20:39:06 +02:00
"stage6/sky",
NULL);
res_group_preload(rg, RES_MATERIAL, RESF_DEFAULT,
2021-05-25 20:39:06 +02:00
"credits/tower",
NULL);
res_group_preload(rg, RES_MODEL, RESF_DEFAULT,
2021-05-25 20:39:06 +02:00
"credits/metal_columns",
"credits/tower",
"cube",
NULL);
}
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 credits_logic_frame(void *arg) {
update_transition();
events_poll(NULL, 0);
credits_process();
global.frames++;
if(credits.end == 0) {
return LFRAME_STOP;
} else if(credits.skipable && gamekeypressed(KEY_SKIP)) {
return LFRAME_SKIP;
} else {
return LFRAME_WAIT;
}
2017-12-26 12:07:40 +01:00
}
Emscripten compatibility (#161) * Major refactoring of the main loop(s) and control flow (WIP) run_at_fps() is gone 🦀 Instead of nested blocking event loops, there is now an eventloop API that manages an explicit stack of scenes. This makes Taisei a lot more portable to async environments where spinning a loop forever without yielding control simply is not an option, and that is the entire point of this change. A prime example of such an environment is the Web (via emscripten). Taisei was able to run there through a terrible hack: inserting emscripten_sleep calls into the loop, which would yield to the browser. This has several major drawbacks: first of all, every function that could possibly call emscripten_sleep must be compiled into a special kind of bytecode, which then has to be interpreted at runtime, *much* slower than JITed WebAssembly. And that includes *everything* down the call stack, too! For more information, see https://emscripten.org/docs/porting/emterpreter.html Even though that method worked well enough for experimenting, despite suboptimal performance, there is another obvious drawback: emscripten_sleep is implemented via setTimeout(), which can be very imprecise and is generally not reliable for fluid animation. Browsers actually have an API specifically for that use case: window.requestAnimationFrame(), but Taisei's original blocking control flow style is simply not compatible with it. Emscripten exposes this API with its emscripten_set_main_loop(), which the eventloop backend now uses on that platform. Unfortunately, C is still C, with no fancy closures or coroutines. With blocking calls into menu/scene loops gone, the control flow is reimplemented via so-called (pun intended) "call chains". That is basically an euphemism for callback hell. With manual memory management and zero type-safety. Not that the menu system wasn't shitty enough already. I'll just keep telling myself that this is all temporary and will be replaced with scripts in v1.4. * improve build system for emscripten + various fixes * squish menu bugs * improve emscripten event loop; disable EMULATE_FUNCTION_POINTER_CASTS Note that stock freetype does not work without EMULATE_FUNCTION_POINTER_CASTS; use a patched version from the "emscripten" branch here: https://github.com/taisei-project/freetype2/tree/emscripten * Enable -Wcast-function-type Calling functions through incompatible pointers is nasal demons and doesn't work in WASM. * webgl: workaround a crash on some browsers * emscripten improvements: * Persist state (config, progress, replays, ...) in local IndexDB * Simpler HTML shell (temporary) * Enable more optimizations * fix build if validate_glsl=false * emscripten: improve asset packaging, with local cache Note that even though there are rules to build audio bundles, audio does *not* work yet. It looks like SDL2_mixer can not work without threads, which is a problem. Yet another reason to write an OpenAL backend - emscripten supports that natively. * emscripten: customize the html shell * emscripten: force "show log" checkbox unchecked initially * emscripten: remove quit shortcut from main menu (since there's no quit) * emscripten: log area fixes * emscripten/webgl: workaround for fullscreen viewport issue * emscripten: implement frameskip * emscripter: improve framerate limiter * align List to at least 8 bytes (shut up warnings) * fix non-emscripten builds * improve fullscreen handling, mainly for emscripten * Workaround to make audio work in chromium emscripten-core/emscripten#6511 * emscripten: better vsync handling; enable vsync & disable fxaa by default
2019-03-09 20:32:32 +01:00
static RenderFrameAction credits_render_frame(void *arg) {
2017-12-26 12:07:40 +01:00
credits_draw();
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 credits_end_loop(void *ctx) {
credits_free();
2023-06-08 01:50:28 +02:00
demoplayer_resume();
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(&credits.cc, NULL);
}
void credits_enter(CallChain next) {
2012-08-05 03:36:55 +02:00
credits_init();
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
credits.cc = next;
2023-06-08 01:50:28 +02:00
demoplayer_suspend();
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
eventloop_enter(&credits, credits_logic_frame, credits_render_frame, credits_end_loop, FPS);
2012-08-05 03:36:55 +02:00
}