player input, gamepad, and replay improvements
may break existing replays
This commit is contained in:
parent
c0e9d50cf4
commit
d9611b0831
10 changed files with 255 additions and 139 deletions
|
@ -36,7 +36,6 @@ typedef struct Dialog {
|
|||
int page_time;
|
||||
|
||||
int birthtime;
|
||||
bool skip;
|
||||
} Dialog;
|
||||
|
||||
Dialog *create_dialog(char *left, char *right);
|
||||
|
|
114
src/gamepad.c
114
src/gamepad.c
|
@ -207,6 +207,24 @@ int gamepad_axis2gamekey(SDL_GameControllerAxis id, int val) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
SDL_GameControllerAxis gamepad_gamekey2axis(KeyIndex key) {
|
||||
switch(key) {
|
||||
case KEY_UP: case KEY_DOWN: return config_get_int(CONFIG_GAMEPAD_AXIS_UD);
|
||||
case KEY_LEFT: case KEY_RIGHT: return config_get_int(CONFIG_GAMEPAD_AXIS_LR);
|
||||
default: return SDL_CONTROLLER_AXIS_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
int gamepad_gamekey2axisval(KeyIndex key) {
|
||||
switch(key) {
|
||||
case KEY_UP: return AXISVAL_UP;
|
||||
case KEY_DOWN: return AXISVAL_DOWN;
|
||||
case KEY_LEFT: return AXISVAL_LEFT;
|
||||
case KEY_RIGHT: return AXISVAL_RIGHT;
|
||||
default: return AXISVAL_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int gamepad_axis2menuevt(SDL_GameControllerAxis id, int val) {
|
||||
if(id == SDL_CONTROLLER_AXIS_LEFTX || id == SDL_CONTROLLER_AXIS_RIGHTX)
|
||||
return val == AXISVAL_LEFT ? E_CursorLeft : E_CursorRight;
|
||||
|
@ -237,31 +255,71 @@ float gamepad_axis_sens(SDL_GameControllerAxis id) {
|
|||
return 1.0;
|
||||
}
|
||||
|
||||
static int gamepad_axis_process_value_deadzone(int raw) {
|
||||
int val, vsign;
|
||||
float deadzone = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_DEADZONE), 0.01, 0.999);
|
||||
int minval = clamp(deadzone, 0, 1) * GAMEPAD_AXIS_MAX;
|
||||
|
||||
val = raw;
|
||||
vsign = sign(val);
|
||||
val = abs(val);
|
||||
|
||||
if(val < minval) {
|
||||
val = 0;
|
||||
} else {
|
||||
val = vsign * clamp((val - minval) / (1.0 - deadzone), 0, GAMEPAD_AXIS_MAX);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int gamepad_axis_process_value(SDL_GameControllerAxis id, int raw) {
|
||||
double sens = gamepad_axis_sens(id);
|
||||
int sens_sign = sign(sens);
|
||||
|
||||
raw = gamepad_axis_process_value_deadzone(raw);
|
||||
|
||||
double x = raw / (double)GAMEPAD_AXIS_MAX;
|
||||
int in_sign = sign(x);
|
||||
|
||||
x = pow(fabs(x), 1.0 / fabs(sens)) * in_sign * sens_sign;
|
||||
x = x ? x : 0;
|
||||
x = clamp(x * GAMEPAD_AXIS_MAX, GAMEPAD_AXIS_MIN, GAMEPAD_AXIS_MAX);
|
||||
|
||||
return (int)x;
|
||||
}
|
||||
|
||||
int gamepad_get_player_axis_value(GamepadPlrAxis paxis) {
|
||||
SDL_GameControllerAxis id;
|
||||
|
||||
if(!gamepad.initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(paxis == PLRAXIS_LR) {
|
||||
id = config_get_int(CONFIG_GAMEPAD_AXIS_LR);
|
||||
} else if(paxis == PLRAXIS_UD) {
|
||||
id = config_get_int(CONFIG_GAMEPAD_AXIS_UD);
|
||||
} else {
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
return gamepad_axis_process_value(id, SDL_GameControllerGetAxis(gamepad.device, id));
|
||||
}
|
||||
|
||||
void gamepad_axis(SDL_GameControllerAxis id, int raw, EventHandler handler, EventFlags flags, void *arg) {
|
||||
signed char *a = gamepad.axis;
|
||||
signed char val = AXISVAL(raw);
|
||||
signed char val = AXISVAL(gamepad_axis_process_value_deadzone(raw));
|
||||
bool free = config_get_int(CONFIG_GAMEPAD_AXIS_FREE);
|
||||
|
||||
bool menu = flags & EF_Menu;
|
||||
bool game = flags & EF_Game;
|
||||
bool gp = flags & EF_Gamepad;
|
||||
|
||||
//printf("axis: %i %i %i\n", id, val, raw);
|
||||
|
||||
if(game && free) {
|
||||
int evt = gamepad_axis2gameevt(id);
|
||||
if(evt >= 0) {
|
||||
double sens = gamepad_axis_sens(id);
|
||||
int sens_sign = sign(sens);
|
||||
|
||||
double x = raw / (double)GAMEPAD_AXIS_MAX;
|
||||
int in_sign = sign(x);
|
||||
|
||||
x = pow(fabs(x), 1.0 / fabs(sens)) * in_sign * sens_sign;
|
||||
x = x ? x : 0;
|
||||
x = clamp(x * GAMEPAD_AXIS_MAX, GAMEPAD_AXIS_MIN, GAMEPAD_AXIS_MAX);
|
||||
|
||||
handler(evt, x, arg);
|
||||
handler(evt, gamepad_axis_process_value(id, raw), arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,11 +341,12 @@ void gamepad_axis(SDL_GameControllerAxis id, int raw, EventHandler handler, Even
|
|||
}
|
||||
}
|
||||
}
|
||||
} else { // simulate release
|
||||
} else if(a[id]) { // simulate release
|
||||
if(game) {
|
||||
int key = gamepad_axis2gamekey(id, a[id]);
|
||||
handler(E_PlrKeyUp, key, arg);
|
||||
}
|
||||
|
||||
a[id] = AXISVAL_NULL;
|
||||
}
|
||||
|
||||
|
@ -353,25 +412,10 @@ void gamepad_event(SDL_Event *event, EventHandler handler, EventFlags flags, voi
|
|||
if(!gamepad.initialized)
|
||||
return;
|
||||
|
||||
int val;
|
||||
int vsign;
|
||||
float deadzone = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_DEADZONE), 0, 0.999);
|
||||
int minval = clamp(deadzone, 0, 1) * GAMEPAD_AXIS_MAX;
|
||||
|
||||
switch(event->type) {
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
if(event->caxis.which == gamepad.instance) {
|
||||
val = event->caxis.value;
|
||||
vsign = sign(val);
|
||||
val = abs(val);
|
||||
|
||||
if(val < minval) {
|
||||
val = 0;
|
||||
} else {
|
||||
val = vsign * clamp((val - minval) / (1.0 - deadzone), 0, GAMEPAD_AXIS_MAX);
|
||||
}
|
||||
|
||||
gamepad_axis(event->caxis.axis, val, handler, flags, arg);
|
||||
gamepad_axis(event->caxis.axis, event->caxis.value, handler, flags, arg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -434,6 +478,14 @@ bool gamepad_gamekeypressed(KeyIndex key) {
|
|||
if(!gamepad.initialized)
|
||||
return false;
|
||||
|
||||
if(!config_get_int(CONFIG_GAMEPAD_AXIS_FREE)) {
|
||||
SDL_GameControllerAxis axis = gamepad_gamekey2axis(key);
|
||||
|
||||
if(axis != SDL_CONTROLLER_AXIS_INVALID && gamepad.axis[axis] == gamepad_gamekey2axisval(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int gpkey = config_gamepad_key_from_key(key);
|
||||
|
||||
if(gpkey < 0) {
|
||||
|
|
|
@ -14,6 +14,21 @@
|
|||
#include "events.h"
|
||||
#include "config.h"
|
||||
|
||||
enum {
|
||||
AXISVAL_LEFT = -1,
|
||||
AXISVAL_RIGHT = 1,
|
||||
|
||||
AXISVAL_UP = -1,
|
||||
AXISVAL_DOWN = 1,
|
||||
|
||||
AXISVAL_NULL = 0
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PLRAXIS_LR, // aka X
|
||||
PLRAXIS_UD, // aka Y
|
||||
} GamepadPlrAxis;
|
||||
|
||||
void gamepad_init(void);
|
||||
void gamepad_shutdown(void);
|
||||
void gamepad_restart(void);
|
||||
|
@ -32,21 +47,7 @@ bool gamepad_gamekeypressed(KeyIndex key);
|
|||
const char* gamepad_button_name(SDL_GameControllerButton btn);
|
||||
const char* gamepad_axis_name(SDL_GameControllerAxis btn);
|
||||
|
||||
// shitty workaround for the options menu. Used to list devices while the gamepad subsystem is off.
|
||||
// only initializes the SDL subsystem so you can use gamepad_devicecount/gamepad_devicename.
|
||||
// if gamepad has been initialized already, these do nothing.
|
||||
void gamepad_init_bare(void);
|
||||
void gamepad_shutdown_bare(void);
|
||||
|
||||
enum {
|
||||
AXISVAL_LEFT = -1,
|
||||
AXISVAL_RIGHT = 1,
|
||||
|
||||
AXISVAL_UP = -1,
|
||||
AXISVAL_DOWN = 1,
|
||||
|
||||
AXISVAL_NULL = 0
|
||||
};
|
||||
int gamepad_get_player_axis_value(GamepadPlrAxis paxis);
|
||||
|
||||
#define GAMEPAD_AXIS_MAX 32767
|
||||
#define GAMEPAD_AXIS_MIN -32768
|
||||
|
|
|
@ -763,7 +763,7 @@ void draw_options_menu(MenuData *menu) {
|
|||
case BT_GamepadDevice: {
|
||||
if(bind_isactive(bind)) {
|
||||
// XXX: I'm not exactly a huge fan of fixing up state in drawing code, but it seems the way to go for now...
|
||||
bind->valrange_max = gamepad_devicecount() - 1;
|
||||
bind->valrange_max = gamepad_devicecount();
|
||||
|
||||
if(bind->selected < 0 || bind->selected > bind->valrange_max) {
|
||||
bind->selected = gamepad_currentdevice();
|
||||
|
|
164
src/player.c
164
src/player.c
|
@ -14,6 +14,8 @@
|
|||
#include "stage.h"
|
||||
#include "stagetext.h"
|
||||
|
||||
#include <SDL_bits.h>
|
||||
|
||||
void init_player(Player *plr) {
|
||||
memset(plr, 0, sizeof(Player));
|
||||
plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-64);
|
||||
|
@ -54,7 +56,7 @@ static void player_full_power(Player *plr) {
|
|||
stagetext_add("Full Power!", VIEWPORT_W * 0.5 + VIEWPORT_H * 0.33 * I, AL_Center, _fonts.mainmenu, rgb(1, 1, 1), 0, 60, 20, 20);
|
||||
}
|
||||
|
||||
void player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
||||
bool player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
||||
npow = clamp(npow, 0, PLR_MAX_POWER);
|
||||
|
||||
switch(plr->cha) {
|
||||
|
@ -72,6 +74,8 @@ void player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
|||
if(plr->power == PLR_MAX_POWER && oldpow < PLR_MAX_POWER && handle_fullpower) {
|
||||
player_full_power(plr);
|
||||
}
|
||||
|
||||
return oldpow != plr->power;
|
||||
}
|
||||
|
||||
void player_move(Player *plr, complex delta) {
|
||||
|
@ -203,9 +207,9 @@ void player_logic(Player* plr) {
|
|||
}
|
||||
}
|
||||
|
||||
void player_bomb(Player *plr) {
|
||||
bool player_bomb(Player *plr) {
|
||||
if(global.boss && global.boss->current && global.boss->current->type == AT_ExtraSpell)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if(global.frames - plr->recovery >= 0 && (plr->bombs > 0 || plr->iddqd) && global.frames - plr->respawntime >= 60) {
|
||||
player_fail_spell(plr);
|
||||
|
@ -234,7 +238,11 @@ void player_bomb(Player *plr) {
|
|||
}
|
||||
|
||||
plr->recovery = global.frames + BOMB_RECOVERY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void player_realdeath(Player *plr) {
|
||||
|
@ -290,37 +298,74 @@ static PlrInputFlag key_to_inflag(KeyIndex key) {
|
|||
case KEY_RIGHT: return INFLAG_RIGHT; break;
|
||||
case KEY_FOCUS: return INFLAG_FOCUS; break;
|
||||
case KEY_SHOT: return INFLAG_SHOT; break;
|
||||
case KEY_SKIP: return INFLAG_SKIP; break;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void player_setinputflag(Player *plr, KeyIndex key, bool mode) {
|
||||
PlrInputFlag flag = key_to_inflag(key);
|
||||
bool player_updateinputflags(Player *plr, PlrInputFlag flags) {
|
||||
if(flags == plr->inputflags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!flag) {
|
||||
return;
|
||||
PlrInputFlag newmove = INFLAGS_MOVE & flags & ~plr->inputflags;
|
||||
|
||||
if(newmove) {
|
||||
plr->prevmove = plr->curmove;
|
||||
plr->prevmovetime = plr->movetime;
|
||||
plr->curmove = (1 << (unsigned)SDL_MostSignificantBitIndex32(flags));
|
||||
plr->movetime = global.frames;
|
||||
}
|
||||
|
||||
plr->inputflags = flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool player_updateinputflags_moveonly(Player *plr, PlrInputFlag flags) {
|
||||
return player_updateinputflags(plr, (flags & INFLAGS_MOVE) | (plr->inputflags & ~INFLAGS_MOVE));
|
||||
}
|
||||
|
||||
bool player_setinputflag(Player *plr, KeyIndex key, bool mode) {
|
||||
PlrInputFlag newflags = plr->inputflags;
|
||||
PlrInputFlag keyflag = key_to_inflag(key);
|
||||
|
||||
if(!keyflag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mode) {
|
||||
if(flag & INFLAGS_MOVE) {
|
||||
plr->prevmove = plr->curmove;
|
||||
plr->prevmovetime = plr->movetime;
|
||||
plr->curmove = flag;
|
||||
plr->movetime = global.frames;
|
||||
}
|
||||
|
||||
plr->inputflags |= flag;
|
||||
newflags |= keyflag;
|
||||
} else {
|
||||
plr->inputflags &= ~flag;
|
||||
newflags &= ~keyflag;
|
||||
}
|
||||
|
||||
return player_updateinputflags(plr, newflags);
|
||||
}
|
||||
|
||||
void player_event(Player* plr, int type, int key) {
|
||||
static bool player_set_axis(int *aptr, uint16_t value) {
|
||||
int16_t new = (int16_t)value;
|
||||
|
||||
if(*aptr == new) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aptr = new;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool player_event(Player *plr, uint8_t type, uint16_t value) {
|
||||
bool useful = true;
|
||||
|
||||
switch(type) {
|
||||
case EV_PRESS:
|
||||
switch(key) {
|
||||
if(global.dialog && (value == KEY_SHOT || value == KEY_BOMB)) {
|
||||
page_dialog(&global.dialog);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(value) {
|
||||
case KEY_BOMB:
|
||||
player_bomb(plr);
|
||||
useful = player_bomb(plr);
|
||||
break;
|
||||
|
||||
case KEY_IDDQD:
|
||||
|
@ -328,35 +373,55 @@ void player_event(Player* plr, int type, int key) {
|
|||
break;
|
||||
|
||||
case KEY_POWERUP:
|
||||
player_set_power(plr, plr->power + 100,true);
|
||||
useful = player_set_power(plr, plr->power + 100, true);
|
||||
break;
|
||||
|
||||
case KEY_POWERDOWN:
|
||||
player_set_power(plr, plr->power - 100,true);
|
||||
useful = player_set_power(plr, plr->power - 100, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
player_setinputflag(plr, key, true);
|
||||
useful = player_setinputflag(plr, value, true);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_RELEASE:
|
||||
player_setinputflag(plr, key, false);
|
||||
player_setinputflag(plr, value, false);
|
||||
break;
|
||||
|
||||
case EV_AXIS_LR:
|
||||
plr->axis_lr = key;
|
||||
useful = player_set_axis(&plr->axis_lr, value);
|
||||
break;
|
||||
|
||||
case EV_AXIS_UD:
|
||||
plr->axis_ud = key;
|
||||
useful = player_set_axis(&plr->axis_ud, value);
|
||||
break;
|
||||
|
||||
case EV_INFLAGS:
|
||||
useful = player_updateinputflags(plr, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warn("Unknown event type %d (value=%d)", type, key);
|
||||
log_warn("Can not handle event: [%i:%02x:%04x]", global.frames, type, value);
|
||||
useful = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return useful;
|
||||
}
|
||||
|
||||
bool player_event_with_replay(Player *plr, uint8_t type, uint16_t value) {
|
||||
assert(global.replaymode == REPLAY_RECORD);
|
||||
|
||||
if(player_event(plr, type, value)) {
|
||||
replay_stage_event(global.replay_stage, global.frames, type, value);
|
||||
return true;
|
||||
} else {
|
||||
log_debug("Useless event discarded: [%i:%02x:%04x]", global.frames, type, value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// free-axis movement
|
||||
|
@ -376,10 +441,12 @@ bool player_applymovement_gamepad(Player *plr) {
|
|||
int sr = sign(creal(direction));
|
||||
int si = sign(cimag(direction));
|
||||
|
||||
player_setinputflag(plr, KEY_UP, si == -1);
|
||||
player_setinputflag(plr, KEY_DOWN, si == 1);
|
||||
player_setinputflag(plr, KEY_LEFT, sr == -1);
|
||||
player_setinputflag(plr, KEY_RIGHT, sr == 1);
|
||||
player_updateinputflags_moveonly(plr,
|
||||
(INFLAG_UP * (si == -1)) |
|
||||
(INFLAG_DOWN * (si == 1)) |
|
||||
(INFLAG_LEFT * (sr == -1)) |
|
||||
(INFLAG_RIGHT * (sr == 1))
|
||||
);
|
||||
|
||||
if(direction) {
|
||||
plr->gamepadmove = true;
|
||||
|
@ -426,30 +493,41 @@ void player_applymovement(Player *plr) {
|
|||
player_move(&global.plr, direction);
|
||||
}
|
||||
|
||||
void player_input_workaround(Player *plr) {
|
||||
if(global.dialog)
|
||||
return;
|
||||
void player_fix_input(Player *plr) {
|
||||
// correct input state to account for any events we might have missed,
|
||||
// usually because the pause menu ate them up
|
||||
|
||||
PlrInputFlag newflags = plr->inputflags;
|
||||
|
||||
for(KeyIndex key = KEYIDX_FIRST; key <= KEYIDX_LAST; ++key) {
|
||||
int flag = key_to_inflag(key);
|
||||
|
||||
if(flag) {
|
||||
int event = -1;
|
||||
if(flag && !(plr->gamepadmove && (flag & INFLAGS_MOVE))) {
|
||||
bool flagset = plr->inputflags & flag;
|
||||
bool keyheld = gamekeypressed(key);
|
||||
|
||||
if(flagset && !keyheld) {
|
||||
// something ate the release event (most likely the ingame menu)
|
||||
event = EV_RELEASE;
|
||||
newflags &= ~flag;
|
||||
} else if(!flagset && keyheld) {
|
||||
// something ate the press event (most likely the ingame menu)
|
||||
event = EV_PRESS;
|
||||
newflags |= flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event != -1) {
|
||||
player_event(plr, event, key);
|
||||
replay_stage_event(global.replay_stage, global.frames, event, key);
|
||||
}
|
||||
if(newflags != plr->inputflags) {
|
||||
player_event_with_replay(plr, EV_INFLAGS, newflags);
|
||||
}
|
||||
|
||||
if(config_get_int(CONFIG_GAMEPAD_AXIS_FREE)) {
|
||||
int axis_lr = gamepad_get_player_axis_value(PLRAXIS_LR);
|
||||
int axis_ud = gamepad_get_player_axis_value(PLRAXIS_UD);
|
||||
|
||||
if(plr->axis_lr != axis_lr) {
|
||||
player_event_with_replay(plr, EV_AXIS_LR, axis_lr);
|
||||
}
|
||||
|
||||
if(plr->axis_ud != axis_ud) {
|
||||
player_event_with_replay(plr, EV_AXIS_UD, axis_ud);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
src/player.h
11
src/player.h
|
@ -48,6 +48,7 @@ typedef enum {
|
|||
INFLAG_RIGHT = 8,
|
||||
INFLAG_FOCUS = 16,
|
||||
INFLAG_SHOT = 32,
|
||||
INFLAG_SKIP = 64,
|
||||
} PlrInputFlag;
|
||||
|
||||
enum {
|
||||
|
@ -119,6 +120,7 @@ enum {
|
|||
EV_AXIS_UD,
|
||||
EV_CHECK_DESYNC, // replay-only
|
||||
EV_FPS, // replay-only
|
||||
EV_INFLAGS,
|
||||
};
|
||||
|
||||
void init_player(Player*);
|
||||
|
@ -128,19 +130,18 @@ void player_draw(Player*);
|
|||
void player_logic(Player*);
|
||||
|
||||
void player_set_char(Player*, Character);
|
||||
void player_set_power(Player *plr, short npow, bool handle_fullpower);
|
||||
bool player_set_power(Player *plr, short npow, bool handle_fullpower);
|
||||
|
||||
void player_move(Player*, complex delta);
|
||||
|
||||
void player_bomb(Player*);
|
||||
void player_realdeath(Player*);
|
||||
void player_death(Player*);
|
||||
void player_graze(Player*, complex, int);
|
||||
|
||||
void player_setinputflag(Player *plr, KeyIndex key, bool mode);
|
||||
void player_event(Player* plr, int type, int key);
|
||||
bool player_event(Player* plr, uint8_t type, uint16_t value);
|
||||
bool player_event_with_replay(Player *plr, uint8_t type, uint16_t value);
|
||||
void player_applymovement(Player* plr);
|
||||
void player_input_workaround(Player *plr);
|
||||
void player_fix_input(Player *plr);
|
||||
|
||||
void player_add_life_fragments(Player *plr, int frags);
|
||||
void player_add_bomb_fragments(Player *plr, int frags);
|
||||
|
|
|
@ -106,17 +106,16 @@ void replay_destroy(Replay *rpy) {
|
|||
log_debug("Replay at %p destroyed", (void*)rpy);
|
||||
}
|
||||
|
||||
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, int16_t value) {
|
||||
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, uint16_t value) {
|
||||
if(!stg) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReplayStage *s = stg;
|
||||
ReplayEvent *e = s->events + s->numevents;
|
||||
ReplayEvent *e = s->events + s->numevents++;
|
||||
e->frame = frame;
|
||||
e->type = type;
|
||||
e->value = (uint16_t)value;
|
||||
s->numevents++;
|
||||
e->value = value;
|
||||
|
||||
if(s->numevents >= s->capacity) {
|
||||
log_debug("Replay stage reached its capacity of %d, reallocating", s->capacity);
|
||||
|
@ -587,6 +586,7 @@ void replay_stage_check_desync(ReplayStage *stg, int time, uint16_t check, Repla
|
|||
if(mode == REPLAY_PLAY) {
|
||||
if(stg->desync_check && stg->desync_check != check) {
|
||||
log_warn("Replay desync detected! %u != %u", stg->desync_check, check);
|
||||
stg->desynced = true;
|
||||
} else {
|
||||
log_debug("%u OK", check);
|
||||
}
|
||||
|
|
|
@ -37,8 +37,9 @@
|
|||
#define REPLAY_EXTENSION "tsr"
|
||||
#define REPLAY_USELESS_BYTE 0x69
|
||||
|
||||
#define REPLAY_WRITE_DESYNC_CHECKS
|
||||
|
||||
#ifdef DEBUG
|
||||
#define REPLAY_WRITE_DESYNC_CHECKS
|
||||
#define REPLAY_LOAD_GARBAGE_TEST
|
||||
#endif
|
||||
|
||||
|
@ -91,6 +92,7 @@ typedef struct ReplayStage {
|
|||
int playpos;
|
||||
int fps;
|
||||
uint16_t desync_check;
|
||||
bool desynced;
|
||||
} ReplayStage;
|
||||
|
||||
typedef struct Replay {
|
||||
|
@ -147,7 +149,7 @@ ReplayStage* replay_create_stage(Replay *rpy, StageInfo *stage, uint64_t seed, D
|
|||
void replay_destroy(Replay *rpy);
|
||||
void replay_destroy_events(Replay *rpy);
|
||||
|
||||
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, int16_t value);
|
||||
void replay_stage_event(ReplayStage *stg, uint32_t frame, uint8_t type, uint16_t value);
|
||||
void replay_stage_check_desync(ReplayStage *stg, int time, uint16_t check, ReplayMode mode);
|
||||
void replay_stage_sync_player_state(ReplayStage *stg, Player *plr);
|
||||
|
||||
|
|
42
src/stage.c
42
src/stage.c
|
@ -263,25 +263,11 @@ void stage_input_event(EventType type, int key, void *arg) {
|
|||
break;
|
||||
#endif
|
||||
|
||||
if(global.dialog && (key == KEY_SHOT || key == KEY_BOMB)) {
|
||||
page_dialog(&global.dialog);
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_PRESS, key);
|
||||
} else {
|
||||
player_event(&global.plr, EV_PRESS, key);
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_PRESS, key);
|
||||
|
||||
if(key == KEY_SKIP && global.dialog) {
|
||||
global.dialog->skip = true;
|
||||
}
|
||||
}
|
||||
player_event_with_replay(&global.plr, EV_PRESS, key);
|
||||
break;
|
||||
|
||||
case E_PlrKeyUp:
|
||||
player_event(&global.plr, EV_RELEASE, key);
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, key);
|
||||
|
||||
if(key == KEY_SKIP && global.dialog)
|
||||
global.dialog->skip = false;
|
||||
player_event_with_replay(&global.plr, EV_RELEASE, key);
|
||||
break;
|
||||
|
||||
case E_Pause:
|
||||
|
@ -289,13 +275,11 @@ void stage_input_event(EventType type, int key, void *arg) {
|
|||
break;
|
||||
|
||||
case E_PlrAxisLR:
|
||||
player_event(&global.plr, EV_AXIS_LR, key);
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_AXIS_LR, key);
|
||||
player_event_with_replay(&global.plr, EV_AXIS_LR, (uint16_t)key);
|
||||
break;
|
||||
|
||||
case E_PlrAxisUD:
|
||||
player_event(&global.plr, EV_AXIS_UD, key);
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_AXIS_UD, key);
|
||||
player_event_with_replay(&global.plr, EV_AXIS_UD, (uint16_t)key);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
|
@ -333,12 +317,7 @@ void replay_input(void) {
|
|||
break;
|
||||
|
||||
default:
|
||||
if(global.dialog && e->type == EV_PRESS && (e->value == KEY_SHOT || e->value == KEY_BOMB))
|
||||
page_dialog(&global.dialog);
|
||||
else if(global.dialog && (e->type == EV_PRESS || e->type == EV_RELEASE) && e->value == KEY_SKIP)
|
||||
global.dialog->skip = (e->type == EV_PRESS);
|
||||
else
|
||||
player_event(&global.plr, e->type, (int16_t)e->value);
|
||||
player_event(&global.plr, e->type, (int16_t)e->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -349,14 +328,7 @@ void replay_input(void) {
|
|||
|
||||
void stage_input(void) {
|
||||
handle_events(stage_input_event, EF_Game, NULL);
|
||||
|
||||
// workaround
|
||||
if(global.dialog && global.dialog->skip && !gamekeypressed(KEY_SKIP)) {
|
||||
global.dialog->skip = false;
|
||||
replay_stage_event(global.replay_stage, global.frames, EV_RELEASE, KEY_SKIP);
|
||||
}
|
||||
|
||||
player_input_workaround(&global.plr);
|
||||
player_fix_input(&global.plr);
|
||||
player_applymovement(&global.plr);
|
||||
}
|
||||
|
||||
|
@ -372,7 +344,7 @@ static void stage_logic(void) {
|
|||
if(global.boss && !global.dialog)
|
||||
process_boss(&global.boss);
|
||||
|
||||
if(global.dialog && global.dialog->skip && global.frames - global.dialog->page_time > 3)
|
||||
if(global.dialog && (global.plr.inputflags & INFLAG_SKIP) && global.frames - global.dialog->page_time > 3)
|
||||
page_dialog(&global.dialog);
|
||||
|
||||
global.frames++;
|
||||
|
|
|
@ -502,9 +502,20 @@ void stage_draw_hud(void) {
|
|||
|
||||
if(global.replaymode == REPLAY_PLAY) {
|
||||
snprintf(buf, sizeof(buf), "Replay: %s (%i fps)", global.replay.playername, global.replay_stage->fps);
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
|
||||
draw_text(AL_Left, 0, SCREEN_H - 0.5 * stringheight(buf, _fonts.standard), buf, _fonts.standard);
|
||||
int x = 0, y = SCREEN_H - 0.5 * stringheight(buf, _fonts.standard);
|
||||
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 0.6f);
|
||||
draw_text(AL_Left, x, y, buf, _fonts.standard);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
if(global.replay_stage->desynced) {
|
||||
x += stringwidth(buf, _fonts.standard);
|
||||
strlcpy(buf, " (DESYNCED)", sizeof(buf));
|
||||
|
||||
glColor4f(1.0f, 0.2f, 0.2f, 0.6f);
|
||||
draw_text(AL_Left, x, y, buf, _fonts.standard);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
#ifdef PLR_DPS_STATS
|
||||
else {
|
||||
|
|
Loading…
Reference in a new issue