X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=player.h;h=0e92f3aae5d6f5038bcd7703adb0c046c731844a;hb=2a238d32da833812e837cf38e16a7685c98db5c3;hp=2c6ee19ef51b94e21e9df556a6ce0bb6a51f2d4c;hpb=a98ce96041b248580c0447bf87d4c6106483cade;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/player.h b/player.h index 2c6ee19..0e92f3a 100644 --- a/player.h +++ b/player.h @@ -2,22 +2,32 @@ * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved */ +/* + * TODO: Tilt camera down to face borde when its behind you or out of vision + */ + #ifndef PLAYER_H #define PLAYER_H +#define PLAYER_REWIND_FRAMES 60*4 + +#include "conf.h" #include "audio.h" #include "common.h" #include "world.h" #include "skeleton.h" #include "bvh.h" -static float - k_walkspeed = 7.0f, /* no longer used */ - k_runspeed = 14.0f, +VG_STATIC float + k_walkspeed = 12.0f, + k_air_accelerate = 20.0f, + + k_runspeed = 20.0f, k_board_radius = 0.3f, k_board_length = 0.45f, k_board_allowance = 0.04f, - k_friction_lat = 8.8f, + //k_friction_lat = 8.8f, + k_friction_lat = 12.0f, k_friction_resistance = 0.01f, k_max_push_speed = 16.0f, k_push_accel = 10.0f, @@ -26,17 +36,19 @@ static float k_steer_air = 3.6f, k_steer_air_lerp = 0.3f, k_pump_force = 0.0f, - k_downforce = 5.0f, + k_downforce = 8.0f, + k_walk_downforce = 8.0f, k_jump_charge_speed = (1.0f/1.0f), k_jump_force = 5.0f, k_pitch_limit = 1.5f, k_look_speed = 2.0f, - k_walk_accel = 5.0f, + k_walk_accel = 150.0f, k_walk_friction = 8.0f; -static int freecam = 0; -static int walk_grid_iterations = 1; -static float fc_speed = 10.0f; +VG_STATIC int freecam = 0; +VG_STATIC int walk_grid_iterations = 1; +VG_STATIC float fc_speed = 10.0f; +VG_STATIC int cl_thirdperson = 0; /* * ----------------------------------------------------------------------------- @@ -44,7 +56,7 @@ static float fc_speed = 10.0f; * ----------------------------------------------------------------------------- */ -static struct gplayer +VG_STATIC struct gplayer { /* Physics */ rigidbody collide_front, collide_back; @@ -57,34 +69,82 @@ static struct gplayer v3f a, v_last, m, bob, vl; /* Utility */ - float vswitch, slip, slip_last, - reverse; + float vswitch, slip, slip_last, reverse; + float grab, jump, pushing, push_time, rise; + v2f grab_mouse_delta; + + int lift_frames; - float grab, jump, pushing, push_time; double start_push; - int in_air, on_board, jump_charge, jump_dir; + int in_air, on_board, jump_charge, jump_dir, grind; m3x3f vr,vr_pstep; } phys, phys_gate_frame; - int is_dead; + m4x3f visual_transform, + inv_visual_transform; + + int is_dead, death_tick_allowance, rewinding; + int rewind_sound_wait; - v3f land_target; - v3f land_target_log[22]; - u32 land_target_colours[22]; - int land_log_count; + struct land_prediction + { + v3f log[50]; + v3f n; + u32 log_length; + float score; + + enum prediction_type + { + k_prediction_none, + k_prediction_land, + k_prediction_grind + } + type; + + u32 colour; + } + predictions[22]; + u32 prediction_count; v3f handl_target, handr_target, handl, handr; + + /* Input */ + struct input_binding *input_js1h, + *input_js1v, + *input_js2h, + *input_js2v, + *input_jump, + *input_push, + *input_walk, + *input_walkh, + *input_walkv, + *input_switch_mode, + *input_reset, + *input_grab; /* Camera */ float air_blend; + float air_time; v3f camera_pos, smooth_localcam; v2f angles; - m4x3f camera, camera_inverse; + + struct rewind_frame + { + v3f pos; + v2f ang; + } + *rewind_buffer; + u32 rewind_incrementer, + rewind_length; + + float rewind_time, rewind_total_length, rewind_predicted_time; + double diag_rewind_start, diag_rewind_time; + float dist_accum; /* animation */ double jump_time; @@ -97,14 +157,20 @@ static struct gplayer fsetup, walk_timer, fjump, - fonboard; + fonboard, + frun, + fgrind; + float walk; int step_phase; + enum mdl_surface_prop surface_prop; /* player model */ struct player_model { - glmesh mesh; + glmesh player_meshes[3]; + + mdl_context meta; struct skeleton sk; struct skeleton_anim *anim_stand, *anim_highg, @@ -113,7 +179,8 @@ static struct gplayer *anim_push, *anim_push_reverse, *anim_ollie, *anim_ollie_reverse, *anim_grabs, *anim_stop, - *anim_walk, *anim_run, *anim_idle; + *anim_walk, *anim_run, *anim_idle, + *anim_jump; u32 id_hip, id_ik_hand_l, @@ -127,17 +194,28 @@ static struct gplayer struct ragdoll_part { u32 bone_id; - v3f offset; + //v3f offset; + + /* Collider transform relative to bone */ + m4x3f collider_mtx, + inv_collider_mtx; u32 use_limits; v3f limits[2]; rigidbody rb; u32 parent; + u32 colour; } - *ragdoll; + ragdoll[32]; u32 ragdoll_count; + rb_constr_pos position_constraints[32]; + u32 position_constraints_count; + + rb_constr_swingtwist cone_constraints[32]; + u32 cone_constraints_count; + int shoes[2]; } mdl; @@ -151,15 +229,18 @@ player = /* * API */ -static float *player_get_pos(void); -static void player_kill(void); -static float *player_cam_pos(void); -static void player_save_frame(void); -static void player_restore_frame(void); +VG_STATIC float *player_get_pos(void); +VG_STATIC void player_kill(void); +VG_STATIC float *player_cam_pos(void); +VG_STATIC void player_save_frame(void); +VG_STATIC void player_restore_frame(void); +VG_STATIC void player_save_rewind_frame(void); /* * Submodules */ +VG_STATIC void player_mouseview(void); + #include "player_physics.h" #include "player_ragdoll.h" #include "player_model.h" @@ -171,19 +252,89 @@ static void player_restore_frame(void); * Events * ----------------------------------------------------------------------------- */ +VG_STATIC int kill_player( int argc, char const *argv[] ); +VG_STATIC int reset_player( int argc, char const *argv[] ); -static void player_init(void) /* 1 */ +VG_STATIC void player_init(void) /* 1 */ { + player.input_js1h = vg_create_named_input( "steer-h", k_input_type_axis ); + player.input_js1v = vg_create_named_input( "steer-v", k_input_type_axis ); + player.input_grab = vg_create_named_input( "grab", k_input_type_axis_norm ); + player.input_js2h = vg_create_named_input( "grab-h", k_input_type_axis ); + player.input_js2v = vg_create_named_input( "grab-v", k_input_type_axis ); + player.input_jump = vg_create_named_input( "jump", k_input_type_button ); + player.input_push = vg_create_named_input( "push", k_input_type_button ); + player.input_walk = vg_create_named_input( "walk", k_input_type_button ); + + player.input_walkh = vg_create_named_input( "walk-h", + k_input_type_axis ); + player.input_walkv = vg_create_named_input( "walk-v", + k_input_type_axis ); + + + player.input_switch_mode = vg_create_named_input( "switch-mode", + k_input_type_button ); + player.input_reset = vg_create_named_input( "reset", k_input_type_button ); + + const char *default_cfg[] = + { + "bind steer-h gp-ls-h", + "bind -steer-h a", + "bind +steer-h d", + + "bind steer-v gp-ls-v", + "bind -steer-v w", + "bind +steer-v s", + + "bind grab gp-rt", + "bind +grab shift", + "bind grab-h gp-rs-h", + "bind grab-v gp-rs-v", + + "bind jump space", + "bind jump gp-a", + + "bind push gp-b", + "bind push w", + + "bind walk shift", + "bind walk gp-ls", + + "bind walk-h gp-ls-h", + "bind walk-v -gp-ls-v", + "bind +walk-h d", + "bind -walk-h a", + "bind +walk-v w", + "bind -walk-v s", + + "bind reset gp-lb", + "bind reset r", + + "bind switch-mode gp-y", + "bind switch-mode e", + }; + + for( int i=0; iang ); + v3_copy( player.camera_pos, fr->pos ); + + player.rewind_incrementer = 0; + + if( player.rewind_length > 1 ) + { + player.rewind_total_length += + v3_dist( player.rewind_buffer[player.rewind_length-1].pos, + player.rewind_buffer[player.rewind_length-2].pos ); + } + } +} + + +/* disaster */ +VG_STATIC int menu_enabled(void); +#include "menu.h" + +/* + * Free camera movement + */ +VG_STATIC void player_mouseview(void) +{ + if( menu_enabled() ) + return; + + v2_muladds( player.angles, vg.mouse_delta, 0.0025f, player.angles ); + + if( vg_input.controller_should_use_trackpad_look ) + { + static v2f last_input; + static v2f vel; + static v2f vel_smooth; + + v2f input = { player.input_js2h->axis.value, + player.input_js2v->axis.value }; + + if( (v2_length2(last_input) > 0.001f) && (v2_length2(input) > 0.001f) ) + { + v2_sub( input, last_input, vel ); + v2_muls( vel, 1.0f/vg.time_delta, vel ); + } + else + { + v2_zero( vel ); + } + + v2_lerp( vel_smooth, vel, vg.time_delta*8.0f, vel_smooth ); + + v2_muladds( player.angles, vel_smooth, vg.time_delta, player.angles ); + v2_copy( input, last_input ); + } + else + { + player.angles[0] += player.input_js2h->axis.value * vg.time_delta * 4.0f; + player.angles[1] += player.input_js2v->axis.value * vg.time_delta * 4.0f; + } + + player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f ); } /* Deal with input etc */ -static void player_update_pre(void) +VG_STATIC void player_update_pre(void) { struct player_phys *phys = &player.phys; - if( vg_get_axis("grabl")>0.0f) + { - player.is_dead = 0; - player_restore_frame(); + v3f ra, rb, rx; + v3_copy( main_camera.pos, ra ); + v3_muladds( ra, main_camera.transform[2], -10.0f, rb ); - if( !phys->on_board ) + float t; + if( spherecast_world( ra, rb, 0.4f, &t, rx ) != -1 ) { - player.angles[0] = atan2f( -phys->rb.forward[2], - -phys->rb.forward[0] ); + m4x3f mtx; + m3x3_identity( mtx ); + v3_lerp( ra, rb, t, mtx[3] ); + + debug_sphere( mtx, 0.4f, 0xff00ff00 ); + + v3f x1; + v3_muladds( mtx[3], rx, 0.4f, x1 ); + vg_line( mtx[3], x1, 0xffffffff ); } + } + + + + + + - player.mdl.shoes[0] = 1; - player.mdl.shoes[1] = 1; - world_routes_notify_reset(); + if( player.rewinding ) + return; + + if( vg_input_button_down( player.input_reset ) && !menu_enabled() ) + { + double delta = world.time - world.last_use; + + if( (delta <= RESET_MAX_TIME) && (world.last_use != 0.0) ) + { + player.rewinding = 1; + player.rewind_sound_wait = 1; + player.rewind_time = (float)player.rewind_length - 0.0001f; + player_save_rewind_frame(); + audio_lock(); + audio_play_oneshot( &audio_rewind[0], 1.0f ); + audio_unlock(); + + /* based on analytical testing. DONT CHANGE! + * + * time taken: y = (x^(4/5)) * 74.5 + * inverse : x = (2/149)^(4/5) * y^(4/5) + */ + + float constant = powf( 2.0f/149.0f, 4.0f/5.0f ), + curve = powf( player.rewind_total_length, 4.0f/5.0f ); + + player.rewind_predicted_time = constant * curve; + player.diag_rewind_start = vg.time; + player.diag_rewind_time = player.rewind_time; + + player.is_dead = 0; + player.death_tick_allowance = 30; + player_restore_frame(); + + if( !phys->on_board ) + { + player.angles[0] = atan2f( -phys->rb.forward[2], + -phys->rb.forward[0] ); + } + + player.mdl.shoes[0] = 1; + player.mdl.shoes[1] = 1; + + world_routes_notify_reset(); + + /* apply 1 frame of movement */ + player_do_motion(); + } + else + { + if( player.is_dead ) + { + reset_player( 0, NULL ); + } + else + { + /* cant do that */ + audio_lock(); + audio_play_oneshot( &audio_rewind[4], 1.0f ); + audio_unlock(); + } + } } - if( vg_get_button_down( "switchmode" ) ) + if( vg_input_button_down( player.input_switch_mode ) && !menu_enabled() ) { phys->on_board ^= 0x1; + + audio_lock(); + if( phys->on_board ) + { + v3_muladds( phys->rb.v, phys->rb.forward, 0.2f, phys->rb.v ); + audio_play_oneshot( &audio_lands[6], 1.0f ); + } + else + { + audio_play_oneshot( &audio_lands[5], 1.0f ); + } + + audio_unlock(); } + + if( !phys->on_board ) + player_mouseview(); } -static void player_update_fixed(void) /* 2 */ +VG_STATIC void player_update_fixed(void) /* 2 */ { + if( player.rewinding ) + return; + + if( player.death_tick_allowance ) + player.death_tick_allowance --; + struct player_phys *phys = &player.phys; if( player.is_dead ) @@ -265,20 +637,32 @@ static void player_update_fixed(void) /* 2 */ } else { + player.rewind_incrementer ++; + + if( player.rewind_incrementer > (u32)(0.25/VG_TIMESTEP_FIXED) ) + { + player_save_rewind_frame(); + } + player_do_motion(); } - - player_audio(); /* FUTURE: can probably move this to post() - BUT, it uses deltas from fixed step physics, - AND this *might* be what we want for realtime - audio, anyway. */ } -static void player_update_post(void) +VG_STATIC void player_update_post(void) { - for( int i=0; ilog_length - 1; j ++ ) + vg_line( p->log[j], p->log[j+1], p->colour ); + + vg_line_cross( p->log[p->log_length-1], p->colour, 0.25f ); + + v3f p1; + v3_add( p->log[p->log_length-1], p->n, p1 ); + vg_line( p->log[p->log_length-1], p1, 0xffffffff ); + } if( player.is_dead ) { @@ -292,16 +676,141 @@ static void player_update_post(void) player_animate(); if( !freecam ) - player_animate_camera(); + { + if( cl_thirdperson ) + player_animate_camera_thirdperson(); + else + player_animate_camera(); + } } if( freecam ) player_freecam(); - player_camera_update(); + /* CAMERA POSITIONING: LAYER 0 */ + v2_copy( player.angles, main_camera.angles ); + v3_copy( player.camera_pos, main_camera.pos ); + + if( player.rewinding ) + { + if( player.rewind_time <= 0.0f ) + { + double taken = vg.time - player.diag_rewind_start; + vg_success( "Rewind took (rt, pl, tl): %f, %f, %f\n", + taken, player.diag_rewind_time, + player.rewind_total_length ); + + player.rewinding = 0; + player.rewind_length = 1; + player.rewind_total_length = 0.0f; + player.rewind_incrementer = 0; + world.sky_target_rate = 1.0; + } + else + { + world.sky_target_rate = -100.0; + assert( player.rewind_length > 0 ); + + v2f override_angles; + v3f override_pos; + + float budget = vg.time_delta, + overall_length = player.rewind_length; + + world_routes_rollback_time( player.rewind_time / overall_length ); + + for( int i=0; (i<10)&&(player.rewind_time>0.0f)&&(budget>0.0f); i ++ ) + { + /* Interpolate frames */ + int i0 = floorf( player.rewind_time ), + i1 = VG_MIN( i0+1, player.rewind_length-1 ); + + struct rewind_frame *fr = &player.rewind_buffer[i0], + *fr1 = &player.rewind_buffer[i1]; + + float dist = vg_maxf( v3_dist( fr->pos, fr1->pos ), 0.001f ), + subl = vg_fractf( player.rewind_time ) + 0.001f, + + sramp= 3.0f-(1.0f/(0.4f+0.4f*player.rewind_time)), + speed = sramp*28.0f + 0.5f*player.rewind_time, + mod = speed * (budget / dist), + + advl = vg_minf( mod, subl ), + advt = (advl / mod) * budget; + + player.dist_accum += speed * advt; + player.rewind_time -= advl; + budget -= advt; + } + + player.rewind_time = vg_maxf( 0.0f, player.rewind_time ); + + float current_time = vg.time - player.diag_rewind_start, + remaining = player.rewind_predicted_time - current_time; + + if( player.rewind_sound_wait ) + { + if( player.rewind_predicted_time >= 6.5f ) + { + if( remaining <= 6.5f ) + { + audio_lock(); + audio_play_oneshot( &audio_rewind[3], 1.0f ); + audio_unlock(); + player.rewind_sound_wait = 0; + } + } + else if( player.rewind_predicted_time >= 2.5f ) + { + if( remaining <= 2.5f ) + { + audio_lock(); + audio_play_oneshot( &audio_rewind[2], 1.0f ); + audio_unlock(); + player.rewind_sound_wait = 0; + } + } + else if( player.rewind_predicted_time >= 1.5f ) + { + if( remaining <= 1.5f ) + { + audio_lock(); + audio_play_oneshot( &audio_rewind[1], 1.0f ); + audio_unlock(); + player.rewind_sound_wait = 0; + } + } + } + + int i0 = floorf( player.rewind_time ), + i1 = VG_MIN( i0+1, player.rewind_length-1 ); + + struct rewind_frame *fr = &player.rewind_buffer[i0], + *fr1 = &player.rewind_buffer[i1]; + + float sub = vg_fractf(player.rewind_time); + + v3_lerp( fr->pos, fr1->pos, sub, override_pos ); + override_angles[0] = vg_alerpf( fr->ang[0], fr1->ang[0], sub ); + override_angles[1] = vg_lerpf ( fr->ang[1], fr1->ang[1], sub ); + + /* CAMERA POSITIONING: LAYER 1 */ + float blend = (4.0f-player.rewind_time) * 0.25f, + c = vg_clampf( blend, 0.0f, 1.0f ); + + main_camera.angles[0] = + vg_alerpf(override_angles[0], player.angles[0], c); + main_camera.angles[1] = + vg_lerpf (override_angles[1], player.angles[1], c); + v3_lerp( override_pos, player.camera_pos, c, main_camera.pos ); + } + } + + camera_update_transform( &main_camera ); + player_audio(); } -static void draw_player(void) /* 3 */ +VG_STATIC void draw_player( camera *cam ) { if( player.is_dead ) player_model_copy_ragdoll(); @@ -309,16 +818,16 @@ static void draw_player(void) /* 3 */ shader_viewchar_use(); vg_tex2d_bind( &tex_characters, 0 ); shader_viewchar_uTexMain( 0 ); - shader_viewchar_uCamera( player.camera[3] ); - shader_viewchar_uPv( vg.pv ); + shader_viewchar_uCamera( cam->transform[3] ); + shader_viewchar_uPv( cam->mtx.pv ); shader_link_standard_ub( _shader_viewchar.id, 2 ); glUniformMatrix4x3fv( _uniform_viewchar_uTransforms, player.mdl.sk.bone_count, 0, (float *)player.mdl.sk.final_mtx ); - mesh_bind( &player.mdl.mesh ); - mesh_draw( &player.mdl.mesh ); + mesh_bind( &player.mdl.player_meshes[cl_playermdl_id] ); + mesh_draw( &player.mdl.player_meshes[cl_playermdl_id] ); } /* @@ -327,18 +836,21 @@ static void draw_player(void) /* 3 */ * ----------------------------------------------------------------------------- */ -static float *player_get_pos(void) +VG_STATIC float *player_get_pos(void) { return player.phys.rb.co; } -static void player_kill(void) +VG_STATIC void player_kill(void) { - player.is_dead = 1; - player_ragdoll_copy_model( player.phys.rb.v ); + if( player.death_tick_allowance == 0 ) + { + player.is_dead = 1; + player_ragdoll_copy_model( player.phys.rb.v ); + } } -static float *player_cam_pos(void) +VG_STATIC float *player_cam_pos(void) { return player.camera_pos; }