fix long standing grind bug
[carveJwlIkooP6JGAAIwe30JlM.git] / player_skate.c
index bec54539ecd625135513fd136df79ad731a8f117..87c8cba7483e9ea22e07e042b967e34189eb4309 100644 (file)
@@ -4,6 +4,8 @@
 #include "player.h"
 #include "audio.h"
 #include "vg/vg_perlin.h"
+#include "menu.h"
+#include "ent_skateshop.h"
 
 VG_STATIC void player__skate_bind( player_instance *player )
 {
@@ -53,7 +55,7 @@ VG_STATIC int skate_collide_smooth( player_instance *player,
                                     m4x3f mtx, rb_sphere *sphere,
                                     rb_ct *man )
 {
-   world_instance *world = get_active_world();
+   world_instance *world = world_current_instance();
 
    int len = 0;
    len = rb_sphere__scene( mtx, sphere, NULL, &world->rb_geo.inf.scene, man );
@@ -90,7 +92,7 @@ VG_STATIC int skate_grind_scansq( player_instance *player,
                                   v3f pos, v3f dir, float r,
                                   struct grind_info *inf )
 {
-   world_instance *world = get_active_world();
+   world_instance *world = world_current_instance();
 
    v4f plane;
    v3_copy( dir, plane );
@@ -100,13 +102,8 @@ VG_STATIC int skate_grind_scansq( player_instance *player,
    boxf box;
    v3_add( pos, (v3f){ r, r, r }, box[1] );
    v3_sub( pos, (v3f){ r, r, r }, box[0] );
-   
-   bh_iter it;
-   bh_iter_init( 0, &it );
-   int idx;
 
-   struct grind_sample
-   {
+   struct grind_sample{
       v2f co;
       v2f normal;
       v3f normal3,
@@ -122,18 +119,20 @@ VG_STATIC int skate_grind_scansq( player_instance *player,
    v3_cross( plane, player->basis[1], support_axis );
    v3_normalize( support_axis );
    
-   while( bh_next( world->geo_bh, &it, box, &idx ) ){
-      u32 *ptri = &world->scene_geo->arrindices[ idx*3 ];
+   bh_iter it;
+   bh_iter_init_box( 0, &it, box );
+   i32 idx;
+   
+   while( bh_next( world->geo_bh, &it, &idx ) ){
+      u32 *ptri = &world->scene_geo.arrindices[ idx*3 ];
       v3f tri[3];
 
       struct world_surface *surf = world_tri_index_surface(world,ptri[0]);
-#if 0
-      if( !(surf->info.flags & k_material_flag_skate_surface) )
+      if( !(surf->info.flags & k_material_flag_grindable) )
          continue;
-#endif
 
       for( int j=0; j<3; j++ )
-         v3_copy( world->scene_geo->arrvertices[ptri[j]].co, tri[j] );
+         v3_copy( world->scene_geo.arrvertices[ptri[j]].co, tri[j] );
 
       for( int j=0; j<3; j++ ){
          int i0 = j,
@@ -234,10 +233,8 @@ too_many_samples:
          float yi = v3_dot( player->basis[1], si->normal3 ),
                yj = v3_dot( player->basis[1], sj->normal3 );
 
-         if( yi > yj )
-            v3_add( si->normal3, average_normal, average_normal );
-         else
-            v3_add( sj->normal3, average_normal, average_normal );
+         if( yi > yj ) v3_add( si->normal3, average_normal, average_normal );
+         else          v3_add( sj->normal3, average_normal, average_normal );
 
          passed_samples ++;
       }
@@ -348,7 +345,7 @@ VG_STATIC int create_jumps_to_hit_target( player_instance *player,
 VG_STATIC 
 void player__approximate_best_trajectory( player_instance *player )
 {
-   world_instance *world = get_active_world();
+   world_instance *world0 = world_current_instance();
 
    struct player_skate *s = &player->_skate;
    float k_trace_delta = k_rb_delta * 10.0f;
@@ -393,6 +390,7 @@ void player__approximate_best_trajectory( player_instance *player )
       v3_copy( player->rb.co, launch_co );
       v3_copy( player->rb.v,  launch_v );
       v3_copy( launch_co, co0 );
+      world_instance *trace_world = world0;
 
       float vt  = (float)m * (1.0f/30.0f),
             ang = vg_lerpf( angle_begin, angle_end, vt ) * 0.15f;
@@ -430,10 +428,9 @@ void player__approximate_best_trajectory( player_instance *player )
 
          /* REFACTOR */
 
-         v3f closest;
+         v3f closest={0.0f,0.0f,0.0f};
          if( search_for_grind ){
-            if( bh_closest_point( world->geo_bh, co1, closest, 1.0f ) != -1 ){
-
+            if( bh_closest_point(trace_world->geo_bh,co1,closest,1.0f) != -1 ){
                float min_dist = 0.75f;
                      min_dist *= min_dist;
 
@@ -492,14 +489,18 @@ void player__approximate_best_trajectory( player_instance *player )
             }
          }
 
-         if( world->rendering_gate ){
-            ent_gate *gate = world->rendering_gate;
+         if( trace_world->rendering_gate ){
+            ent_gate *gate = trace_world->rendering_gate;
             if( gate_intersect( gate, co1, co0 ) ){
                m4x3_mulv( gate->transport, co0, co0 );
                m4x3_mulv( gate->transport, co1, co1 );
                m3x3_mulv( gate->transport, launch_v, launch_v);
                m4x3_mulv( gate->transport, launch_co, launch_co );
                m3x3_mul( gate->transport, basis, basis );
+
+               if( gate->type == k_gate_type_nonlocel ){
+                  trace_world = &world_static.worlds[ gate->target ];
+               }
             }
          }
 
@@ -509,15 +510,16 @@ void player__approximate_best_trajectory( player_instance *player )
          float scan_radius = k_board_radius;
                scan_radius *= vg_clampf( t, 0.02f, 1.0f );
 
-         int idx = spherecast_world( world, co0, co1, scan_radius, &t1, n );
+         int idx = spherecast_world(trace_world, co0, co1, scan_radius, &t1, n);
          if( idx != -1 ){
             v3f co;
             v3_lerp( co0, co1, t1, co );
             v3_copy( co, inf->log[ inf->log_length ++ ] ); 
 
             v3_copy( n, inf->n );
-            u32 *tri = &world->scene_geo->arrindices[ idx*3 ];
-            struct world_surface *surf = world_tri_index_surface(world, tri[0]);
+            u32 *tri = &trace_world->scene_geo.arrindices[ idx*3 ];
+            struct world_surface *surf = 
+               world_tri_index_surface( trace_world, tri[0] );
 
             inf->type = k_prediction_land;
 
@@ -528,11 +530,13 @@ void player__approximate_best_trajectory( player_instance *player )
             inf->score = -v3_dot( ve, inf->n );
             inf->land_dist = t + k_trace_delta * t1;
 
-            
             /* Bias prediction towords ramps */
-            if( !(surf->info.flags & k_material_flag_skate_surface) )
+            if( !(surf->info.flags & k_material_flag_skate_target) )
                inf->score *= 10.0f;
 
+            if( surf->info.flags & k_material_flag_boundary )
+               s->possible_jump_count --;
+
             break;
          }
          
@@ -579,8 +583,6 @@ void player__approximate_best_trajectory( player_instance *player )
          v3_add( launch_co, co0, co0 );
 
          /* rough scan to make sure we dont collide with anything */
-         /* NOTE this was rarely needed and ends up with false negatives. */
-#if 0
          for( int j=1; j<=16; j++ ){
             t  = (float)j*(1.0f/16.0f);
             t *= 0.9f;
@@ -594,15 +596,14 @@ void player__approximate_best_trajectory( player_instance *player )
             float t1;
             v3f n;
 
-            int idx = spherecast_world( world, co0,co1,
-                                        k_board_radius*0.5f, &t1, n);
+            int idx = spherecast_world( world0, co0,co1,
+                                        k_board_radius*0.1f, &t1, n);
             if( idx != -1 ){
                goto invalidated_grind;
             }
 
             v3_copy( co1, co0 );
          }
-#endif
 
          v3_copy( grind.n, jump->n );
 
@@ -614,10 +615,8 @@ void player__approximate_best_trajectory( player_instance *player )
 
          s->possible_jumps[ s->possible_jump_count ++ ] = *jump;
 
-#if 0
          continue;
 invalidated_grind:;
-#endif
       }
    }
 
@@ -661,15 +660,16 @@ invalidated_grind:;
       v3_copy( best->v, player->rb.v );
       s->land_dist = best->land_dist;
 
-      v2f steer = { player->input_js1h->axis.value,
-                    player->input_js1v->axis.value };
-      v2_normalize_clamp( steer );
       s->state.gravity_bias = best->gravity;
 
       if( best->type == k_prediction_grind ){
          s->state.activity = k_skate_activity_air_to_grind;
       }
 
+      v2f steer;
+      joystick_state( k_srjoystick_steer, steer );
+      v2_normalize_clamp( steer );
+
       if( (fabsf(steer[1]) > 0.5f) && (s->land_dist >= 1.5f) ){
          s->state.flip_rate = (1.0f/s->land_dist) * vg_signf(steer[1]) *
                                  s->state.reverse ;
@@ -711,10 +711,6 @@ VG_STATIC void skate_apply_air_model( player_instance *player )
    q_axis_angle( correction, axis, 
                   acosf(angle)*2.0f*VG_TIMESTEP_FIXED );
    q_mul( correction, player->rb.q, player->rb.q );
-
-   v2f steer = { player->input_js1h->axis.value,
-                 player->input_js1v->axis.value };
-   v2_normalize_clamp( steer );
 }
 
 VG_STATIC int player_skate_trick_input( player_instance *player );
@@ -792,7 +788,7 @@ VG_STATIC void skate_apply_grab_model( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
 
-   float grabt = player->input_grab->axis.value;
+   float grabt = axis_state( k_sraxis_grab );
 
    if( grabt > 0.5f ){
       v2_muladds( s->state.grab_mouse_delta, vg.mouse_delta, 0.02f, 
@@ -810,9 +806,12 @@ VG_STATIC void skate_apply_steering_model( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
 
+   v2f jsteer;
+   joystick_state( k_srjoystick_steer, jsteer );
+
    /* Steering */
-   float steer = player->input_js1h->axis.value,
-         grab  = player->input_grab->axis.value;
+   float steer = jsteer[0],
+         grab  = axis_state( k_sraxis_grab );
 
    steer = vg_signf( steer ) * steer*steer * k_steer_ground;
 
@@ -891,9 +890,8 @@ VG_STATIC void skate_apply_friction_model( player_instance *player )
 
    /* Pushing additive force */
 
-   if( !player->input_jump->button.value ){
-      if( player->input_push->button.value || 
-          (vg.time-s->state.start_push<0.75) )
+   if( !button_press( k_srbind_jump ) ){
+      if( button_press( k_srbind_push ) || (vg.time-s->state.start_push<0.75) )
       {
          if( (vg.time - s->state.cur_push) > 0.25 )
             s->state.start_push = vg.time;
@@ -921,7 +919,7 @@ VG_STATIC void skate_apply_jump_model( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
    int charging_jump_prev = s->state.charging_jump;
-   s->state.charging_jump = player->input_jump->button.value;
+   s->state.charging_jump = button_press( k_srbind_jump );
 
    /* Cannot charge this in air */
    if( s->state.activity <= k_skate_activity_air_to_grind ){
@@ -961,7 +959,10 @@ VG_STATIC void skate_apply_jump_model( player_instance *player )
          s->grind_cooldown = 30;
          s->state.activity = k_skate_activity_ground;
 
-         float tilt  = player->input_js1h->axis.value * 0.3f;
+         v2f steer;
+         joystick_state( k_srjoystick_steer, steer );
+
+         float tilt  = steer[0] * 0.3f;
                tilt *= vg_signf(v3_dot( player->rb.v, s->grind_dir ));
 
          v4f qtilt;
@@ -975,12 +976,8 @@ VG_STATIC void skate_apply_jump_model( player_instance *player )
       s->state.jump_charge = 0.0f;
       s->state.jump_time = vg.time;
 
-      v2f steer = { player->input_js1h->axis.value,
-                    player->input_js1v->axis.value };
-      v2_normalize_clamp( steer );
-
       audio_lock();
-      audio_oneshot_3d( &audio_jumps[rand()%2], player->rb.co, 40.0f, 1.0f );
+      audio_oneshot_3d( &audio_jumps[vg_randu32()%2], player->rb.co, 40.0f, 1.0f );
       audio_unlock();
    }
 }
@@ -996,7 +993,7 @@ VG_STATIC void skate_apply_pump_model( player_instance *player )
 
    /* Throw / collect routine 
     */
-   if( player->input_grab->axis.value > 0.5f ){
+   if( axis_state( k_sraxis_grab ) > 0.5f ){
       if( s->state.activity == k_skate_activity_ground ){
          /* Throw */
          v3_muls( player->rb.to_world[1], k_mmthrow_scale, s->state.throw_v );
@@ -1010,7 +1007,8 @@ VG_STATIC void skate_apply_pump_model( player_instance *player )
       v3_muladds( s->state.throw_v, player->rb.to_world[1], -doty, Fl);
 
       if( s->state.activity == k_skate_activity_ground ){
-         v3_muladds( player->rb.v,     Fl,  k_mmcollect_lat, player->rb.v );
+         if( v3_length2(player->rb.v)<(20.0f*20.0f) )
+            v3_muladds( player->rb.v,     Fl,  k_mmcollect_lat, player->rb.v );
          v3_muladds( s->state.throw_v, Fl, -k_mmcollect_lat, s->state.throw_v );
       }
 
@@ -1039,8 +1037,8 @@ VG_STATIC void skate_apply_cog_model( player_instance *player )
    v3_copy( s->state.up_dir, ideal_dir );
    v3_normalize( ideal_dir );
 
-   v3_muladds( player->rb.co, ideal_dir,
-               1.0f-player->input_grab->axis.value, ideal_cog );
+   float grab = axis_state( k_sraxis_grab );
+   v3_muladds( player->rb.co, ideal_dir, 1.0f-grab, ideal_cog );
    v3_sub( ideal_cog, s->state.cog, ideal_diff );
 
    /* Apply velocities */
@@ -1107,17 +1105,17 @@ VG_STATIC void skate_copy_holdout( player_instance *player )
 
 VG_STATIC int player_skate_trick_input( player_instance *player )
 {
-   return (player->input_trick0->button.value) |
-          (player->input_trick1->button.value << 1) |
-          (player->input_trick2->button.value << 1) |
-          (player->input_trick2->button.value);
+   return (button_press( k_srbind_trick0 )     ) |
+          (button_press( k_srbind_trick1 ) << 1) |
+          (button_press( k_srbind_trick2 ) << 1) |
+          (button_press( k_srbind_trick2 )     );
 }
 
 VG_STATIC void player__skate_pre_update( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
 
-   if( vg_input_button_down( player->input_use ) ){
+   if( button_down( k_srbind_use ) ){
       player->subsystem = k_player_subsystem_walk;
 
       v3f angles;
@@ -1161,7 +1159,7 @@ VG_STATIC void player__skate_post_update( player_instance *player )
       jump_info *jump = &s->possible_jumps[i];
 
       if( jump->log_length == 0 ){
-         vg_fatal_exit_loop( "assert: jump->log_length == 0\n" );
+         vg_fatal_error( "assert: jump->log_length == 0\n" );
       }
       
       for( int j=0; j<jump->log_length - 1; j ++ ){
@@ -1191,10 +1189,10 @@ VG_STATIC void player__skate_post_update( player_instance *player )
       slide = 0.0f;
    }
 
-   float
-         vol_main    = sqrtf( (1.0f-air)*attn*(1.0f-slide) * 0.4f ),
-         vol_air     = sqrtf(       air *attn * 0.5f ),
-         vol_slide   = sqrtf( (1.0f-air)*attn*slide * 0.25f );
+   f32 gate        = 1.0f-menu.factive,
+       vol_main    = sqrtf( (1.0f-air)*attn*(1.0f-slide) * 0.4f ) * gate,
+       vol_air     = sqrtf(       air *attn * 0.5f )              * gate,
+       vol_slide   = sqrtf( (1.0f-air)*attn*slide * 0.25f )       * gate;
 
    const u32 flags = AUDIO_FLAG_SPACIAL_3D|AUDIO_FLAG_LOOP;
 
@@ -1227,7 +1225,7 @@ VG_STATIC void player__skate_post_update( player_instance *player )
                           vg_lerpf( 250.0f, 80.0f, attn ) );
 
    if( s->sample_change_cooldown > 0.0f ){
-      s->sample_change_cooldown -= vg.frame_delta;
+      s->sample_change_cooldown -= vg.time_frame_delta;
    }
    else{
       int sample_type = k_skate_sample_concrete;
@@ -1319,7 +1317,7 @@ int skate_compute_surface_alignment( player_instance *player,
                                      v3f surface_normal, v3f axel_dir )
 {
    struct player_skate *s = &player->_skate;
-   world_instance *world = get_active_world();
+   world_instance *world = world_current_instance();
 
    v3f truck, left, right;
    m4x3_mulv( player->rb.to_world, ra, truck );
@@ -1399,11 +1397,11 @@ int skate_compute_surface_alignment( player_instance *player,
 
       if( idx != -1 )
       {
-         u32 *tri = &world->scene_geo->arrindices[ idx * 3 ];
+         u32 *tri = &world->scene_geo.arrindices[ idx * 3 ];
          v3f verts[3];
 
          for( int j=0; j<3; j++ )
-            v3_copy( world->scene_geo->arrvertices[ tri[j] ].co, verts[j] );
+            v3_copy( world->scene_geo.arrvertices[ tri[j] ].co, verts[j] );
 
          v3f vert0, vert1, n;
          v3_sub( verts[1], verts[0], vert0 );
@@ -1446,14 +1444,16 @@ VG_STATIC void skate_weight_distribute( player_instance *player )
 
    int reverse_dir = v3_dot( player->rb.to_world[2], player->rb.v ) < 0.0f?1:-1;
 
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
+
    if( s->state.manual_direction == 0 ){
-      if( (player->input_js1v->axis.value > 0.7f) && 
-          (s->state.activity == k_skate_activity_ground) &&
+      if( (steer[1] > 0.7f) && (s->state.activity == k_skate_activity_ground) &&
           (s->state.jump_charge <= 0.01f) )
          s->state.manual_direction = reverse_dir;
    }
    else{
-      if( player->input_js1v->axis.value < 0.1f ){
+      if( steer[1] < 0.1f ){
          s->state.manual_direction = 0;
       }
       else{
@@ -1464,7 +1464,7 @@ VG_STATIC void skate_weight_distribute( player_instance *player )
    }
 
    if( s->state.manual_direction ){
-      float amt = vg_minf( player->input_js1v->axis.value * 8.0f, 1.0f );
+      float amt = vg_minf( steer[1] * 8.0f, 1.0f );
       s->weight_distribution[2] = k_board_length * amt * 
                                           (float)s->state.manual_direction;
    }
@@ -1530,7 +1530,7 @@ VG_STATIC int skate_point_visible( v3f origin, v3f target )
    v3_muls( dir, 1.0f/ray.dist, dir );
    ray.dist -= 0.025f;
 
-   if( ray_world( get_active_world(), origin, dir, &ray ) )
+   if( ray_world( world_current_instance(), origin, dir, &ray ) )
       return 0;
 
    return 1;
@@ -1617,9 +1617,10 @@ VG_STATIC void skate_grind_truck_apply( player_instance *player,
    v3_normalize( target_fwd );
    v3_normalize( fwd );
 
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
 
-   float way = player->input_js1v->axis.value *
-                  vg_signf( v3_dot( raw_nplane, player->rb.v ) );
+   float way = steer[1] * vg_signf( v3_dot( raw_nplane, player->rb.v ) );
 
    v4f q;
    q_axis_angle( q, axis, VG_PIf*0.125f * way );
@@ -1662,6 +1663,10 @@ VG_STATIC void skate_5050_apply( player_instance *player,
    v3_muladds( inf_back->co, inf_avg.dir, 0.5f, inf_avg.co );
    v3_normalize( inf_avg.dir );
 
+   /* dont ask */
+   v3_muls( inf_avg.dir, vg_signf(v3_dot(inf_avg.dir,player->rb.v)), 
+            inf_avg.dir );
+
    v3f axis_front, axis_back, axis;
    v3_cross( inf_front->dir, inf_front->n, axis_front );
    v3_cross( inf_back->dir,  inf_back->n,  axis_back  );
@@ -1671,8 +1676,10 @@ VG_STATIC void skate_5050_apply( player_instance *player,
    v3_cross( axis, inf_avg.dir, inf_avg.n );
    skate_grind_decay( player, &inf_avg, 1.0f );
 
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
 
-   float way = player->input_js1v->axis.value *
+   float way = steer[1] *
                   vg_signf( v3_dot( player->rb.to_world[2], player->rb.v ) );
    v4f q;
    v3f up, target_up;
@@ -1688,6 +1695,8 @@ VG_STATIC void skate_5050_apply( player_instance *player,
                                     k_grind_spring, 
                                     k_grind_dampener,
                                     k_rb_delta );
+   vg_line_arrow( player->rb.co, up, 1.0f, VG__GREEN );
+   vg_line_arrow( player->rb.co, target_up, 1.0f, VG__GREEN );
 
    v3f fwd_nplane, dir_nplane;
    v3_muladds( player->rb.to_world[2], inf_avg.n,
@@ -1704,6 +1713,8 @@ VG_STATIC void skate_5050_apply( player_instance *player,
                                     1000.0f,
                                     k_grind_dampener,
                                     k_rb_delta );
+   vg_line_arrow( player->rb.co, fwd_nplane, 0.8f, VG__RED );
+   vg_line_arrow( player->rb.co, dir_nplane, 0.8f, VG__RED );
 
    v3f pos_front = { 0.0f, -k_board_radius, -1.0f * k_board_length },
        pos_back  = { 0.0f, -k_board_radius,  1.0f * k_board_length },
@@ -1946,7 +1957,7 @@ VG_STATIC enum skate_activity skate_availible_grind( player_instance *player )
    struct player_skate *s = &player->_skate;
 
    if( s->grind_cooldown > 100 ){
-      vg_fatal_exit_loop( "wth!\n" );
+      vg_fatal_error( "wth!\n" );
    }
 
    /* debounces this state manager a little bit */
@@ -1966,18 +1977,20 @@ VG_STATIC enum skate_activity skate_availible_grind( player_instance *player )
    int allow_back  = 1,
        allow_front = 1;
 
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
+
    if( s->state.activity == k_skate_activity_grind_5050 || 
        s->state.activity == k_skate_activity_grind_back50 ||
        s->state.activity == k_skate_activity_grind_front50 )
    {
-      float tilt = player->input_js1v->axis.value;
+      float tilt = steer[1];
 
       if( fabsf(tilt) >= 0.25f ){
          v3f raw = {0.0f,0.0f,tilt};
          m3x3_mulv( player->rb.to_world, raw, raw );
 
-         float way = player->input_js1v->axis.value *
-                        vg_signf( v3_dot( raw, player->rb.v ) );
+         float way = tilt * vg_signf( v3_dot( raw, player->rb.v ) );
 
          if( way < 0.0f ) allow_front = 0;
          else allow_back = 0;
@@ -2015,7 +2028,7 @@ VG_STATIC enum skate_activity skate_availible_grind( player_instance *player )
          res_front50 = skate_grind_truck_entry( player, -1.0f, &inf_front50 );
 
       if( res_back50 != res_front50 ){
-         int wants_to_do_that = fabsf(player->input_js1v->axis.value) >= 0.25f;
+         int wants_to_do_that = fabsf(steer[1]) >= 0.25f;
 
          res_back50  &= wants_to_do_that;
          res_front50 &= wants_to_do_that;
@@ -2067,7 +2080,7 @@ VG_STATIC enum skate_activity skate_availible_grind( player_instance *player )
 VG_STATIC void player__skate_update( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
-   world_instance *world = get_active_world();
+   world_instance *world = world_current_instance();
 
    v3_copy( player->rb.co, s->state.prev_pos );
    s->state.activity_prev = s->state.activity;
@@ -2188,7 +2201,7 @@ VG_STATIC void player__skate_update( player_instance *player )
          if( !prev_contacts[i] ){
             v3f co;
             m4x3_mulv( player->rb.to_world, wheels[i].pos, co );
-            audio_oneshot_3d( &audio_taps[rand()%4], co, 40.0f, 0.75f );
+            audio_oneshot_3d( &audio_taps[vg_randu32()%4], co, 40.0f, 0.75f );
          }
       }
       audio_unlock();
@@ -2390,7 +2403,8 @@ begin_collision:;
 
    manifold_len += l;
 
-   debug_capsule( mtx, capsule.radius, capsule.height, VG__WHITE );
+   if( vg_lines.draw )
+      debug_capsule( mtx, capsule.radius, capsule.height, VG__WHITE );
 
    /* add limits */
    if( s->state.activity >= k_skate_activity_grind_any ){
@@ -2561,11 +2575,11 @@ begin_collision:;
 
       if( s->state.activity == k_skate_activity_ground ){
          if( (fabsf(s->state.slip) > 0.75f) ){
-            audio_oneshot_3d( &audio_lands[rand()%2+3], player->rb.co, 
+            audio_oneshot_3d( &audio_lands[vg_randu32()%2+3], player->rb.co, 
                               40.0f, 1.0f );
          }
          else{
-            audio_oneshot_3d( &audio_lands[rand()%3], player->rb.co, 
+            audio_oneshot_3d( &audio_lands[vg_randu32()%3], player->rb.co, 
                               40.0f, 1.0f );
          }
       }
@@ -2656,7 +2670,7 @@ VG_STATIC void player__skate_animate( player_instance *player,
 
    float curspeed  = v3_length( player->rb.v ),
          kickspeed = vg_clampf( curspeed*(1.0f/40.0f), 0.0f, 1.0f ),
-         kicks     = (vg_randf()-0.5f)*2.0f*kickspeed,
+         kicks     = (vg_randf64()-0.5f)*2.0f*kickspeed,
          sign      = vg_signf( kicks );
 
    s->wobble[0] = vg_lerpf( s->wobble[0], kicks*kicks*sign, 6.0f*vg.time_delta);
@@ -2736,11 +2750,10 @@ VG_STATIC void player__skate_animate( player_instance *player,
                                (vg.time - s->state.cur_push) < 0.125,
                                6.0f*vg.time_delta );
 
-      float pt = push_time + vg.accumulator;
       if( s->state.reverse > 0.0f )
-         skeleton_sample_anim( sk, s->anim_push, pt, bpose );
+         skeleton_sample_anim( sk, s->anim_push, push_time, bpose );
       else
-         skeleton_sample_anim( sk, s->anim_push_reverse, pt, bpose );
+         skeleton_sample_anim( sk, s->anim_push_reverse, push_time, bpose );
 
       skeleton_lerp_pose( sk, apose, bpose, s->blend_push, apose );
 
@@ -2767,7 +2780,10 @@ VG_STATIC void player__skate_animate( player_instance *player,
    
    mdl_keyframe air_pose[32];
    {
-      float target = -player->input_js1h->axis.value;
+      v2f steer;
+      joystick_state( k_srjoystick_steer, steer );
+
+      float target = -steer[1];
 
       s->blend_airdir = vg_lerpf( s->blend_airdir, target, 2.4f*vg.time_delta );
       
@@ -2776,9 +2792,10 @@ VG_STATIC void player__skate_animate( player_instance *player,
 
       static v2f grab_choice;
 
-      v2f grab_input = { player->input_js2h->axis.value,
-                         player->input_js2v->axis.value };
+      v2f grab_input;
+      joystick_state( k_srjoystick_grab, grab_input );
       v2_add( s->state.grab_mouse_delta, grab_input, grab_input );
+
       if( v2_length2( grab_input ) <= 0.001f )
          grab_input[0] = -1.0f;
       else
@@ -2858,7 +2875,8 @@ VG_STATIC void player__skate_animate( player_instance *player,
             vg_warn( "FIX THIS! CARROT\n" );
             v4_copy( player->rb.q, s->state.smoothed_rotation );
          }
-         v4_lerp( s->state.smoothed_rotation, player->rb.q, 2.0f*vg.frame_delta,
+         v4_lerp( s->state.smoothed_rotation, player->rb.q, 
+                  2.0f*vg.time_frame_delta,
                   s->state.smoothed_rotation );
          q_normalize( s->state.smoothed_rotation );
 
@@ -2951,15 +2969,22 @@ VG_STATIC void player__skate_animate( player_instance *player,
       q_mul( kf_board->q, qtrick, kf_board->q );
       q_normalize( kf_board->q );
 
-
-      /* foot weight distribution */
-      if( s->blend_weight > 0.0f ){
-         kf_foot_l->co[2] += s->blend_weight * 0.2f;
-         kf_foot_r->co[2] += s->blend_weight * 0.1f;
-      }
-      else{
-         kf_foot_r->co[2] += s->blend_weight * 0.3f;
-         kf_foot_l->co[2] += s->blend_weight * 0.1f;
+      struct player_board *board = player_get_player_board( player );
+      
+      if( board ){
+         /* foot weight distribution */
+         if( s->blend_weight > 0.0f ){
+            kf_foot_l->co[2] = 
+               vg_lerpf( kf_foot_l->co[2], 
+                         board->truck_positions[k_board_truck_back][2]+0.3f, 
+                         0.5f*s->blend_weight );
+         }
+         else{
+            kf_foot_r->co[2] = 
+               vg_lerpf( kf_foot_r->co[2], 
+                         board->truck_positions[k_board_truck_front][2]-0.3f, 
+                         -0.5f*s->blend_weight );
+         }
       }
 
       float slapm = vg_maxf( 1.0f-v3_length2( s->state.trick_vel ), 0.0f );
@@ -3025,15 +3050,18 @@ VG_STATIC void player__skate_animate( player_instance *player,
 
    /* transform */
    rb_extrapolate( &player->rb, dest->root_co, dest->root_q );
-   v3_muladds( dest->root_co, player->rb.to_world[1], -0.1f, dest->root_co );
 
-   float substep = vg_clampf( vg.accumulator / VG_TIMESTEP_FIXED, 0.0f, 1.0f );
+   v3f ext_up,ext_co;
+   q_mulv( dest->root_q, (v3f){0.0f,1.0f,0.0f}, ext_up );
+   v3_copy( dest->root_co, ext_co );
+   v3_muladds( dest->root_co, ext_up, -0.1f, dest->root_co );
 
    v4f qflip;
    if( (s->state.activity <= k_skate_activity_air_to_grind) &&
        (fabsf(s->state.flip_rate) > 0.01f) )
    {
-      float t     = s->state.flip_time;
+      float substep = vg.time_fixed_extrapolate;
+      float t     = s->state.flip_time+s->state.flip_rate*substep*k_rb_delta;
             sign  = vg_signf( t );
 
       t  = 1.0f - vg_minf( 1.0f, fabsf( t * 1.1f ) );
@@ -3050,7 +3078,7 @@ VG_STATIC void player__skate_animate( player_instance *player,
       q_normalize( dest->root_q );
 
       v3f rotation_point, rco;
-      v3_muladds( player->rb.co, player->rb.to_world[1], 0.5f, rotation_point );
+      v3_muladds( ext_co, ext_up, 0.5f, rotation_point );
       v3_sub( dest->root_co, rotation_point, rco );
       
       q_mulv( qflip, rco, rco );
@@ -3071,6 +3099,8 @@ VG_STATIC void player__skate_post_animate( player_instance *player )
    m4x3_mulv( av->sk.final_mtx[ av->id_head ], head, s->state.head_position );
    m4x3_mulv( player->rb.to_local, s->state.head_position, 
                                    s->state.head_position );
+
+   /* TODO: Extrapolate to_local matrix? */
 }
 
 VG_STATIC void player__skate_reset_animator( player_instance *player )