replay system reasonable
[carveJwlIkooP6JGAAIwe30JlM.git] / player_replay.c
1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
3
4 #include "player_replay.h"
5
6 VG_STATIC void replay_clear( replay_buffer *replay ){
7 replay->head = NULL;
8 replay->tail = NULL;
9 replay->cursor_frame = NULL;
10 replay->statehead = NULL;
11 replay->cursor = -99999.9;
12 }
13
14 replay_gamestate *replay_frame_gamestate( replay_frame *frame, u16 index ){
15 void *baseptr = frame;
16
17 replay_gamestate *array = (baseptr + vg_align8( sizeof(replay_frame)));
18 return &array[ index ];
19 }
20
21 replay_sfx *replay_frame_sfx( replay_frame *frame, u16 index ){
22 void *gsarr = replay_frame_gamestate( frame, 0 );
23 u32 gssize = frame->gamestate_count * sizeof(replay_gamestate);
24
25 replay_sfx *array = (gsarr + vg_align8(gssize));
26 return &array[index];
27 }
28
29 u32 _replay_frame_size( u16 gamestate_count, u16 sfx_count ){
30 return vg_align8( sizeof( replay_frame ) ) +
31 vg_align8( gamestate_count * sizeof(replay_gamestate) ) +
32 vg_align8( sfx_count * sizeof(replay_sfx) );
33 }
34
35 u32 replay_frame_size( replay_frame *frame ){
36 return _replay_frame_size( frame->gamestate_count, frame->sfx_count );
37 }
38
39 VG_STATIC replay_frame *replay_newframe( replay_buffer *replay,
40 u16 gamestate_count, u16 sfx_count ){
41 replay_frame *frame = NULL;
42 if( replay->head ){
43 assert( replay->head );
44
45 u32 headsize = replay_frame_size( replay->head ),
46 nextpos = ((void *)replay->head - replay->data) + headsize,
47 nextsize = _replay_frame_size( gamestate_count, sfx_count );
48
49 if( nextsize > replay->size ){
50 vg_error( "Keyframe too big\n" );
51 return NULL;
52 }
53
54 if( nextpos + nextsize > replay->size )
55 nextpos = 0;
56
57 check_again:;
58
59 u32 tailpos = (void *)replay->tail - replay->data;
60
61 if( tailpos >= nextpos ){
62 if( nextpos + nextsize > tailpos ){
63 /* remove links */
64 if( replay->cursor_frame == replay->tail )
65 replay->cursor_frame = NULL;
66 if( replay->statehead == replay->tail )
67 replay->statehead = NULL;
68
69 /* pop node */
70 replay->tail = replay->tail->r;
71
72 if( replay->tail ) {
73 replay->tail->l = NULL;
74 goto check_again;
75 }
76 else
77 replay->head = NULL;
78 }
79 }
80
81 frame = replay->data + nextpos;
82
83 if( replay->head )
84 replay->head->r = frame;
85 }
86 else
87 frame = replay->data;
88
89 frame->gamestate_count = gamestate_count;
90 frame->sfx_count = sfx_count;
91 frame->l = replay->head;
92 frame->r = NULL;
93 replay->head = frame;
94 if( !replay->tail ) replay->tail = frame;
95 if( gamestate_count ) replay->statehead = frame;
96
97 return frame;
98 }
99
100 VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
101 if( !replay->head ) return;
102 assert( replay->tail );
103
104 if( t < replay->tail->time ) t = replay->tail->time;
105 if( t > replay->head->time ) t = replay->head->time;
106
107 if( !replay->cursor_frame ) {
108 replay->cursor = replay->head->time;
109 replay->cursor_frame = replay->head;
110
111 if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
112 replay->cursor = replay->tail->time;
113 replay->cursor_frame = replay->tail;
114 }
115 }
116
117 f64 dir = t - replay->cursor;
118 if( dir == 0.0 ) return;
119 dir = vg_signf( dir );
120
121 u32 i=4096;
122 while( i --> 0 ){
123 if( dir < 0.0 )
124 if( t > replay->cursor_frame->time ) break;
125
126 replay_frame *next;
127 if( dir > 0.0 ) next = replay->cursor_frame->r;
128 else next = replay->cursor_frame->l;
129
130 if( !next ) break;
131
132 if( dir > 0.0 )
133 if( t < next->time ) break;
134
135 replay->cursor_frame = next;
136 replay->cursor = next->time;
137
138 if( !i ) return;
139 }
140
141 replay->cursor = t;
142 }
143
144 VG_STATIC replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){
145 replay_frame *frame = replay->cursor_frame;
146
147 u32 i=4096;
148 while( i --> 0 ){
149 if( !frame ) return frame;
150 if( frame->gamestate_count ) return frame;
151 frame = frame->l;
152 }
153
154 return NULL;
155 }
156
157 VG_STATIC void replay_get_camera( replay_buffer *replay, camera *cam ){
158 cam->nearz = 0.1f;
159 cam->farz = 100.0f;
160 if( replay->cursor_frame ){
161 /* TODO: frame lerp */
162 cam->fov = replay->cursor_frame->cam_fov;
163 v3_copy( replay->cursor_frame->cam_pos, cam->pos );
164 v3_copy( replay->cursor_frame->cam_angles, cam->angles );
165 }
166 else {
167 v3_zero( cam->pos );
168 v3_zero( cam->angles );
169 cam->fov = 90.0f;
170 }
171 }
172
173 VG_STATIC void skaterift_replay_pre_update(void){
174 if( skaterift.activity != k_skaterift_replay ) return;
175
176 f64 speed = 1.0;
177 f64 target = skaterift.replay.cursor;
178
179 if( vg_getkey( SDLK_9 ) ){
180 target -= vg.time_frame_delta * speed;
181 skaterift.replay_control = k_replay_control_scrub;
182 replay_seek( &skaterift.replay, target );
183 }
184 if( vg_getkey( SDLK_0 ) ){
185 target += vg.time_frame_delta * speed;
186 skaterift.replay_control = k_replay_control_scrub;
187 replay_seek( &skaterift.replay, target );
188 }
189
190 if( vg_getkey( SDLK_7 ) )
191 skaterift.replay_control = k_replay_control_play;
192
193 if( skaterift.replay_control == k_replay_control_play ){
194 target += vg.time_frame_delta;
195 replay_seek( &skaterift.replay, target );
196 }
197
198 if( vg_getkey( SDLK_8 ) ){
199 replay_frame *prev = replay_find_recent_stateframe( &skaterift.replay );
200
201 if( prev ){
202 /* TODO: Make gamestate_apply function / swap ... */
203 replay_gamestate *gs = replay_frame_gamestate( prev, 0 );
204
205 if( gs->system == k_player_subsystem_walk ){
206 memcpy( &localplayer._walk.state, &gs->walk,
207 sizeof(struct player_walk_state) );
208 }
209 else if( gs->system == k_player_subsystem_skate ){
210 memcpy( &localplayer._skate.state, &gs->skate,
211 sizeof(struct player_skate_state) );
212 }
213 localplayer.subsystem = gs->system;
214
215 memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) );
216 v3_copy( gs->angles, localplayer.angles );
217
218 v3_copy( prev->cam_pos, localplayer.cam.pos );
219 v3_copy( prev->cam_angles, localplayer.cam.angles );
220 localplayer.cam.fov = prev->cam_fov;
221
222 memcpy( &localplayer.cam_control, &gs->cam_control,
223 sizeof(struct player_cam_controller) );
224
225 /* chop end off replay */
226 prev->r = NULL;
227 skaterift.replay.statehead = prev;
228 skaterift.replay.head = prev;
229 skaterift.replay.cursor_frame = prev;
230 skaterift.replay.cursor = prev->time;
231 skaterift.replay_control = k_replay_control_scrub;
232 skaterift.activity = k_skaterift_default;
233 vg.time = prev->time;
234 return;
235 }
236 }
237 }
238
239 VG_STATIC void skaterift_replay_debug_info(void){
240 player__debugtext( 2, "replay info" );
241
242 replay_buffer *replay = &skaterift.replay;
243
244 u32 head = 0,
245 tail = 0;
246 if( replay->tail ) tail = (void *)replay->tail - replay->data;
247 if( replay->head ) head = (void *)replay->head - replay->data;
248
249 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
250
251 if( replay->statehead ){
252 u32 state = (void *)replay->statehead - replay->data;
253 player__debugtext( 1, "gs @%u\n", state );
254 }
255 else
256 player__debugtext( 1, "gs @NULL\n" );
257
258 f64 start = replay->cursor,
259 end = replay->cursor;
260 if( replay->tail ) start = replay->tail->time;
261 if( replay->head ) end = replay->head->time;
262
263 f64 cur = replay->cursor - start,
264 len = end - start;
265
266 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
267 }
268
269 VG_STATIC void skaterift_replay_imgui(void){
270 if( skaterift.activity != k_skaterift_replay ) return;
271
272 replay_buffer *replay = &skaterift.replay;
273 f64 start = replay->cursor,
274 end = replay->cursor;
275 if( replay->tail ) start = replay->tail->time;
276 if( replay->head ) end = replay->head->time;
277 f64 len = end - start,
278 cur = (replay->cursor - start) / len;
279
280 char buffer[ 128 ];
281
282 /* mainbar */
283 ui_px height = 20,
284 cwidth = 2;
285 ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
286 ui_fill( bar, ui_colour( k_ui_bg ) );
287
288 /* cursor frame block */
289 if( replay->cursor_frame ){
290 if( replay->cursor_frame->r ){
291 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
292 s = (replay->cursor_frame->time - start) / len;
293 ui_rect box = { s*(f64)vg.window_x, bar[1]-2,
294 VG_MAX(4,(ui_px)(l*vg.window_x)), bar[3]+2 };
295 ui_fill( box, ui_colour( k_ui_bg+4 ) );
296 }
297 }
298
299 /* cursor */
300 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
301 cwidth, bar[3] };
302 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
303
304 /* latest state marker */
305 if( replay->statehead ){
306 f64 t = (replay->statehead->time - start) / len;
307 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
308 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
309 }
310
311 /* previous state marker */
312 replay_frame *prev = replay_find_recent_stateframe( replay );
313 if( prev ){
314 f64 t = (prev->time - start) / len;
315 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
316 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
317 }
318
319 cusor[1] -= height;
320 cusor[2] = 200;
321 snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
322 ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
323
324 snprintf( buffer, 128, "-%.2fs\n", len );
325 ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
326 ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
327 }
328
329 #endif /* PLAYER_REPLAY_C */