Further refinements to replay API and structures

Compatibility preserved.

The replay API functions are now completely independent of the global state.
This commit is contained in:
Andrei "Akari" Alexeyev 2017-02-10 01:24:19 +02:00
parent 4246ed2cfa
commit 0bbf1a619e
8 changed files with 82 additions and 74 deletions

View file

@ -78,13 +78,6 @@ enum {
GAMEOVER_RESTART
};
typedef enum {
D_Easy = 1,
D_Normal,
D_Hard,
D_Lunatic
} Difficulty;
typedef struct {
int fpstime; // frame counter
int fps;
@ -122,6 +115,7 @@ typedef struct {
Replay replay;
ReplayMode replaymode;
ReplayStage *replay_stage;
float shake_view;

View file

@ -35,6 +35,7 @@ troll:
if(char_menu_loop(&m) == -1)
goto troll;
global.replay_stage = NULL;
replay_init(&global.replay);
int chr = global.plr.cha;
@ -60,7 +61,7 @@ troll2:
goto troll2;
}
if(global.replay.active) {
if(global.replay_stage) {
switch(tconfig.intval[SAVE_RPY]) {
case 0: break;
@ -76,6 +77,8 @@ troll2:
break;
}
}
global.replay_stage = NULL;
}
if(global.game_over == GAMEOVER_WIN && !arg) {

View file

@ -55,8 +55,7 @@ void start_replay(void *arg) {
init_player(&global.plr);
for(int i = mctx->pickedstage; i < global.replay.numstages; ++i) {
replay_select(&global.replay, i);
ReplayStage *rstg = global.replay.current;
ReplayStage *rstg = global.replay_stage = global.replay.stages+i;
StageInfo *gstg = stage_get(rstg->stage);
if(!gstg) {
@ -77,6 +76,7 @@ void start_replay(void *arg) {
global.game_over = 0;
global.replaymode = REPLAY_RECORD;
replay_destroy(&global.replay);
global.replay_stage = NULL;
}
MenuData* replayview_sub_stageselect(ReplayviewItemContext *ictx) {

View file

@ -378,15 +378,15 @@ void player_input_workaround(Player *plr) {
if(!shot && plr->fire) {
player_event(plr, EV_RELEASE, KEY_SHOT);
replay_event(&global.replay, EV_RELEASE, KEY_SHOT);
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, KEY_SHOT);
} else if(shot && !plr->fire) {
player_event(plr, EV_PRESS, KEY_SHOT);
replay_event(&global.replay, EV_PRESS, KEY_SHOT);
replay_stage_event(global.replay_stage, global.frames, EV_PRESS, KEY_SHOT);
}
if(!focus && plr->focus > 0) {
player_event(plr, EV_RELEASE, KEY_FOCUS);
replay_event(&global.replay, EV_RELEASE, KEY_FOCUS);
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, KEY_FOCUS);
}
}
}

View file

@ -22,15 +22,14 @@ static uint8_t replay_magic_header[] = REPLAY_MAGIC_HEADER;
void replay_init(Replay *rpy) {
memset(rpy, 0, sizeof(Replay));
rpy->active = True;
rpy->playername = malloc(strlen(tconfig.strval[PLAYERNAME]) + 1);
strcpy(rpy->playername, tconfig.strval[PLAYERNAME]);
printf("replay_init(): replay initialized for writting\n");
}
ReplayStage* replay_init_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Player *plr) {
ReplayStage* replay_create_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Difficulty diff, uint32_t points, Player *plr) {
ReplayStage *s;
rpy->stages = (ReplayStage*)realloc(rpy->stages, sizeof(ReplayStage) * (++rpy->numstages));
@ -42,8 +41,8 @@ ReplayStage* replay_init_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Pla
s->stage = stage->id;
s->seed = seed;
s->diff = global.diff;
s->points = global.points;
s->diff = diff;
s->points = points;
s->plr_pos_x = floor(creal(plr->pos));
s->plr_pos_y = floor(cimag(plr->pos));
@ -58,7 +57,6 @@ ReplayStage* replay_init_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Pla
s->plr_moveflags = plr->moveflags;
printf("replay_init_stage(): created a new stage for writting\n");
replay_select(rpy, rpy->numstages-1);
return s;
}
@ -100,24 +98,14 @@ void replay_destroy(Replay *rpy) {
printf("Replay destroyed.\n");
}
ReplayStage* replay_select(Replay *rpy, int stage) {
if(stage < 0 || stage >= rpy->numstages) {
return NULL;
}
rpy->current = &(rpy->stages[stage]);
rpy->currentidx = stage;
return rpy->current;
}
void replay_event(Replay *rpy, uint8_t type, int16_t value) {
if(!rpy->active) {
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, int16_t value) {
if(!stg) {
return;
}
ReplayStage *s = rpy->current;
ReplayStage *s = stg;
ReplayEvent *e = &(s->events[s->numevents]);
e->frame = global.frames;
e->frame = frame;
e->type = type;
e->value = (uint16_t)value;
s->numevents++;
@ -385,6 +373,7 @@ int replay_read(Replay *rpy, SDL_RWops *file, ReplayReadMode mode) {
#undef CHECKPROP
#undef PRINTPROP
#undef PRINTPROP_NOASSIGN
char* replay_getpath(char *name, int ext) {
char *p = (char*)malloc(strlen(get_replays_path()) + strlen(name) + strlen(REPLAY_EXTENSION) + 3);
@ -463,23 +452,24 @@ void replay_copy(Replay *dst, Replay *src, int steal_events) {
}
}
void replay_check_desync(Replay *rpy, int time, uint16_t check) {
if(time % (FPS * 5)) {
void replay_stage_check_desync(ReplayStage *stg, int time, uint16_t check, ReplayMode mode) {
if(!stg || time % (FPS * 5)) {
return;
}
if(global.replaymode == REPLAY_RECORD) {
#ifdef REPLAY_WRITE_DESYNC_CHECKS
printf("replay_check_desync(): %u\n", check);
replay_event(rpy, EV_CHECK_DESYNC, (int16_t)check);
#endif
} else {
if(rpy->desync_check && rpy->desync_check != check) {
warnx("replay_check_desync(): Replay desync detected! %u != %u\n", rpy->desync_check, check);
if(mode == REPLAY_PLAY) {
if(stg->desync_check && stg->desync_check != check) {
warnx("replay_check_desync(): Replay desync detected! %u != %u\n", stg->desync_check, check);
} else {
printf("replay_check_desync(): %u OK\n", check);
}
}
#ifdef REPLAY_WRITE_DESYNC_CHECKS
else {
printf("replay_stage_check_desync(): %u\n", check);
replay_stage_event(stg, time, EV_CHECK_DESYNC, (int16_t)check);
}
#endif
}
int replay_test(void) {

View file

@ -78,8 +78,12 @@ typedef struct ReplayStage {
/* END stored fields */
ReplayEvent *events;
// events allocated (may be higher than numevents)
int capacity;
int playpos;
uint16_t desync_check;
} ReplayStage;
typedef struct Replay {
@ -97,7 +101,8 @@ typedef struct Replay {
char *playername;
uint16_t numstages;
// ReplayStage stages[{numstages}];
// Contains {numstages} elements when not NULL
ReplayStage *stages;
// ALL input events from ALL of the stages
// This is actually loaded into separate sub-arrays for every stage, see ReplayStage.events
@ -112,11 +117,6 @@ typedef struct Replay {
/* END stored fields */
int active;
ReplayStage *stages;
ReplayStage *current;
int currentidx;
uint16_t desync_check;
size_t fileoffset;
} Replay;
@ -133,22 +133,24 @@ typedef enum ReplayReadMode {
} ReplayReadMode;
void replay_init(Replay *rpy);
ReplayStage* replay_init_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Player *plr);
ReplayStage* replay_create_stage(Replay *rpy, StageInfo *stage, uint64_t seed, Difficulty diff, uint32_t points, Player *plr);
void replay_destroy(Replay *rpy);
void replay_destroy_events(Replay *rpy);
ReplayStage* replay_select(Replay *rpy, int stage);
void replay_event(Replay *rpy, uint8_t type, int16_t value);
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, int16_t value);
void replay_stage_check_desync(ReplayStage *stg, int time, uint16_t check, ReplayMode mode);
int replay_write(Replay *rpy, SDL_RWops *file);
int replay_read(Replay *rpy, SDL_RWops *file, ReplayReadMode mode);
char* replay_getpath(char *name, int ext); // must be freed
int replay_save(Replay *rpy, char *name);
int replay_load(Replay *rpy, char *name, ReplayReadMode mode);
void replay_copy(Replay *dst, Replay *src, int steal_events);
char* replay_getpath(char *name, int ext); // must be freed
#endif
void replay_check_desync(Replay *rpy, int time, uint16_t check);
int replay_test(void);

View file

@ -79,10 +79,10 @@ void stage_input_event(EventType type, int key, void *arg) {
case E_PlrKeyDown:
if(global.dialog && (key == KEY_SHOT || key == KEY_BOMB)) {
page_dialog(&global.dialog);
replay_event(&global.replay, EV_PRESS, key);
replay_stage_event(global.replay_stage, global.frames, EV_PRESS, key);
} else {
player_event(&global.plr, EV_PRESS, key);
replay_event(&global.replay, EV_PRESS, key);
replay_stage_event(global.replay_stage, global.frames, EV_PRESS, key);
if(key == KEY_SKIP && global.dialog) {
global.dialog->skip = True;
@ -92,7 +92,7 @@ void stage_input_event(EventType type, int key, void *arg) {
case E_PlrKeyUp:
player_event(&global.plr, EV_RELEASE, key);
replay_event(&global.replay, EV_RELEASE, key);
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, key);
if(key == KEY_SKIP && global.dialog)
global.dialog->skip = False;
@ -104,12 +104,12 @@ void stage_input_event(EventType type, int key, void *arg) {
case E_PlrAxisLR:
player_event(&global.plr, EV_AXIS_LR, key);
replay_event(&global.replay, EV_AXIS_LR, key);
replay_stage_event(global.replay_stage, global.frames, EV_AXIS_LR, key);
break;
case E_PlrAxisUD:
player_event(&global.plr, EV_AXIS_UD, key);
replay_event(&global.replay, EV_AXIS_UD, key);
replay_stage_event(global.replay_stage, global.frames, EV_AXIS_UD, key);
break;
default: break;
@ -122,7 +122,7 @@ void stage_replay_event(EventType type, int state, void *arg) {
}
void replay_input(void) {
ReplayStage *s = global.replay.current;
ReplayStage *s = global.replay_stage;
int i;
handle_events(stage_replay_event, EF_Game, NULL);
@ -144,7 +144,7 @@ void replay_input(void) {
else if(global.dialog && (e->type == EV_PRESS || e->type == EV_RELEASE) && e->value == KEY_SKIP)
global.dialog->skip = (e->type == EV_PRESS);
else if(e->type == EV_CHECK_DESYNC)
global.replay.desync_check = e->value;
s->desync_check = e->value;
else
player_event(&global.plr, e->type, (int16_t)e->value);
break;
@ -161,7 +161,7 @@ void stage_input(void) {
// workaround
if(global.dialog && global.dialog->skip && !gamekeypressed(KEY_SKIP)) {
global.dialog->skip = False;
replay_event(&global.replay, EV_RELEASE, KEY_SKIP);
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, KEY_SKIP);
}
player_applymovement(&global.plr);
@ -424,15 +424,20 @@ void stage_logic(int time) {
if(!global.dialog && !global.boss)
global.timer++;
if(global.timer == time - FADE_TIME || global.replaymode == REPLAY_PLAY && global.frames == global.replay.current->events[global.replay.current->numevents-1].frame - FADE_TIME)
if(global.timer == time - FADE_TIME ||
(
global.replaymode == REPLAY_PLAY &&
global.frames == global.replay_stage->events[global.replay_stage->numevents-1].frame - FADE_TIME
)
) {
set_transition(TransFadeBlack, FADE_TIME, FADE_TIME*2);
}
if(global.timer >= time)
global.game_over = GAMEOVER_WIN;
// BGM handling
if(global.dialog && global.dialog->messages[global.dialog->pos].side == BGM)
{
if(global.dialog && global.dialog->messages[global.dialog->pos].side == BGM) {
start_bgm(global.dialog->messages[global.dialog->pos].msg);
page_dialog(&global.dialog);
}
@ -470,8 +475,12 @@ void stage_loop(StageInfo* info, StageRule start, StageRule end, StageRule draw,
stage_start();
if(global.replaymode == REPLAY_RECORD) {
if(global.replay.active)
replay_init_stage(&global.replay, info, seed, &global.plr);
if(tconfig.intval[SAVE_RPY]) {
global.replay_stage = replay_create_stage(&global.replay, info, seed, global.diff, global.points, &global.plr);
} else {
global.replay_stage = NULL;
}
printf("Random seed: %u\n", seed);
// match replay format
@ -479,7 +488,12 @@ void stage_loop(StageInfo* info, StageRule start, StageRule end, StageRule draw,
uint16_t py = floor(cimag(global.plr.pos));
global.plr.pos = px + I * py;
} else {
ReplayStage *stg = global.replay.current;
if(!global.replay_stage) {
errx(-1, "Attemped to replay a NULL stage");
return;
}
ReplayStage *stg = global.replay_stage;
printf("REPLAY_PLAY mode: %d events, stage: \"%s\"\n", stg->numevents, stage_get(stg->stage)->title);
tsrand_seed_p(&global.rand_game, stg->seed);
@ -522,7 +536,7 @@ void stage_loop(StageInfo* info, StageRule start, StageRule end, StageRule draw,
event();
((global.replaymode == REPLAY_PLAY)? replay_input : stage_input)();
replay_check_desync(&global.replay, global.frames, (tsrand() ^ global.points) & 0xFFFF);
replay_stage_check_desync(global.replay_stage, global.frames, (tsrand() ^ global.points) & 0xFFFF, global.replaymode);
stage_logic(endtime);
@ -550,14 +564,12 @@ void stage_loop(StageInfo* info, StageRule start, StageRule end, StageRule draw,
}
if(global.replaymode == REPLAY_RECORD) {
replay_event(&global.replay, EV_OVER, 0);
replay_stage_event(global.replay_stage, global.frames, EV_OVER, 0);
}
end();
stage_end();
tsrand_switch(&global.rand_visual);
// if(global.replaymode != REPLAY_PLAY)
// replay_destroy(&global.replay);
}
void draw_title(int t, StageInfo *info, Alignment al, int x, int y, const char *text, TTF_Font *font, Color *color) {

View file

@ -19,6 +19,13 @@
#include <projectile.h>
typedef enum {
D_Easy = 1,
D_Normal,
D_Hard,
D_Lunatic
} Difficulty;
#define TIMER(ptr) int *__timep = ptr; int _i = 0, _ni = 0; _i = _ni = _i;
#define AT(t) if(*__timep == t)
#define FROM_TO(start,end,step) _ni = _ni; _i = (*__timep - (start))/(step); if(*__timep >= (start) && *__timep <= (end) && !((*__timep - (start)) % (step)))