replay/demoplayer: automatic "demo" playback when idling in menus
Currently the game comes with no demos; place your own replays into $userdir/resources/demos if you want to test this.
This commit is contained in:
parent
71109fd253
commit
57a08d4c7a
15 changed files with 368 additions and 12 deletions
|
@ -90,6 +90,8 @@ glsl_files = files(
|
|||
'text_cutscene.frag.glsl',
|
||||
'text_default.frag.glsl',
|
||||
'text_default.vert.glsl',
|
||||
'text_demo.frag.glsl',
|
||||
'text_demo.vert.glsl',
|
||||
'text_dialog.frag.glsl',
|
||||
'text_dialog.vert.glsl',
|
||||
'text_example.frag.glsl',
|
||||
|
|
36
resources/00-taisei.pkgdir/shader/text_demo.frag.glsl
Normal file
36
resources/00-taisei.pkgdir/shader/text_demo.frag.glsl
Normal file
|
@ -0,0 +1,36 @@
|
|||
#version 330 core
|
||||
|
||||
#include "lib/render_context.glslh"
|
||||
#include "lib/sprite_main.frag.glslh"
|
||||
#include "lib/util.glslh"
|
||||
|
||||
vec3 colormap(float p) {
|
||||
vec3 c;
|
||||
c.r = smoothstep(0.30, 0.7, p) * smoothstep(0.20, 0.30, 1.0 - p);
|
||||
c.g = smoothstep(0.35, 0.7, p) * smoothstep(0.23, 0.32, 1.0 - p);
|
||||
c.b = smoothstep(0.35, 0.7, p) * smoothstep(0.24, 0.31, 1.0 - p);
|
||||
return c;
|
||||
}
|
||||
|
||||
void spriteMain(out vec4 fragColor) {
|
||||
float t = customParams.r;
|
||||
vec2 tco = flip_native_to_bottomleft(texCoordOverlay);
|
||||
float base_gradient = smoothstep(-0.5, 0.8, tco.y);
|
||||
|
||||
vec4 clr = vec4(
|
||||
pow(vec3(base_gradient, base_gradient, base_gradient),
|
||||
vec3(1.3, 1.2, 1.1) - 0.5
|
||||
), 1);
|
||||
|
||||
float go = tco.y;
|
||||
go += tco.x * mix(-1, 1, 1 - tco.y);
|
||||
|
||||
vec4 g = vec4(colormap(fract(-0.5 * t + go)), 0);
|
||||
clr = alphaCompose(clr, g);
|
||||
|
||||
vec3 outlines = texture(tex, texCoord).rgb;
|
||||
vec4 border = vec4(vec3(g.rgb), 0.5) * outlines.g;
|
||||
vec4 fill = clr * outlines.r;
|
||||
|
||||
fragColor = alphaCompose(border, fill);
|
||||
}
|
2
resources/00-taisei.pkgdir/shader/text_demo.prog
Normal file
2
resources/00-taisei.pkgdir/shader/text_demo.prog
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
objects = text_demo.vert text_demo.frag
|
8
resources/00-taisei.pkgdir/shader/text_demo.vert.glsl
Normal file
8
resources/00-taisei.pkgdir/shader/text_demo.vert.glsl
Normal file
|
@ -0,0 +1,8 @@
|
|||
#version 330 core
|
||||
|
||||
#define SPRITE_OUT_COLOR
|
||||
#define SPRITE_OUT_TEXCOORD
|
||||
#define SPRITE_OUT_TEXCOORD_OVERLAY
|
||||
#define SPRITE_OUT_CUSTOM
|
||||
|
||||
#include "lib/sprite_default.vert.glslh"
|
|
@ -33,6 +33,7 @@
|
|||
#include "filewatch/filewatch.h"
|
||||
#include "dynstage.h"
|
||||
#include "eventloop/eventloop.h"
|
||||
#include "replay/demoplayer.h"
|
||||
|
||||
attr_unused
|
||||
static void taisei_shutdown(void) {
|
||||
|
@ -43,8 +44,8 @@ static void taisei_shutdown(void) {
|
|||
progress_save();
|
||||
}
|
||||
|
||||
demoplayer_shutdown();
|
||||
progress_unload();
|
||||
|
||||
stage_objpools_shutdown();
|
||||
gamemode_shutdown();
|
||||
shutdown_resources();
|
||||
|
@ -423,12 +424,13 @@ static void main_post_vfsinit(CallChainResult ccr) {
|
|||
return;
|
||||
}
|
||||
|
||||
enter_menu(create_main_menu(), CALLCHAIN(main_cleanup, ctx));
|
||||
run_call_chain(&CALLCHAIN(main_mainmenu, ctx), NULL);
|
||||
eventloop_run();
|
||||
}
|
||||
|
||||
static void main_mainmenu(CallChainResult ccr) {
|
||||
MainContext *ctx = ccr.ctx;
|
||||
demoplayer_init();
|
||||
enter_menu(create_main_menu(), CALLCHAIN(main_cleanup, ctx));
|
||||
}
|
||||
|
||||
|
@ -514,7 +516,7 @@ static void main_replay(MainContext *mctx) {
|
|||
stralloc(&mctx->replay_out->playername, mctx->replay_in->playername);
|
||||
}
|
||||
|
||||
replay_play(mctx->replay_in, mctx->replay_idx, CALLCHAIN(main_cleanup, mctx));
|
||||
replay_play(mctx->replay_in, mctx->replay_idx, false, CALLCHAIN(main_cleanup, mctx));
|
||||
eventloop_run();
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,10 @@ static void action_play_bgm(MenuData *m, void *arg) {
|
|||
|
||||
static void add_bgm(MenuData *m, const char *bgm_name, bool preload) {
|
||||
if(preload) {
|
||||
preload_resource(RES_BGM, bgm_name, RESF_OPTIONAL);
|
||||
// FIXME HACK: make this just RESF_OPTIONAL once we have proper refcounting for resources!
|
||||
// Currently without RESF_PERMANENT we segfault after returning from demo playback,
|
||||
// because transient resources get unloaded.
|
||||
preload_resource(RES_BGM, bgm_name, RESF_PERMANENT | RESF_OPTIONAL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
193
src/replay/demoplayer.c
Normal file
193
src/replay/demoplayer.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "demoplayer.h"
|
||||
#include "replay.h"
|
||||
#include "replay/struct.h"
|
||||
#include "global.h"
|
||||
#include "vfs/public.h"
|
||||
#include "events.h"
|
||||
|
||||
#define DEMOPLAYER_DIR_PATH "res/demos"
|
||||
#define DEMOPLAYER_WAIT_TIME (60 * FPS)
|
||||
|
||||
struct {
|
||||
uint time;
|
||||
uint wait_time;
|
||||
uint next_demo_index;
|
||||
char **demo_files;
|
||||
size_t num_demo_files;
|
||||
int suspend_level;
|
||||
} dplr;
|
||||
|
||||
static bool demo_path_filter(const char *path) {
|
||||
return strendswith(path, "." REPLAY_EXTENSION);
|
||||
}
|
||||
|
||||
static bool demoplayer_check_demos(void) {
|
||||
if(!dplr.demo_files || dplr.num_demo_files == 0) {
|
||||
log_warn("No demos found");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void demoplayer_init(void) {
|
||||
dplr.demo_files = vfs_dir_list_sorted(
|
||||
DEMOPLAYER_DIR_PATH, &dplr.num_demo_files, vfs_dir_list_order_ascending, demo_path_filter
|
||||
);
|
||||
|
||||
if(demoplayer_check_demos()) {
|
||||
log_info("Found %zu demo files", dplr.num_demo_files);
|
||||
}
|
||||
|
||||
dplr.wait_time = env_get("TAISEI_DEMO_TIME", DEMOPLAYER_WAIT_TIME);
|
||||
|
||||
if(!dplr.wait_time) {
|
||||
dplr.wait_time = UINT32_MAX;
|
||||
}
|
||||
|
||||
dplr.suspend_level = 1;
|
||||
demoplayer_resume();
|
||||
}
|
||||
|
||||
void demoplayer_shutdown(void) {
|
||||
if(dplr.suspend_level == 0) {
|
||||
demoplayer_suspend();
|
||||
}
|
||||
|
||||
vfs_dir_list_free(dplr.demo_files, dplr.num_demo_files);
|
||||
dplr.demo_files = NULL;
|
||||
dplr.num_demo_files = 0;
|
||||
}
|
||||
|
||||
typedef struct DemoPlayerContext {
|
||||
Replay rpy;
|
||||
} DemoPlayerContext;
|
||||
|
||||
static void demoplayer_start_demo_posttransition(CallChainResult ccr);
|
||||
static void demoplayer_end_demo(CallChainResult ccr);
|
||||
|
||||
static void demoplayer_start_demo(void) {
|
||||
auto ctx = ALLOC(DemoPlayerContext);
|
||||
CallChain cc = CALLCHAIN(demoplayer_end_demo, ctx);
|
||||
|
||||
demoplayer_suspend();
|
||||
|
||||
if(!demoplayer_check_demos()) {
|
||||
run_call_chain(&cc, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
uint demoidx = dplr.next_demo_index;
|
||||
dplr.next_demo_index = (demoidx + 1) % dplr.num_demo_files;
|
||||
|
||||
char *demo_filename = dplr.demo_files[demoidx];
|
||||
char demo_path[sizeof(DEMOPLAYER_DIR_PATH) + 1 + strlen(demo_filename)];
|
||||
snprintf(demo_path, sizeof(demo_path), DEMOPLAYER_DIR_PATH "/%s", demo_filename);
|
||||
|
||||
log_debug("Staring demo %s", demo_path);
|
||||
|
||||
if(!replay_load_vfspath(&ctx->rpy, demo_path, REPLAY_READ_ALL)) {
|
||||
log_error("Failed to load replay %s", demo_path);
|
||||
run_call_chain(&cc, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
set_transition(TransFadeWhite, FADE_TIME * 3, FADE_TIME * 2,
|
||||
CALLCHAIN(demoplayer_start_demo_posttransition, ctx));
|
||||
}
|
||||
|
||||
static void demoplayer_start_demo_posttransition(CallChainResult ccr) {
|
||||
DemoPlayerContext *ctx = ccr.ctx;
|
||||
CallChain end = CALLCHAIN(demoplayer_end_demo, ctx);
|
||||
|
||||
if(TRANSITION_RESULT_CANCELED(ccr)) {
|
||||
run_call_chain(&end, NULL);
|
||||
} else {
|
||||
replay_play(&ctx->rpy, 0, true, end);
|
||||
}
|
||||
}
|
||||
|
||||
static void demoplayer_end_demo(CallChainResult ccr) {
|
||||
DemoPlayerContext *ctx = ccr.ctx;
|
||||
replay_reset(&ctx->rpy);
|
||||
mem_free(ctx);
|
||||
demoplayer_resume();
|
||||
}
|
||||
|
||||
static bool demoplayer_frame_event(SDL_Event *evt, void *arg) {
|
||||
if(dplr.time == dplr.wait_time) {
|
||||
demoplayer_start_demo();
|
||||
dplr.time = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(dplr.time < dplr.wait_time);
|
||||
++dplr.time;
|
||||
|
||||
// log_debug("%u", dplr.time);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool demoplayer_activity_event(SDL_Event *evt, void *arg) {
|
||||
switch(evt->type) {
|
||||
case SDL_KEYDOWN:
|
||||
goto reset;
|
||||
|
||||
default: switch(TAISEI_EVENT(evt->type)) {
|
||||
case TE_GAMEPAD_BUTTON_DOWN:
|
||||
case TE_GAMEPAD_AXIS:
|
||||
goto reset;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
reset:
|
||||
// log_debug("Reset timer (was %u)", dplr.time);
|
||||
dplr.time = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void demoplayer_suspend(void) {
|
||||
dplr.suspend_level++;
|
||||
assert(dplr.suspend_level > 0);
|
||||
log_debug("Suspend level is now %i", dplr.suspend_level);
|
||||
|
||||
if(dplr.suspend_level > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Removing event handlers");
|
||||
|
||||
events_unregister_handler(demoplayer_frame_event);
|
||||
events_unregister_handler(demoplayer_activity_event);
|
||||
}
|
||||
|
||||
void demoplayer_resume(void) {
|
||||
dplr.suspend_level--;
|
||||
assert(dplr.suspend_level >= 0);
|
||||
log_debug("Suspend level is now %i", dplr.suspend_level);
|
||||
|
||||
if(dplr.suspend_level > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("Installing event handlers");
|
||||
|
||||
events_register_handler(&(EventHandler) {
|
||||
demoplayer_frame_event, NULL, EPRIO_LAST, MAKE_TAISEI_EVENT(TE_FRAME),
|
||||
});
|
||||
events_register_handler(&(EventHandler) {
|
||||
demoplayer_activity_event, NULL, EPRIO_SYSTEM,
|
||||
});
|
||||
}
|
15
src/replay/demoplayer.h
Normal file
15
src/replay/demoplayer.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "taisei.h"
|
||||
|
||||
void demoplayer_init(void);
|
||||
void demoplayer_shutdown(void);
|
||||
void demoplayer_suspend(void);
|
||||
void demoplayer_resume(void);
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
replay_src = files(
|
||||
'demoplayer.c',
|
||||
'play.c',
|
||||
'read.c',
|
||||
'replay.c',
|
||||
|
|
|
@ -20,13 +20,14 @@ typedef struct ReplayContext {
|
|||
CallChain cc;
|
||||
Replay *rpy;
|
||||
int stage_idx;
|
||||
bool demo_mode;
|
||||
} ReplayContext;
|
||||
|
||||
static void replay_do_cleanup(CallChainResult ccr);
|
||||
static void replay_do_play(CallChainResult ccr);
|
||||
static void replay_do_post_play(CallChainResult ccr);
|
||||
|
||||
void replay_play(Replay *rpy, int firstidx, CallChain next) {
|
||||
void replay_play(Replay *rpy, int firstidx, bool demo_mode, CallChain next) {
|
||||
if(firstidx >= rpy->stages.num_elements || firstidx < 0) {
|
||||
log_error("No stage #%i in the replay", firstidx);
|
||||
run_call_chain(&next, NULL);
|
||||
|
@ -37,6 +38,7 @@ void replay_play(Replay *rpy, int firstidx, CallChain next) {
|
|||
.cc = next,
|
||||
.rpy = rpy,
|
||||
.stage_idx = firstidx,
|
||||
.demo_mode = demo_mode,
|
||||
}), NULL));
|
||||
}
|
||||
|
||||
|
@ -63,6 +65,7 @@ static void replay_do_play(CallChainResult ccr) {
|
|||
} else {
|
||||
assume(rstg != NULL);
|
||||
replay_state_init_play(&global.replay.input, rpy, rstg);
|
||||
global.replay.input.play.demo_mode = ctx->demo_mode;
|
||||
replay_state_deinit(&global.replay.output);
|
||||
global.plr.mode = plrmode_find(rstg->plr_char, rstg->plr_shot);
|
||||
stage_enter(stginfo, CALLCHAIN(replay_do_post_play, ctx));
|
||||
|
|
|
@ -44,4 +44,4 @@ bool replay_load_vfspath(Replay *rpy, const char *path, ReplayReadMode mode) att
|
|||
|
||||
int replay_find_stage_idx(Replay *rpy, uint8_t stageid) attr_nonnull_all;
|
||||
|
||||
void replay_play(Replay *rpy, int firstidx, CallChain next) attr_nonnull_all;
|
||||
void replay_play(Replay *rpy, int firstidx, bool demo_mode, CallChain next) attr_nonnull_all;
|
||||
|
|
|
@ -29,6 +29,7 @@ typedef struct ReplayState {
|
|||
uint16_t desync_check;
|
||||
int desync_check_frame;
|
||||
int desync_frame;
|
||||
bool demo_mode;
|
||||
} play;
|
||||
|
||||
struct {
|
||||
|
|
44
src/stage.c
44
src/stage.c
|
@ -13,8 +13,9 @@
|
|||
#include "global.h"
|
||||
#include "video.h"
|
||||
#include "resource/bgm.h"
|
||||
#include "replay/state.h"
|
||||
#include "replay/demoplayer.h"
|
||||
#include "replay/stage.h"
|
||||
#include "replay/state.h"
|
||||
#include "replay/struct.h"
|
||||
#include "config.h"
|
||||
#include "player.h"
|
||||
|
@ -55,6 +56,10 @@ static inline bool is_quickloading(StageFrameState *fstate) {
|
|||
return fstate->quicksave && fstate->quicksave == global.replay.input.replay;
|
||||
}
|
||||
|
||||
bool stage_is_demo_mode(void) {
|
||||
return global.replay.input.replay && global.replay.input.play.demo_mode;
|
||||
}
|
||||
|
||||
static void sync_bgm(StageFrameState *fstate) {
|
||||
double t = fstate->bgm_start_pos + (global.frames - fstate->bgm_start_time) / (double)FPS;
|
||||
audio_bgm_seek_realtime(t);
|
||||
|
@ -445,6 +450,21 @@ static bool stage_input_handler_replay(SDL_Event *event, void *arg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool stage_input_handler_demo(SDL_Event *event, void *arg) {
|
||||
if(event->type == SDL_KEYDOWN && !event->key.repeat) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch(TAISEI_EVENT(event->type)) {
|
||||
case TE_GAME_KEY_DOWN:
|
||||
case TE_GAMEPAD_BUTTON_DOWN:
|
||||
exit:
|
||||
stage_finish(GAMEOVER_ABORT);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct replay_event_arg {
|
||||
ReplayState *st;
|
||||
ReplayEvent *resume_event;
|
||||
|
@ -488,7 +508,11 @@ static void leave_replay_mode(StageFrameState *fstate, ReplayState *rp_in) {
|
|||
static void replay_input(StageFrameState *fstate) {
|
||||
if(!is_quickloading(fstate)) {
|
||||
events_poll((EventHandler[]){
|
||||
{ .proc = stage_input_handler_replay },
|
||||
{ .proc =
|
||||
stage_is_demo_mode()
|
||||
? stage_input_handler_demo
|
||||
: stage_input_handler_replay
|
||||
},
|
||||
{ NULL }
|
||||
}, EFLAG_GAME);
|
||||
}
|
||||
|
@ -746,7 +770,10 @@ void stage_finish(int gameover) {
|
|||
global.gameover = GAMEOVER_TRANSITIONING;
|
||||
CallChain cc = CALLCHAIN(stage_finalize, (void*)(intptr_t)gameover);
|
||||
set_transition(TransFadeBlack, FADE_TIME, FADE_TIME*2, cc);
|
||||
audio_bgm_stop(BGM_FADE_LONG);
|
||||
|
||||
if(!stage_is_demo_mode()) {
|
||||
audio_bgm_stop(BGM_FADE_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
|
@ -786,6 +813,10 @@ static void stage_preload(void) {
|
|||
}
|
||||
|
||||
static void display_stage_title(StageInfo *info) {
|
||||
if(stage_is_demo_mode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stagetext_add(info->title, VIEWPORT_W/2 + I * (VIEWPORT_H/2-40), ALIGN_CENTER, res_font("big"), RGB(1, 1, 1), 50, 85, 35, 35);
|
||||
stagetext_add(info->subtitle, VIEWPORT_W/2 + I * (VIEWPORT_H/2), ALIGN_CENTER, res_font("standard"), RGB(1, 1, 1), 60, 85, 35, 35);
|
||||
}
|
||||
|
@ -795,6 +826,10 @@ TASK(start_bgm, { BGM *bgm; }) {
|
|||
}
|
||||
|
||||
void stage_start_bgm(const char *bgm) {
|
||||
if(stage_is_demo_mode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
INVOKE_TASK_DELAYED(1, start_bgm, res_bgm(bgm));
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1162,8 @@ static void _stage_enter(
|
|||
|
||||
if(is_quickloading(fstate)) {
|
||||
audio_sfx_set_enabled(false);
|
||||
} else {
|
||||
demoplayer_suspend();
|
||||
}
|
||||
|
||||
SCHED_INVOKE_TASK(&fstate->sched, stage_comain, fstate);
|
||||
|
@ -1195,6 +1232,7 @@ void stage_end_loop(void *ctx) {
|
|||
if(is_quickload) {
|
||||
_stage_enter(stginfo, cc, quicksave, quicksave_is_automatic);
|
||||
} else {
|
||||
demoplayer_resume();
|
||||
run_call_chain(&cc, NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ void stage_load_quicksave(void);
|
|||
|
||||
CoSched *stage_get_sched(void);
|
||||
|
||||
bool stage_is_demo_mode(void);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define HAVE_SKIP_MODE
|
||||
#endif
|
||||
|
|
|
@ -314,6 +314,12 @@ void stage_draw_pre_init(void) {
|
|||
"monosmall",
|
||||
NULL);
|
||||
|
||||
if(stage_is_demo_mode()) {
|
||||
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
|
||||
"text_demo",
|
||||
NULL);
|
||||
}
|
||||
|
||||
stagedraw.framerate_graphs = env_get("TAISEI_FRAMERATE_GRAPHS", GRAPHS_DEFAULT);
|
||||
stagedraw.objpool_stats = env_get("TAISEI_OBJPOOL_STATS", OBJPOOLSTATS_DEFAULT);
|
||||
|
||||
|
@ -1501,9 +1507,8 @@ void stage_draw_bottom_text(void) {
|
|||
.font_ptr = font,
|
||||
});
|
||||
|
||||
if(global.replay.input.replay != NULL) {
|
||||
ReplayState *rst = &global.replay.input;
|
||||
|
||||
ReplayState *rst = &global.replay.input;
|
||||
if(rst->replay != NULL && (!stage_is_demo_mode() || rst->play.desync_frame >= 0)) {
|
||||
r_shader_ptr(stagedraw.hud_text.shader);
|
||||
// XXX: does it make sense to use the monospace font here?
|
||||
|
||||
|
@ -1832,6 +1837,51 @@ void stage_draw_hud(void) {
|
|||
.color = RGBA(1 - red, 1 - red, 1 - red, 1 - red),
|
||||
});
|
||||
}
|
||||
|
||||
// Demo indicator
|
||||
if(stage_is_demo_mode()) {
|
||||
cmplxf pos = CMPLXF(
|
||||
VIEWPORT_X + VIEWPORT_W * 0.5f,
|
||||
VIEWPORT_Y + VIEWPORT_H * 0.5f - 75
|
||||
);
|
||||
|
||||
float bg_width = 250;
|
||||
float bg_height = 60;
|
||||
|
||||
Sprite *bg = res_sprite("part/smoke");
|
||||
|
||||
SpriteParams sp = {
|
||||
.sprite_ptr = bg,
|
||||
.shader_ptr = res_shader("sprite_default"),
|
||||
.color = RGBA(0.1, 0.1, 0.2, 0.07),
|
||||
};
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_translate(crealf(pos), cimagf(pos), 0);
|
||||
r_mat_mv_scale(bg_width / bg->w, bg_height / bg->h, 1);
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_rotate(global.frames * 0.5 * DEG2RAD, 0, 0, 1);
|
||||
r_draw_sprite(&sp);
|
||||
r_mat_mv_pop();
|
||||
|
||||
sp.color = RGBA(0.2, 0.1, 0.1, 0.07);
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_rotate(global.frames * -0.93 * DEG2RAD, 0, 0, 1);
|
||||
r_draw_sprite(&sp);
|
||||
r_mat_mv_pop();
|
||||
|
||||
r_mat_mv_pop();
|
||||
|
||||
text_draw("Demo", &(TextParams) {
|
||||
.align = ALIGN_CENTER,
|
||||
.font = "big",
|
||||
.shader = "text_demo",
|
||||
.shader_params = &(ShaderCustomParams) { global.frames / 60.0f },
|
||||
.pos.as_cmplx = pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void stage_display_clear_screen(const StageClearBonus *bonus) {
|
||||
|
|
Loading…
Reference in a new issue