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:
parent
4246ed2cfa
commit
0bbf1a619e
8 changed files with 82 additions and 74 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
src/replay.c
52
src/replay.c
|
@ -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) {
|
||||
|
|
26
src/replay.h
26
src/replay.h
|
@ -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);
|
||||
|
|
48
src/stage.c
48
src/stage.c
|
@ -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) {
|
||||
|
|
|
@ -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)))
|
||||
|
|
Loading…
Reference in a new issue