taisei/src/replay/demoplayer.c
Andrei Alexeyev 57a08d4c7a
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.
2023-04-07 16:08:50 +02:00

193 lines
4.4 KiB
C

/*
* 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,
});
}