taisei/src/replay/play.c
2024-05-17 04:58:47 +02:00

103 lines
2.4 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "replay.h"
#include "struct.h"
#include "state.h"
#include "stageinfo.h"
#include "../stage.h"
#include "global.h"
typedef struct ReplayContext {
CallChain cc;
Replay *rpy;
ResourceGroup rg;
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, 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);
return;
}
auto ctx = ALLOC(ReplayContext, {
.cc = next,
.rpy = rpy,
.stage_idx = firstidx,
.demo_mode = demo_mode,
});
res_group_init(&ctx->rg);
replay_do_play(CALLCHAIN_RESULT(ctx, NULL));
}
static void replay_do_play(CallChainResult ccr) {
ReplayContext *ctx = ccr.ctx;
ReplayStage *rstg = NULL;
StageInfo *stginfo = NULL;
Replay *rpy = ctx->rpy;
while(ctx->stage_idx < rpy->stages.num_elements) {
rstg = dynarray_get_ptr(&rpy->stages, ctx->stage_idx++);
stginfo = stageinfo_get_by_id(rstg->stage);
if(!stginfo) {
log_warn("Invalid stage %X in replay at %i skipped.", rstg->stage, ctx->stage_idx);
continue;
}
break;
};
if(stginfo == NULL) {
replay_do_cleanup(ccr);
} else {
assume(rstg != NULL);
replay_state_init_play(&global.replay.input, rpy, rstg);
global.replay.input.play.demo_mode = ctx->demo_mode;
global.plr.mode = plrmode_find(rstg->plr_char, rstg->plr_shot);
res_group_release(&ctx->rg);
stage_enter(stginfo, &ctx->rg, CALLCHAIN(replay_do_post_play, ctx));
}
}
static void replay_do_post_play(CallChainResult ccr) {
ReplayContext *ctx = ccr.ctx;
if(global.gameover == GAMEOVER_ABORT) {
replay_do_cleanup(ccr);
return;
}
if(global.gameover == GAMEOVER_RESTART) {
--ctx->stage_idx;
}
global.gameover = 0;
replay_do_play(ccr);
}
static void replay_do_cleanup(CallChainResult ccr) {
ReplayContext *ctx = ccr.ctx;
global.gameover = 0;
replay_state_deinit(&global.replay.input);
res_group_release(&ctx->rg);
CallChain cc = ctx->cc;
mem_free(ctx);
run_call_chain(&cc, NULL);
}