sandsurf/glide basics
authorhgn <hgodden00@gmail.com>
Sun, 21 Jan 2024 14:10:15 +0000 (14:10 +0000)
committerhgn <hgodden00@gmail.com>
Sun, 21 Jan 2024 14:10:15 +0000 (14:10 +0000)
15 files changed:
blender_export.py
ent_tornado.c [new file with mode: 0644]
maps_src/dev_flatworld/main.mdl
model.h
particle.h
player.c
player.h
player_effects.c
player_effects.h
player_glide.c [new file with mode: 0644]
player_glide.h [new file with mode: 0644]
player_replay.c
player_skate.c
player_walk.c
skaterift.c

index f64989bf1cb66dae2e51249e382f3075f378e58c..695fff05d7eee6303b7333e48ffd8b333c0936ed 100644 (file)
@@ -3674,7 +3674,9 @@ class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
       ('1','wood',''),
       ('2','grass',''),
       ('3','tiles',''),
-      ('4','metal','')
+      ('4','metal',''),
+      ('5','snow (low friction)',''),
+      ('6','sand (medium friction)','')
       ])
    
    collision: bpy.props.BoolProperty( \
diff --git a/ent_tornado.c b/ent_tornado.c
new file mode 100644 (file)
index 0000000..d59c6b7
--- /dev/null
@@ -0,0 +1,83 @@
+#include "world.h"
+#include "particle.h"
+
+static f32 k_tornado_strength = 0.0f,
+           k_tornado_ratio    = 0.5f,
+           k_tornado_range    = 10.f;
+
+static void ent_tornado_init(void){
+   vg_console_reg_var( "k_tonado_strength", &k_tornado_strength,
+                        k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+   vg_console_reg_var( "k_tonado_ratio", &k_tornado_ratio,
+                        k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+   vg_console_reg_var( "k_tonado_range", &k_tornado_range,
+                        k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+}
+
+static void ent_tornado_debug(void) {
+   world_instance *world = world_current_instance();
+   for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+      ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+      if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+         v3f p1;
+         v3_add( marker->transform.co, (v3f){0,20,0}, p1 );
+         vg_line( marker->transform.co, p1, VG__RED );
+
+         m4x3f mmdl;
+         m4x3_identity( mmdl );
+         v3_copy( marker->transform.co, mmdl[3] );
+         vg_line_sphere( mmdl, k_tornado_range, 0 );
+      }
+   }
+}
+
+static void ent_tornado_forces( v3f co, v3f cv, v3f out_a ){
+   world_instance *world = world_current_instance();
+   v3_zero( out_a );
+
+   for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+      ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+      if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+         v3f d, dir;
+         v3_sub( co, marker->transform.co, d );
+         d[1] = 0.0f;
+
+         f32 dist = v3_length( d );
+         v3_normalize( d );
+
+         v3_cross( d, (v3f){0,1,0}, dir );
+         if( v3_dot( dir, cv ) < 0.0f )
+            v3_negate( dir, dir );
+
+         f32  s = vg_maxf(0.0f, 1.0f-dist/k_tornado_range),
+              F0 = s*k_tornado_strength,
+              F1 = s*s*k_tornado_strength;
+
+         v3_muladds( out_a, dir, F0 * k_tornado_ratio, out_a );
+         v3_muladds( out_a, d,   F1 * -(1.0f-k_tornado_ratio), out_a );
+      }
+   }
+}
+
+static void ent_tornado_pre_update(void){
+   world_instance *world = world_current_instance();
+   for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+      ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+      if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+         v3f co;
+         vg_rand_sphere( &vg.rand, co );
+
+         v3f tangent = { co[2], 0, co[0] };
+
+         f32 s = vg_signf( co[1] );
+         v3_muls( tangent, s*10.0f, tangent );
+         co[1] *= s;
+
+         v3_muladds( marker->transform.co, co, k_tornado_range, co );
+         particle_spawn( &particles_env, co, tangent, 2.0f, 0xffffffff );
+      }
+   }
+}
index 7860b3f8a2bc8643b5bf0b6ead71112addd25082..10619c573fcd1a44dd0c286a8c788ef22b577b70 100644 (file)
Binary files a/maps_src/dev_flatworld/main.mdl and b/maps_src/dev_flatworld/main.mdl differ
diff --git a/model.h b/model.h
index a182e51293d9b1d18901e1671d2d39d92a251f1f..62438dcf0f84f4924fa0a449677cb9b715ff5f68 100644 (file)
--- a/model.h
+++ b/model.h
@@ -26,11 +26,13 @@ enum mdl_shader{
 };
 
 enum mdl_surface_prop{
-   k_surface_prop_concrete          = 0,
-   k_surface_prop_wood              = 1,
-   k_surface_prop_grass             = 2,
-   k_surface_prop_tiles             = 3,
-   k_surface_prop_metal             = 4
+   k_surface_prop_concrete = 0,
+   k_surface_prop_wood     = 1,
+   k_surface_prop_grass    = 2,
+   k_surface_prop_tiles    = 3,
+   k_surface_prop_metal    = 4,
+   k_surface_prop_snow     = 5,
+   k_surface_prop_sand     = 6
 };
 
 enum material_flag{
index 14b2e66e94314199948a92a27d5013e6dc5797ca..fb4d27f1bbdff659cdfe76bb12d1e5d170f9dfe2 100644 (file)
@@ -33,6 +33,11 @@ static particles_grind = {
    .scale = 0.02f,
    .velocity_scale = 0.001f,
    .width = 0.0125f
+},
+particles_env = {
+   .scale = 0.04f,
+   .velocity_scale = 0.001f,
+   .width = 0.25f
 };
 
 static void particle_alloc( particle_system *sys, u32 max );
index 7a099f0fc16847c58d5862f22d0a97f0b5d2880f..3a5c10069b1c233ea3f490f6d1ee1639b69ae3e3 100644 (file)
--- a/player.c
+++ b/player.c
@@ -377,6 +377,7 @@ static void player__networked_sfx( u8 system, u8 priority, u8 id,
 #include "player_skate.c"
 #include "player_dead.c"
 #include "player_drive.c"
+#include "player_glide.c"
 #include "player_basic_info.c"
 
 #include "player_render.c"
index f837cb955cb96b87a033e9879eed80a87d327a8a..41d527454f73aa089b31e7d98f26bf7aa4607ac0 100644 (file)
--- a/player.h
+++ b/player.h
@@ -12,6 +12,7 @@ enum player_subsystem{
    k_player_subsystem_dead = 2,
    k_player_subsystem_drive = 3,
    k_player_subsystem_basic_info = 4,
+   k_player_subsystem_glide = 5,
    k_player_subsystem_max,
    k_player_subsystem_invalid = 255
 };
@@ -68,6 +69,7 @@ struct player_subsystem_interface{
 #include "player_skate.h"
 #include "player_dead.h"
 #include "player_drive.h"
+#include "player_glide.h"
 #include "player_basic_info.h"
 
 #include "player_replay.h"
@@ -192,7 +194,8 @@ struct player_subsystem_interface static *player_subsystems[] = {
    [k_player_subsystem_dead]  = &player_subsystem_dead,
    [k_player_subsystem_drive] = &player_subsystem_drive,
    [k_player_subsystem_skate] = &player_subsystem_skate,
-   [k_player_subsystem_basic_info]=&player_subsystem_basic_info
+   [k_player_subsystem_basic_info]=&player_subsystem_basic_info,
+   [k_player_subsystem_glide] = &player_subsystem_glide,
 };
 
 /*
index 2bfc3bf3bcfcaad691c4ec16471a079a3e64eac1..9b13445a6b4207ebde985b934202118382921261 100644 (file)
@@ -17,7 +17,7 @@ static void effect_spark_apply( effect_spark *ef, v3f co, v3f v, f32 dt ){
    if( !ef->colour ) return;
 
    if( ef->t < 0.0f ){
-      ef->t = 0.05f+vg_randf64(&vg.rand)*0.1f;
+      ef->t += 0.05f+vg_randf64(&vg.rand)*0.1f;
 
       v3f dir;
       v3_copy( v, dir );
index ae0f95e0ef86dfc06102ee65e492a4af8c43c63c..c52361d45bcae8e7a1fc0fb32c2f602fb24f1b50 100644 (file)
@@ -20,7 +20,7 @@ static void effect_spark_apply( effect_spark *ef, v3f co, v3f v, f32 dt );
 
 struct player_effects_data {
    effect_blink blink;
-   effect_spark spark;
+   effect_spark spark, sand;
 };
 
 #endif /* PLAYER_EFFECTS */
diff --git a/player_glide.c b/player_glide.c
new file mode 100644 (file)
index 0000000..80cb2bb
--- /dev/null
@@ -0,0 +1,186 @@
+#ifndef PLAYER_GLIDE_C
+#define PLAYER_GLIDE_C
+
+#include "player_glide.h"
+#include "input.h"
+
+static f32 k_glide_steer = 2.0f,
+           k_glide_cl = 0.04f,
+           k_glide_cs = 0.02f,
+           k_glide_drag = 0.0001f,
+           k_glide_slip_yaw = 0.1f,
+           k_glide_lift_pitch = 0.0f,
+           k_glide_wing_orient = -0.1f,
+           k_glide_balance = 1.0f;
+
+static i32 k_glide_pause = 0;
+
+static void player_glide_pre_update(void){
+}
+
+static void massless_accel( rigidbody *rb, v3f delta, v3f impulse ){
+   /* linear */
+   v3_muladds( rb->v, impulse, k_rb_delta, rb->v );
+   
+   /* Angular velocity */
+   v3f wa;
+   v3_cross( delta, impulse, wa );
+   v3_muladds( rb->w, wa, k_rb_delta, rb->w );
+}
+
+static void calculate_lift( v3f vl, f32 aoa_bias, 
+                            v3f axis, v3f back, f32 power,
+                            v3f out_force ){
+   v3f up;
+   v3_cross( back, axis, up );
+
+   v3f wind;
+   v3_muladds( vl, axis, -v3_dot(axis,vl), wind );
+   
+   f32 windv2 = v3_length2(wind),
+       aoa    = atan2f( v3_dot( up, wind ), v3_dot( back, wind ) ) + aoa_bias,
+       cl     = aoa / VG_PIf, /* TODO: make it a curve */
+       L      = windv2 * cl * power;
+
+   v3f lift_dir;
+   v3_normalize( wind );
+   v3_cross( wind, axis, lift_dir );
+
+   /* this is where induced drag (from the flappy things) would go */
+   
+   v3_muls( lift_dir, L, out_force );
+}
+
+static void calculate_drag( v3f vl, f32 cd, v3f out_force ){
+   f32 v2 = v3_length2( vl );
+   v3f dir;
+   v3_copy( vl, dir );
+   v3_normalize( dir );
+   v3_muls( vl, -cd*v2, out_force );
+}
+
+static void player_glide_update(void){
+   rigidbody *rb = &localplayer.rb;
+   vg_line_sphere( rb->to_world, 1.0f, 0 );
+
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
+
+   /* lift */
+   v3f vl, wl;
+   m3x3_mulv( rb->to_local, rb->v, vl );
+   m3x3_mulv( rb->to_local, rb->w, wl );
+   
+   v3f F, Flift, Fslip, Fdrag, FslipW, FliftW;
+
+   calculate_lift( vl, steer[1]*k_glide_steer, 
+                  (v3f){1,0,0},
+                  (v3f){0,sinf(k_glide_wing_orient),cosf(k_glide_wing_orient)},
+                  k_glide_cl, Flift );
+   v3_copy( Flift, player_glide.info_lift );
+   v3_cross( (v3f){0,0,0}, Flift, FliftW );
+   
+   calculate_lift( vl, 0.0f,
+                  (v3f){0,1,0},(v3f){0,0,1},
+                  k_glide_cs, Fslip );
+   v3_copy( Fslip, player_glide.info_slip );
+   v3_cross( (v3f){0,k_glide_lift_pitch,k_glide_slip_yaw}, Fslip, FslipW );
+
+   calculate_drag( vl, k_glide_drag, Fdrag );
+   v3_copy( Fdrag, player_glide.info_drag );
+
+   v3f balance = {0.0f,-k_glide_balance,0.0f};
+   m3x3_mulv( rb->to_local, balance, balance );
+   vg_info( PRINTF_v3f( balance ) );
+
+   v3f Fw = {
+       steer[1]*k_glide_steer - balance[2],
+       0.0f,
+      -steer[0]*k_glide_steer + balance[0],
+   };
+
+   if( player_glide.ticker ){
+      player_glide.ticker --;
+      return;
+   }
+   player_glide.ticker += k_glide_pause;
+
+   /* apply forces */
+   v3_add( Flift, Fslip, F );
+   v3_add( F, Fdrag, F );
+
+   m3x3_mulv( rb->to_world, F, F );
+   v3_muladds( rb->v, F, k_rb_delta, rb->v );
+
+   v3_add( Fw, FslipW, Fw );
+   v3_add( Fw, FliftW, Fw );
+   m3x3_mulv( rb->to_world, Fw, Fw );
+   v3_muladds( rb->w, Fw, k_rb_delta, rb->w );
+
+   rb_iter( rb );
+   rb_update_transform( rb );
+}
+
+static void player_glide_post_update(void){
+}
+
+static void player_glide_animate(void){
+   struct player_glide *g = &player_glide;
+   struct player_glide_animator *animator = &g->animator;
+   rb_extrapolate( &localplayer.rb, animator->root_co, animator->root_q );
+}
+
+static void player_glide_pose( void *_animator, player_pose *pose ){
+   struct skeleton *sk = &localplayer.skeleton;
+   struct player_glide_animator *animator = _animator;
+
+   skeleton_sample_anim( sk, player_glide.anim_temp, 0.0f, pose->keyframes );
+
+   v3_copy( animator->root_co, pose->root_co );
+   v4_copy( animator->root_q, pose->root_q );
+}
+
+static void player_glide_post_animate(void){
+   if( localplayer.cam_control.camera_mode == k_cam_firstperson )
+      localplayer.cam_velocity_influence = 0.0f;
+   else
+      localplayer.cam_velocity_influence = 0.0f;
+
+   v3f fwd;
+   v3_muls( localplayer.rb.to_world[2], -1.0f, fwd );
+   v3_angles( fwd, localplayer.angles );
+}
+
+static void player_glide_im_gui(void){
+   player__debugtext( 1, "Nothing here" );
+   player__debugtext( 1, " lift: %.2f %.2f %.2f", 
+                           player_glide.info_lift[0],
+                           player_glide.info_lift[1],
+                           player_glide.info_lift[2] );
+   player__debugtext( 1, " slip: %.2f %.2f %.2f", 
+                           player_glide.info_slip[0],
+                           player_glide.info_slip[1],
+                           player_glide.info_slip[2] );
+   player__debugtext( 1, " drag: %.2f %.2f %.2f", 
+                           player_glide.info_drag[0],
+                           player_glide.info_drag[1],
+                           player_glide.info_drag[2] );
+}
+
+static void player_glide_bind(void){
+   struct skeleton *sk = &localplayer.skeleton;
+   player_glide.anim_temp = skeleton_get_anim( sk, "idle_cycle+y" );
+
+   u32 mask = VG_VAR_CHEAT|VG_VAR_PERSISTENT;
+   VG_VAR_F32( k_glide_steer, flags=mask );
+   VG_VAR_F32( k_glide_cl, flags=mask );
+   VG_VAR_F32( k_glide_cs, flags=mask );
+   VG_VAR_F32( k_glide_drag, flags=mask );
+   VG_VAR_F32( k_glide_slip_yaw, flags=mask );
+   VG_VAR_F32( k_glide_lift_pitch, flags=mask );
+   VG_VAR_I32( k_glide_pause, flags=mask );
+   VG_VAR_F32( k_glide_balance, flags=mask );
+   VG_VAR_F32( k_glide_wing_orient, flags=mask );
+}
+
+#endif /* PLAYER_GLIDE_C */
diff --git a/player_glide.h b/player_glide.h
new file mode 100644 (file)
index 0000000..e50fde3
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef PLAYER_GLIDE_H
+#define PLAYER_GLIDE_H
+
+#include "player.h"
+
+struct player_glide {
+   struct skeleton_anim *anim_temp;
+
+   struct player_glide_animator {
+      v3f root_co;
+      v4f root_q;
+   }
+   animator;
+
+   v3f info_lift,
+       info_slip,
+       info_drag;
+
+   u32 ticker;
+}
+static player_glide;
+
+static void player_glide_pre_update(void);
+static void player_glide_update(void);
+static void player_glide_post_update(void);
+static void player_glide_animate(void);
+static void player_glide_pose( void *animator, player_pose *pose );
+
+static void player_glide_post_animate(void);
+static void player_glide_im_gui(void);
+static void player_glide_bind(void);
+
+struct player_subsystem_interface static player_subsystem_glide = {
+   .pre_update = player_glide_pre_update,
+   .update = player_glide_update,
+   .post_update = player_glide_post_update,
+   .animate = player_glide_animate,
+   .pose = player_glide_pose,
+   .post_animate = player_glide_post_animate,
+   .im_gui = player_glide_im_gui,
+   .bind = player_glide_bind,
+
+   .animator_data = &player_glide.animator,
+   .animator_size = sizeof(player_glide.animator),
+   .name = "Glide"
+};
+
+#endif /* PLAYER_GLIDE_H */
index 6fea7d91338c5805fe7dae536877fe6c3ab47845..31c4c74ead695a6cf789c060337956d07b543aed 100644 (file)
@@ -267,21 +267,18 @@ static void skaterift_record_frame( replay_buffer *replay,
 
    u16 gamestate_size = 0;
    if( save_state ){
+      /* TODO: have as part of system struct */
       gamestate_size = (u32 []){ 
          [k_player_subsystem_walk ] = sizeof(struct player_walk_state),
          [k_player_subsystem_drive] = 0,
          [k_player_subsystem_skate] = sizeof(struct player_skate_state),
          [k_player_subsystem_dead ] = localplayer.ragdoll.part_count * 
-                                       sizeof(struct replay_rb)
+                                       sizeof(struct replay_rb),
+         [k_player_subsystem_glide] = 0,
       }[ localplayer.subsystem ];
    }
 
-   u16 animator_size = (u16 []){
-      [k_player_subsystem_walk ] = sizeof(struct player_walk_animator),
-      [k_player_subsystem_drive] = 0,
-      [k_player_subsystem_skate] = sizeof(struct player_skate_animator),
-      [k_player_subsystem_dead ] = sizeof(struct player_dead_animator)
-   }[ localplayer.subsystem ];
+   u16 animator_size = player_subsystems[localplayer.subsystem]->animator_size;
    
    replay_frame *frame = replay_newframe( replay,
                                           animator_size, gamestate_size, 
@@ -347,6 +344,9 @@ static void skaterift_record_frame( replay_buffer *replay,
    else if( localplayer.subsystem == k_player_subsystem_dead ){
       memcpy( dst, &player_dead.animator, animator_size );
    }
+   else if( localplayer.subsystem == k_player_subsystem_glide ){
+      memcpy( dst, &player_glide.animator, animator_size );
+   }
 
    /* sound effects */
    memcpy( replay_frame_data( frame, k_replay_framedata_sfx ),
index 779ba7596f5f5126ae102443bdd416d02b4c406c..6d9aa924a1cbfc00dcb51bcb2828882c94675884 100644 (file)
@@ -8,6 +8,8 @@
 #include "ent_skateshop.h"
 #include "addon.h"
 
+#include "ent_tornado.c"
+
 static void player__skate_bind(void){
    struct skeleton *sk = &localplayer.skeleton;
    rb_update_transform( &localplayer.rb );
@@ -403,18 +405,29 @@ static void player__approximate_best_trajectory(void){
       inf->gravity = gravity;
       v3_copy( launch_v, inf->v );
 
+      /* initial conditions */
+      v3f v;
+      v3_copy( launch_v, v );
+      v3_copy( launch_co, co1 );
+
       for( int i=1; i<=50; i++ ){
-         float t = (float)i * k_trace_delta;
+         f32 t = (f32)i * k_trace_delta;
 
-         v3_muls( launch_v, t, co1 );
-         co1[1] += -0.5f * gravity * t*t;
-         v3_add( launch_co, co1, co1 );
+         /* integrate forces */
+         v3f a;
+         ent_tornado_forces( co1, v, a );
+         a[1] -= gravity;
 
-         float launch_vy = launch_v[1];
+         /* position */
+         v3_muladds( co1, v, k_trace_delta, co1 );
+         v3_muladds( co1, a, 0.5f*k_trace_delta*k_trace_delta, co1 );
+
+         /* velocity */
+         v3_muladds( v, a, k_trace_delta, v );
 
          int search_for_grind = 1;
          if( grind_located ) search_for_grind = 0;
-         if( launch_vy - gravity*t > 0.0f ) search_for_grind = 0;
+         if( v[1] > 0.0f ) search_for_grind = 0;
 
          /* REFACTOR */
 
@@ -448,13 +461,9 @@ static void player__approximate_best_trajectory(void){
          }
 
          if( search_for_grind ){
-            v3f ve;
-            v3_copy( launch_v, ve );
-            ve[1] += -gravity * t;
-
-            if( skate_grind_scansq( closest, ve, 0.5f, &grind ) ){
+            if( skate_grind_scansq( closest, v, 0.5f, &grind ) ){
                /* check alignment */
-               v2f v0 = { ve[0], ve[2] },
+               v2f v0 = { v[0], v[2] },
                    v1 = { grind.dir[0], grind.dir[2] };
 
                v2_normalize( v0 );
@@ -467,7 +476,7 @@ static void player__approximate_best_trajectory(void){
                   a_min = cosf( VG_PIf * 0.05f );
 
                /* check speed */
-               if( (fabsf(v3_dot( ve, grind.dir ))>=k_grind_axel_min_vel) &&
+               if( (fabsf(v3_dot( v, grind.dir ))>=k_grind_axel_min_vel) &&
                    (a >= a_min) && 
                    (fabsf(grind.dir[1]) < 0.70710678118654752f))
                {
@@ -509,12 +518,7 @@ static void player__approximate_best_trajectory(void){
                world_tri_index_surface( trace_world, tri[0] );
 
             inf->type = k_prediction_land;
-
-            v3f ve;
-            v3_copy( launch_v, ve );
-            ve[1] += -gravity * t;
-
-            inf->score = -v3_dot( ve, inf->n );
+            inf->score = -v3_dot( v, inf->n );
             inf->land_dist = t + k_trace_delta * t1;
 
             /* Bias prediction towords ramps */
@@ -901,10 +905,20 @@ static void skate_apply_friction_model(void){
    f32 lat = k_friction_lat;
 
    if( fabsf(axis_state(k_sraxis_skid)) > 0.1f ){
-      lat = k_friction_lat * 2.0f;
+      if( (player_skate.surface == k_surface_prop_snow) ||
+          (player_skate.surface == k_surface_prop_sand) ){
+         lat *= 8.0f;
+      }
+      else
+         lat *= 1.5f;
    }
 
-   vel[0] += vg_cfrictf( vel[0], k_friction_lat * k_rb_delta );
+   if( player_skate.surface == k_surface_prop_snow )
+      lat *= 0.5f;
+   else if( player_skate.surface == k_surface_prop_sand )
+      lat *= 0.6f;
+
+   vel[0] += vg_cfrictf( vel[0], lat * k_rb_delta );
    vel[2] += vg_cfrictf( vel[2], k_friction_resistance * k_rb_delta );
 
    /* Pushing additive force */
@@ -1206,6 +1220,12 @@ static void player__skate_pre_update(void){
    if( button_down(k_srbind_use) && (v3_length2(state->trick_vel) < 0.01f) ){
       localplayer.subsystem = k_player_subsystem_walk;
 
+      if( state->activity <= k_skate_activity_air_to_grind ){
+         localplayer.subsystem = k_player_subsystem_glide;
+         player__begin_holdout( (v3f){0,0,0} );
+         return;
+      }
+
       v3f angles;
       v3_copy( localplayer.cam.angles, localplayer.angles );
       localplayer.angles[2] = 0.0f;
@@ -1221,6 +1241,7 @@ static void player__skate_pre_update(void){
       player__begin_holdout( offset );
       player__walk_transition( state->activity <= k_skate_activity_air_to_grind?
                                0: 1, state->trick_euler[0] );
+
       return;
    }
 
@@ -2352,6 +2373,11 @@ grinding:;
    skate_apply_trick_model();
    skate_apply_pump_model();
 
+   ent_tornado_debug();
+   v3f a;
+   ent_tornado_forces( localplayer.rb.co, localplayer.rb.v, a );
+   v3_muladds( localplayer.rb.v, a, k_rb_delta, localplayer.rb.v );
+
 begin_collision:;
 
    /*
@@ -3407,27 +3433,37 @@ static void player__skate_pose( void *_animator, player_pose *pose ){
 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 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 );
+   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 );
 
+   if( animator->surface == k_surface_prop_sand ){
+      if( (animator->slide>0.4f) && (v3_length2(animator->root_v)>4.0f*4.0f) ){
+         v3f v, co;
+         v3_muls( animator->root_v, 0.5f, v );
+         v3_lerp( vp0, vp1, vg_randf64(&vg.rand), co );
+
+         effect_data->sand.colour = 0xff8ec4e6;
+         effect_spark_apply( &effect_data->sand, co, v, vg.time_delta * 8.0 );
+      }
+   }
+
+   if( animator->grind > 0.5f ){
       int back = 0, front = 0, mid = 0;
 
       if( animator->activity == k_skate_activity_grind_5050 ){
index 38429c7c531be3c6dabf9266a9d094df0886ae63..9c23978a7d463b899bdcc55cda466b778bd75a03 100644 (file)
@@ -1097,7 +1097,9 @@ static void player__walk_im_gui(void){
                                              "wood",
                                              "grass",
                                              "tiles",
-                                             "metal" }
+                                             "metal",
+                                             "snow",
+                                             "sand" }
                                              [w->surface] );
 }
 
index aa0bc8de2e50889a2ba52e8b65ada5ac0a82055a..be4084ef8588c6a2b9b41159a7a8002cc1ca6cab 100644 (file)
@@ -195,6 +195,7 @@ static void skaterift_load_player_content(void){
    replay_clear( &skaterift.replay );
 
    particle_alloc( &particles_grind, 300 );
+   particle_alloc( &particles_env, 200 );
 
    player_load_animation_reference( "models/ch_none.mdl" );
    player_model_load( &localplayer.fallback_model, "models/ch_none.mdl" );
@@ -228,6 +229,7 @@ static void vg_load(void){
    vg_loader_step( addon_system_init, NULL );
    vg_loader_step( workshop_init, NULL );
    vg_loader_step( skateshop_init, NULL );
+   vg_loader_step( ent_tornado_init, NULL );
   
    vg_loader_step( skaterift_load_player_content, NULL );
 
@@ -501,6 +503,11 @@ static void render_scene(void){
    //particle_system_debug( &particles_grind );
    particle_system_prerender( &particles_grind );
    particle_system_render( &particles_grind, &skaterift.cam );
+   
+   ent_tornado_pre_update();
+   particle_system_update( &particles_env, vg.time_delta );
+   particle_system_prerender( &particles_env );
+   particle_system_render( &particles_env, &skaterift.cam );
 
    /* 
     * render transition