+
+
+ /* hand placement */
+
+ u32 hand_id = animator->z < 0.5f?
+ localplayer.id_ik_hand_l: localplayer.id_ik_hand_r;
+
+ v3f sample_co;
+ m4x3f mmdl;
+ q_m3x3( pose->root_q, mmdl );
+ q_mulv( pose->root_q, pose->keyframes[hand_id-1].co, mmdl[3] );
+ v3_add( mmdl[3], pose->root_co, mmdl[3] );
+ m4x3_mulv( mmdl, sk->bones[hand_id].co, sample_co );
+
+ v3_muladds( sample_co, mmdl[1], 0.3f, sample_co );
+ vg_line_point( sample_co, 0.04f, 0xff0000ff );
+
+ v3f dir;
+ v3_muls( mmdl[1], -1.0f, dir );
+ ray_hit hit = { .dist = 1.5f };
+ if(ray_world( world_current_instance(), sample_co, dir, &hit, 0 )){
+ vg_line_cross( hit.pos, 0xff0000ff, 0.05f );
+ vg_line( sample_co, hit.pos, 0xffffffff );
+
+ f32 amt = vg_maxf( 0.0f, animator->slide-0.5f ) *
+ 2.0f * fabsf(animator->z*2.0f-1.0f);
+
+ f32 d = (hit.dist - 0.3f) * amt;
+ pose->keyframes[hand_id-1].co[1] -= d;
+ kf_hip->co[1] -= d*0.4f;
+ }
+
+ /* skid */
+ f32 amt = vg_maxf(0.0f, (animator->slide - 0.5f) * 2.0f);
+ u8 skidders[] = { localplayer.id_ik_foot_l,
+ localplayer.id_ik_foot_r,
+ localplayer.id_board };
+ v4f qskid;
+ q_axis_angle( qskid, (v3f){0,1,0}, -animator->steer[1]*0.2f );
+
+ for( u32 i=0; i<vg_list_size(skidders); i ++ ){
+ mdl_keyframe *kf = &pose->keyframes[ skidders[i]-1 ];
+ keyframe_rotate_around( kf,
+ (v3f){0,0,0.4f*(animator->z*2.0f-1.0f)*amt},
+ sk->bones[skidders[i]].co, qskid );
+ }
+ }
+#endif
+}
+
+static void player__skate_effects( void *_animator, m4x3f *final_mtx,
+ struct player_board *board,
+ struct player_effects_data *effect_data ){
+
+ struct skeleton *sk = &localplayer.skeleton;
+ struct player_skate_animator *animator = _animator;
+
+ if( animator->grind > 0.5f ){
+ v3f vp0, vp1, vpc;
+ if( board ){
+ v3_copy((v3f){0.0f,0.02f, board->truck_positions[0][2]}, vp1 );
+ v3_copy((v3f){0.0f,0.02f, board->truck_positions[1][2]}, vp0 );
+ }
+ else{
+ v3_zero( vp0 );
+ v3_zero( vp1 );
+ }
+
+ v3f *board_mtx = final_mtx[ localplayer.id_board ];
+ m4x3_mulv( board_mtx, vp0, vp0 );
+ m4x3_mulv( board_mtx, vp1, vp1 );
+ v3_add( vp0, vp1, vpc );
+ v3_muls( vpc, 0.5f, vpc );
+
+ int back = 0, front = 0, mid = 0;
+
+ if( animator->activity == k_skate_activity_grind_5050 ){
+ back = 1;
+ front = 1;
+ }
+ else if( animator->activity == k_skate_activity_grind_back50 ){
+ back = 1;
+ }
+ else if( animator->activity == k_skate_activity_grind_front50 ){
+ front = 1;
+ }
+ else if( animator->activity == k_skate_activity_grind_boardslide ){
+ mid = 1;
+ }
+
+ if( back ){
+ effect_spark_apply( &effect_data->spark, vp0,
+ animator->root_v, vg.time_delta );
+ }
+
+ if( front ){
+ effect_spark_apply( &effect_data->spark, vp1,
+ animator->root_v, vg.time_delta );
+ }
+
+ if( mid ){
+ effect_spark_apply( &effect_data->spark, vpc,
+ animator->root_v, vg.time_delta );
+ }