SHIT
authorhgn <hgodden00@gmail.com>
Sun, 15 Jun 2025 20:15:51 +0000 (21:15 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 15 Jun 2025 20:15:51 +0000 (21:15 +0100)
47 files changed:
content_skaterift/maps/dev_hub/main.mdl
content_skaterift/maps/mp_mtzero/before.mdl
content_skaterift/maps/mp_mtzero/main.mdl
content_skaterift/maps/mp_spawn/main.mdl
content_skaterift/playermodels/skaterift_amin/ch_amin.mdl [new file with mode: 0644]
content_skaterift/playermodels/skaterift_pro/ch_pro.mdl [new file with mode: 0644]
content_skaterift/playermodels/sr2t/ch_sr2t.mdl [new file with mode: 0644]
shaders/common_world.glsl
shaders/model_character_view.fs
shaders/model_skinned.vs
skaterift_blender/sr_main.py
skaterift_blender/sr_mdl.py
src/addon.c
src/addon.h
src/addon_types.c
src/addon_types.h
src/ent_npc.c
src/ent_route.c
src/ent_skateshop.c
src/ent_skateshop.h
src/entity.h
src/menu.c
src/menu.h
src/metascene.c
src/model.c
src/model.h
src/network.c
src/player.c
src/player.h
src/player_remote.c
src/player_remote.h
src/player_render.c
src/player_render.h
src/player_skate.c
src/replay2.c
src/replay2.h
src/save.c
src/save.h
src/skaterift.c
src/workshop.c
src/workshop.h
src/world.c
src/world.h
src/world_gate.c
src/world_load.c
src/world_routes.c
src/world_sfd.c

index 1a63b818661bc7d524f12be9e584c3f516e6158d..35aa0d361cd3d5a8f08a672d41e2b4a852d8897d 100644 (file)
Binary files a/content_skaterift/maps/dev_hub/main.mdl and b/content_skaterift/maps/dev_hub/main.mdl differ
index a4fbcbc95028acb974cbcd1ab216d0dff30dae46..989451506cea1b3455dc9504b534957d583d417f 100644 (file)
Binary files a/content_skaterift/maps/mp_mtzero/before.mdl and b/content_skaterift/maps/mp_mtzero/before.mdl differ
index 3bf242e86063616dcc9e753452003d3aae630ae7..3748ac63a9e2a467a44dbb3d9c3a9855ab30af32 100644 (file)
Binary files a/content_skaterift/maps/mp_mtzero/main.mdl and b/content_skaterift/maps/mp_mtzero/main.mdl differ
index 20b72b478be95e86f159739e8eff257f8dcb2cc8..fd79f69ab64fb43448c1cfd777ee2d3bd45ae2b4 100644 (file)
Binary files a/content_skaterift/maps/mp_spawn/main.mdl and b/content_skaterift/maps/mp_spawn/main.mdl differ
diff --git a/content_skaterift/playermodels/skaterift_amin/ch_amin.mdl b/content_skaterift/playermodels/skaterift_amin/ch_amin.mdl
new file mode 100644 (file)
index 0000000..7da942e
Binary files /dev/null and b/content_skaterift/playermodels/skaterift_amin/ch_amin.mdl differ
diff --git a/content_skaterift/playermodels/skaterift_pro/ch_pro.mdl b/content_skaterift/playermodels/skaterift_pro/ch_pro.mdl
new file mode 100644 (file)
index 0000000..026ed9c
Binary files /dev/null and b/content_skaterift/playermodels/skaterift_pro/ch_pro.mdl differ
diff --git a/content_skaterift/playermodels/sr2t/ch_sr2t.mdl b/content_skaterift/playermodels/sr2t/ch_sr2t.mdl
new file mode 100644 (file)
index 0000000..d6a248e
Binary files /dev/null and b/content_skaterift/playermodels/sr2t/ch_sr2t.mdl differ
index 0bc4e7b6bbcceb2471c9c31523dcd8b8d1d4a035..b6a644b5cfb02573ded5c8516bb316520e0f8e5d 100644 (file)
@@ -61,8 +61,10 @@ float shadow_sample( vec3 co ){
    return clamp( fdelta, 0.2, 0.4 )-0.2;
 }
 
-float newlight_compute_sun_shadow( vec3 co, vec3 dir ){
-   if( g_shadow_samples == 0 ){
+float newlight_compute_sun_shadow( vec3 co, vec3 dir )
+{
+   if( g_shadow_samples == 0 )
+   {
       return 1.0;
    }
 
@@ -94,8 +96,7 @@ vec3 scene_apply_fog( vec3 vfrag, vec3 colour, float fdist ){
    return mix( vfrag, colour, min( 1.0, dist ) );
 }
 
-vec3 scene_calculate_light( int light_index, 
-                            vec3 halfview, vec3 co, vec3 normal )
+vec3 scene_calculate_light( int light_index, vec3 halfview, vec3 co, vec3 normal )
 {
    vec4 light_colour = texelFetch( uLightsArray, light_index+0 );
    vec4 light_co     = texelFetch( uLightsArray, light_index+1 );
@@ -112,33 +113,35 @@ vec3 scene_calculate_light( int light_index,
 
    float falloff = max( 0.0, 1.0-(dist2*light_co.w) );
 
-   if( light_dir.w < 0.999999 ){
+   if( light_dir.w < 0.999999 )
+   {
       float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );
       falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );
    }
 
-   return light_colour.rgb * attenuation * falloff 
-            * step( g_day_phase, light_colour.w );
+   return light_colour.rgb * attenuation * falloff * step( g_day_phase, light_colour.w );
 }
 
-vec3 scene_calculate_packed_light_patch( uint packed_index, 
-                                         vec3 halfview, vec3 co, vec3 normal )
+vec3 scene_calculate_packed_light_patch( uint packed_index, vec3 halfview, vec3 co, vec3 normal )
 {
    uint light_count = packed_index & 0x3u;
 
    vec3 l = vec3(0.0);
 
-   if( light_count >= 1u ){
+   if( light_count >= 1u )
+   {
       int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );
       int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );
       int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );
 
       l += scene_calculate_light( index_0, halfview, co, normal );
 
-      if( light_count >= 2u ){
+      if( light_count >= 2u )
+      {
          l += scene_calculate_light( index_1, halfview, co, normal );
 
-         if( light_count >= 3u ){
+         if( light_count >= 3u )
+         {
             l += scene_calculate_light( index_2, halfview, co, normal );
          }
       }
@@ -147,6 +150,60 @@ vec3 scene_calculate_packed_light_patch( uint packed_index,
    return l;
 }
 
+vec3 scene_calculate_light_step( int light_index, vec3 halfview, vec3 co, vec3 normal )
+{
+   vec4 light_colour = texelFetch( uLightsArray, light_index+0 );
+   vec4 light_co     = texelFetch( uLightsArray, light_index+1 );
+   vec4 light_dir    = texelFetch( uLightsArray, light_index+2 );
+
+   vec3 light_delta = light_co.xyz-co;
+   float dist2 = dot(light_delta,light_delta);
+
+   light_delta = normalize( light_delta );
+
+   float quadratic = dist2*100.0;
+   float attenuation  = 1.0/( 1.0 + quadratic );
+         attenuation *= smoothstep( 0.1,0.3, dot( light_delta, normal ) )*0.4+0.6;
+
+   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );
+
+   if( light_dir.w < 0.999999 )
+   {
+      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );
+      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );
+   }
+
+   return light_colour.rgb * attenuation * falloff * step( g_day_phase, light_colour.w );
+}
+
+vec3 scene_calculate_packed_light_patch_step( uint packed_index, vec3 halfview, vec3 co, vec3 normal )
+{
+   uint light_count = packed_index & 0x3u;
+
+   vec3 l = vec3(0.0);
+
+   if( light_count >= 1u )
+   {
+      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );
+      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );
+      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );
+
+      l += scene_calculate_light_step( index_0, halfview, co, normal );
+
+      if( light_count >= 2u )
+      {
+         l += scene_calculate_light_step( index_1, halfview, co, normal );
+
+         if( light_count >= 3u )
+         {
+            l += scene_calculate_light_step( index_2, halfview, co, normal );
+         }
+      }
+   }
+
+   return l;
+}
+
 vec3 world_compute_lighting( vec3 diffuse, vec3 normal, vec3 co,
                              float light_mask )
 {
index 42fadcbec8f7f489f087f7568d15891836c14142..f03ccfd37ede0dd3cb157ced2ddc73cc9b06d33b 100644 (file)
@@ -15,11 +15,13 @@ in vec3 aWorldCo;
 
 vec3 character_clearskies_lighting( vec3 normal, float shadow, vec3 halfview )
 {
-   float fresnel = 1.0 - abs(dot(normal,halfview));
+   float fresnel = smoothstep(0.2,0.7,1.0 - abs(dot(normal,halfview)));
 
    vec3  reflect_colour = mix( g_daysky_colour.rgb, g_sunset_colour.rgb, g_sunset_phase );
-   vec3  sky_reflection = 0.5 * fresnel * reflect_colour;
-   vec3  light_sun      = max(0.0, dot(normal,g_sun_dir.xyz)*0.5+0.5) * g_sun_colour.rgb * g_day_phase;
+
+   float lgrad = smoothstep(0.4,0.5,dot(normal,g_sun_dir.xyz));
+   vec3  light_sun      = (lgrad*0.2+0.8) * g_sun_colour.rgb * g_day_phase;
+   vec3  sky_reflection = 0.2 * fresnel * vec3(1,1,1) * (1.0-shadow);//reflect_colour;
 
    float scaled_shadow = max( shadow, 1.0 - max(g_sun_dir.y,0.0) );
    vec3 ambient = mix( g_ambient_colour.rgb, g_sunset_ambient.rgb, g_sunset_phase );
@@ -31,6 +33,8 @@ vec3 character_compute_lighting( vec3 diffuse, vec3 normal, vec3 co, float light
    if( g_light_preview == 1 )
       diffuse = vec3(0.75);
 
+   //normal = vec3(0,1,0);
+
    // Lighting
    vec3 halfview = uCamera - co;
    float fdist = length(halfview);
@@ -61,8 +65,8 @@ vec3 character_compute_lighting( vec3 diffuse, vec3 normal, vec3 co, float light
    ivec3 coord = ivec3( cube_coord );
    uvec4 index_sample = texelFetch( uLightsIndex, coord, 0 );
 
-   total_light += scene_calculate_packed_light_patch( index_sample.x, halfview, co, normal ) * light_mask;
-   total_light += scene_calculate_packed_light_patch( index_sample.y, halfview, co, normal ) * light_mask;
+   total_light += scene_calculate_packed_light_patch_step( index_sample.x, halfview, co, normal ) * light_mask;
+   total_light += scene_calculate_packed_light_patch_step( index_sample.y, halfview, co, normal ) * light_mask;
 
    // Take a section of the sky function to give us a matching fog colour
 
index c0775eb50ef5a032148e86e5385160971d674376..53393c0bb4dacdae37d467e10841179736df79f2 100644 (file)
@@ -7,6 +7,7 @@ layout (location=5) in ivec4 a_groups;
 
 #include "motion_vectors_vs.glsl"
 
+uniform vec2 uUvOffset;
 uniform mat4 uPv;
 uniform mat4x3 uTransforms[32];
 
@@ -31,7 +32,7 @@ void main()
    
    gl_Position = uPv * vec4( world_pos, 1.0 );
    aColour = a_colour;
-   aUv = a_uv;
+   aUv = a_uv + uUvOffset;
    aNorm = world_normal;
    aCo = a_co;
    aWorldCo = world_pos;
index d272cbd2c20067d78002a334a3b08f896cf3b025..937fa40e2c34ecdda26346cdb22fe069e0b0c2b5 100644 (file)
@@ -43,6 +43,9 @@ sr_entity_list = [
    ('ent_atom',         'Atom',           '', 30 ),
    ('ent_cutscene',     'Cutscene',       '', 31 ),
    # reserved 32.. light
+   # reserved 33.. mesh
+   ('editer_property',  'Editer Property','', 34 ),
+   ('editer_item',      'Editer Item',    '', 35 )
 ]
 
 MDL_VERSION_NR = 109
@@ -62,6 +65,7 @@ def get_entity_enum_id( alias ):
    if alias == 'ent_cubemap': return 21
    if alias == 'mdl_armature': return 28
    if alias == 'ent_light': return 32
+   if alias == 'mdl_mesh': return 33
 
    return 0
 #}
@@ -509,6 +513,35 @@ class ent_swspreview(Structure):
                ("id_display1",c_uint32)]
 #}
 
+class editer_max_union(Union):
+#{
+   _fields_ = [("_u32",c_uint32),
+               ("_f32",c_float),
+               ("pstr_options",c_uint32)]
+#}  
+
+class editer_property(Structure):
+#{
+   _fields_ = [("pstr_alias",c_uint32),
+               ("ui_type",c_uint8),
+               ("_anonymous_union",editer_max_union)]
+#}
+
+class editer_item(Structure):
+#
+   _fields_ = [("transform",mdl_transform),
+               ("submesh_start",c_uint32),
+               ("submesh_count",c_uint32),
+               ("pstr_visibility",c_uint32),
+               ("pstr_uv_x",c_uint32),
+               ("pstr_uv_y",c_uint32),
+               ("discard_send",c_uint16),
+               ("discard_mask",c_uint16)]
+#}
+
+# Other
+# ---------------------------------------------------------------
+
 class ent_camera(Structure):
 #{
    _fields_ = [("co",c_float*3),("r",c_float*3),("fov",c_float)]
@@ -754,7 +787,8 @@ def sr_compile( collection ):
    # begin
    # -------------------------------------------------------
 
-   for obj in collection.all_objects: 
+   for obj in sorted( collection.all_objects, key = lambda a: a.name ): 
+      print( obj.name )
       _mdl_compiler_add_object( obj )
 
    _mdl_compiler_compile_meshes()
@@ -1734,7 +1768,7 @@ class SR_UL_ENT_LIST(bpy.types.UIList):#{
       c = s1.column()
       c.prop( item, 'target', text='', emboss=False )
       c = s1.column()
-      c.label( text=item.target.SR_data.ent_type if item.target else '' )
+      c.label( text= obj_ent_type(item.target) if item.target else '' )
    #}
 #}
 
@@ -1933,6 +1967,28 @@ class SR_OBJECT_ENT_MARKER(bpy.types.PropertyGroup):
    flags: bpy.props.IntProperty()
 #}
 
+class SR_OBJECT_EDITER_PROPERTY(bpy.types.PropertyGroup):
+#{
+   alias: bpy.props.StringProperty( name="Alias" )
+   tipo: bpy.props.EnumProperty(items=(('0', 'On/Off Toggle', ""),
+                                       ('1', 'Slider', ""),
+                                       ('2', 'Selecter', "") ))
+
+   max_f32: bpy.props.FloatProperty( name="Max Value (f32)" )
+   max_u32: bpy.props.IntProperty( name="Max Value (u32)" )
+   selecter_string: bpy.props.StringProperty( name="Selecter Options (1 character)" )
+#}
+
+class SR_OBJECT_EDITER_ITEM(bpy.types.PropertyGroup):
+#{
+   visibility: bpy.props.StringProperty( name="Visible When" )
+   uv_x: bpy.props.StringProperty( name="UV offset X" )
+   uv_y: bpy.props.StringProperty( name="UV offset Y" )
+   discard_send: bpy.props.StringProperty( name="Discard Send" )
+   discard_mask: bpy.props.StringProperty( name="Discard Mask" )
+#}
+
+
 class SR_OBJECT_ENT_GLYPH(bpy.types.PropertyGroup):
 #{
    mini: bpy.props.FloatVectorProperty(size=2)
@@ -2407,6 +2463,9 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    ent_atom: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ATOM)
    ent_cutscene: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CUTSCENE)
 
+   editer_property: bpy.props.CollectionProperty(type=SR_OBJECT_EDITER_PROPERTY)
+   editer_item: bpy.props.CollectionProperty(type=SR_OBJECT_EDITER_ITEM)
+
    ent_type: bpy.props.EnumProperty(
       name="Type",
       items=sr_entity_list,
@@ -3549,7 +3608,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL, SR_MARKER_PANEL, SR_ENTITY_PANEL, \
             SR_OBJECT_ENT_LIST_ENTRY, SR_UL_ENT_LIST, SR_OBJECT_ENT_LIST, \
             SR_OT_ENT_LIST_NEW_ITEM, SR_OT_ENT_LIST_DEL_ITEM,\
             SR_OBJECT_ENT_GLIDER, SR_OBJECT_ENT_NPC, SR_OBJECT_ENT_SCRIPT, SR_OBJECT_ENT_LOGIC, \
-            SR_OBJECT_ENT_ATOM, SR_OBJECT_ENT_CUTSCENE, \
+            SR_OBJECT_ENT_ATOM, SR_OBJECT_ENT_CUTSCENE, SR_OBJECT_EDITER_ITEM, SR_OBJECT_EDITER_PROPERTY, \
             \
             SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, 
             SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES, SR_MARKER_PROPERTIES \
index 9b35d22c704146297681440757c2f975b69ad3fb..3a53b88e54817b199e1eb0bc08f5d7d97f22ad3b 100644 (file)
@@ -53,7 +53,15 @@ def obj_ent_type( obj ):
    elif obj.type == 'CAMERA': return 'ent_camera'
    elif obj.type == 'LIGHT_PROBE' and (obj.data.type in ['SPHERE','CUBEMAP']):
       return 'ent_cubemap'
-   else: return obj.SR_data.ent_type
+   else: 
+   #{
+      if obj.SR_data.ent_type != 'none':
+         return obj.SR_data.ent_type
+      elif obj.type == 'MESH':
+         return 'mdl_mesh'
+      else:
+         return 'none'
+   #}
 #}
 
 def _mdl_compiler_add_object( obj ):
@@ -76,7 +84,7 @@ def _mdl_compiler_add_object( obj ):
       add_mesh = True
       if ent_type in [ 'ent_traffic', 'ent_prop', 'ent_font', \
                        'ent_font_variant', 'ent_objective', \
-                       'ent_region' ]:
+                       'ent_region', 'editer_item' ]:
       #{
          add_mesh = False
       #}
@@ -258,8 +266,7 @@ def mdl_compile_mesh_internal( obj ):
                               if obj.vertex_groups[_.group].name in \
                                  rig_weight_groups ]
 
-               weight_groups = sorted( src_groups, key = \
-                                       lambda a: a.weight, reverse=True )
+               weight_groups = sorted( src_groups, key = lambda a: a.weight, reverse=True )
                tot = 0.0
                for ml in range(3):#{
                   if len(weight_groups) > ml:#{
@@ -991,6 +998,41 @@ def _mdl_compiler_compile_entities():
             prop.pstr_alias = _af_pack_string( obj_data.alias )
             sr_ent_push( prop )
          #}
+         elif ent_type == 'editer_item':
+         #{
+            item = editer_item()
+            obj_data = obj.SR_data.editer_item[0]
+            compile_obj_transform( obj, item.transform )
+            item.submesh_start, item.submesh_count, _ = mdl_compile_mesh_internal( obj )
+            item.pstr_visibility = _af_pack_string( obj_data.visibility )
+            item.pstr_uv_x = _af_pack_string( obj_data.uv_x )
+            item.pstr_uv_y = _af_pack_string( obj_data.uv_y )
+
+            def _binary_string( string ):
+            #{
+               out = 0
+               for d, dc in enumerate(string):
+                  if dc == '1':
+                     out |= 0x1 << ((len(string)-1) - d)
+               return out
+            #}
+
+            item.discard_send = _binary_string( obj_data.discard_send )
+            item.discard_mask = _binary_string( obj_data.discard_mask )
+
+            sr_ent_push( item )
+         #}
+         elif ent_type == 'editer_property':
+         #{
+            prop = editer_property()
+            obj_data = obj.SR_data.editer_property[0]
+            prop.pstr_alias = _af_pack_string( obj_data.alias )
+            prop.ui_type = int(obj_data.tipo)
+            if prop.ui_type == 0: prop._anonymous_union._u32 = 1
+            elif prop.ui_type == 2: prop._anonymous_union.pstr_options = _af_pack_string( obj_data.selecter_string )
+            elif prop.ui_type == 1: prop._anonymous_union._f32 = obj_data.max_f32
+            sr_ent_push( prop )
+         #}
          elif ent_type == 'ent_font':
          #{
             _ent_font_compile( obj )
index 786b32458b39b91adc79a4db36ebf1247db09f83..cf932b31a11c4e917b0409feb4f19e1d6bfe1baa 100644 (file)
@@ -105,98 +105,151 @@ bool _addon_get_filtered_index( enum addon_type type, addon_id id, u32 whitelist
    return 0;
 }
 
+static u32 addon_alias_hash( addon_alias *alias )
+{
+   u32 hash = 5381;
+   for( u32 i=0; i<sizeof(addon_alias)-ADDON_CPART_MAX; i ++ )
+      hash = ((hash << 5) + hash) + ((u8 *)alias)[i];
+   return hash;
+}
+
+/*
+ * equality check (excluding custom part)
+ */
+bool addon_alias_eq( addon_alias *a, addon_alias *b )
+{
+   if( a->type == b->type )
+   {
+      if( a->steam_workshop_id == b->steam_workshop_id )
+      {
+         if( a->steam_workshop_id )
+            return 1;
+         else
+            return !strcmp( a->folder, b->folder );
+      }
+   }
+   
+   return 0;
+}
+
 addon_id _get_addon_by_alias( addon_alias *alias )
 {
    if( alias->type == k_addon_type_none ) 
       return 0;
 
-   u32 foldername_djb2 = 0;
-   if( !alias->workshop_id )
-      foldername_djb2 = vg_strdjb2( alias->foldername );
-
    VG_MUTEX_LOCK( _addon.registry_lock );
    u32 registry_count = _addon.registry_count;
    VG_MUTEX_UNLOCK( _addon.registry_lock );
 
-   u32 count = 0;
+   u32 hash = addon_alias_hash( alias );
    for( u32 i=0; i < registry_count; i++ )
    {
       addon_reg *reg = &_addon.registry[i];
-      if( reg->alias.type == alias->type )
+      if( reg->alias_hash == hash )
       {
-         if( alias->workshop_id )
-         {
-            if( alias->workshop_id == reg->alias.workshop_id )
-               return i+1;
-         }
-         else
-         {
-            if( reg->foldername_hash == foldername_djb2 )
-            {
-               if( !strcmp( reg->alias.foldername, alias->foldername ) )
-                  return i+1;
-            }
-         }
-
-         count ++;
+         if( addon_alias_eq( &reg->alias, alias ) )
+            return i+1;
       }
    }
  
    return 0;
 }
 
-/*
- * equality check
- */
-bool addon_alias_eq( addon_alias *a, addon_alias *b )
+void addon_make_uid_cpart( addon_id id, char buf[ ADDON_UID_MAX ], char cpart[ ADDON_CPART_MAX ] )
 {
-   if( a->type == b->type )
+   if( id ) 
    {
-      if( a->workshop_id == b->workshop_id )
-      {
-         if( a->workshop_id )
-            return 1;
-         else
-            return !strcmp( a->foldername, b->foldername );
-      }
-      else
-         return 0;
+      if( cpart )
+         if( cpart[0] == '\0' )
+            cpart = NULL;
+
+      addon_reg *reg = addon_details(id);
+      addon_alias *alias = &reg->alias;
+
+      if( alias->steam_workshop_id ) 
+         snprintf( buf, ADDON_UID_MAX, "sr%03d-steam-"PRINTF_U64"%s%s", alias->type, alias->steam_workshop_id, 
+                   cpart? ":": "", cpart? cpart: "" );
+      else                     
+         snprintf( buf, ADDON_UID_MAX, "sr%03d-local-%s%s%s", alias->type, alias->folder,
+                   cpart? ":": "", cpart? cpart: "" );
    }
-   else return 0;
+   else 
+      buf[0] = '\0';
 }
 
+void addon_make_uid( addon_id id, char buf[ ADDON_UID_MAX ] )
+{
+   addon_make_uid_cpart( id, buf, NULL );
+}
 
-/*
- * Create a string version of addon alias in buf
- */
-void addon_alias_uid( addon_alias *alias, char buf[ADDON_UID_MAX] )
+void addon_make_savedata_path( addon_id id, char out_buf[ 256 ] )
 {
-   if( alias->workshop_id ) snprintf( buf, 128, "sr%03d-steam-"PRINTF_U64, alias->type, alias->workshop_id );
-   else                     snprintf( buf, 128, "sr%03d-local-%s", alias->type, alias->foldername );
+   char uid[ ADDON_UID_MAX ];
+   addon_make_uid( id, uid );
+   snprintf( out_buf, 256, "savedata/%s.bkv", uid );
 }
 
-void addon_uid( addon_id id, char buf[ ADDON_UID_MAX ] )
+u32 addon_uid_get_custom_part( const char *uid, char out_cpart[ ADDON_CPART_MAX ] )
 {
-   if( id ) addon_alias_uid( &addon_details(id)->alias, buf );
-   else     buf[0] = '\0';
+   if( uid == NULL )
+   {
+      out_cpart[0] = '\0';
+      return 0;
+   }
+
+   u32 count = 0;
+   bool begins = 0;
+   bool ends = 0;
+
+   for( u32 i=0; i<ADDON_UID_MAX-1; i ++ )
+   {
+      char c = uid[i];
+      if( c == '\0' )
+      {
+         out_cpart[ count ] = '\0';
+         ends = 1;
+         break;
+      }
+
+      if( c == ':' ) 
+      {
+         begins = 1;
+         count = 0;
+      }
+      else
+         if( begins )
+            out_cpart[ count ++ ] = c;
+   }
+
+   if( begins )
+   {
+      if( ends )
+      {
+         return count;
+      }
+      else
+      {
+         vg_error( "While extracting custom part from uid '%s', encounted too long custom string.\n", uid );
+         return 0;
+      }
+   }
+   else return 0;
 }
 
 /*
  * parse uid to alias. returns 1 if successful
  */
-bool addon_uid_to_alias( const char *uid, addon_alias *alias )
+bool addon_parse_uid( const char *uid, addon_alias *alias )
 {
 /*           1
  * 01234567890123
- * sr&&&-@@@@@-#*
+ * sr000-#####-#
  *    |    |   |
- *  type   |   id
- *         |
+ *  type   |  id
+ *         |   
  *     location
  */
-   alias->type = k_addon_type_none;
-   alias->workshop_id = 0;
-   alias->foldername[0] = '\0';
+   memset( alias, 0, sizeof(addon_alias) );
 
    if( strlen(uid) < 13 )
       return 0;
@@ -211,17 +264,41 @@ bool addon_uid_to_alias( const char *uid, addon_alias *alias )
    char location[6];
    memcpy( location, uid+6, 5 );
    location[5] = '\0';
+   char *endptr = NULL;
 
    if( !strcmp(location,"steam") )
-      alias->workshop_id = atoll( uid+12 );
+   {
+      alias->steam_workshop_id = strtoull( uid+12, &endptr, 10 );
+   }
    else if( !strcmp(location,"local") )
    {
-      alias->workshop_id = 0;
-      vg_strncpy( uid+12, alias->foldername, 64, k_strncpy_always_add_null );
+      const char *folder = uid+12;
+      for( u32 i=0; i<ADDON_FOLDERNAME_MAX-1; i ++ )
+      {
+         char c = folder[i];
+         if( (c == ':') || (c == '\0') )
+         {
+            alias->folder[i+1] = '\0';
+                    /* ffs */
+            endptr = ((char *)uid)+12+i;
+            break;
+         }
+         else
+         {
+            alias->folder[i] = c;
+         }
+      }
    }
    else
       return 0;
 
+   if( endptr == NULL )
+   {
+      vg_error( "While parsing uid string, we encountered a folder which is too long (>63 chrs).\n"
+                "  or a broken steamid\n" );
+      return 0;
+   }
+
    return 1;
 }
 
@@ -277,12 +354,6 @@ void addon_system_init( void )
    }
 }
 
-static void addon_set_foldername( addon_reg *reg, const char name[64] )
-{
-   vg_strncpy( name, reg->alias.foldername, 64, k_strncpy_always_add_null );
-   reg->foldername_hash = vg_strdjb2( reg->alias.foldername );
-}
-
 static bool addon_try_load_metadata( addon_reg *reg, const char *folder_path )
 {
    THREAD_1;
@@ -335,15 +406,19 @@ static addon_id addon_alloc_reg( PublishedFileId_t workshop_id, enum addon_type
    reg->flags = 0;
    reg->metadata_len = 0;
    reg->cache_id = 0;
-   reg->alias.workshop_id = workshop_id;
-   reg->alias.foldername[0] = '\0';
+
+   memset( &reg->alias, 0, sizeof(addon_alias) );
+   reg->alias.steam_workshop_id = workshop_id;
    reg->alias.type = type;
 
    if( workshop_id )
    {
+#if 0
+      /* Why were we doing this? */
       char foldername[64];
       snprintf( foldername, 64, PRINTF_U64, workshop_id );
       addon_set_foldername( reg, foldername );
+#endif
       reg->flags |= ADDON_REG_WORKSHOP;
    }
    return id;
@@ -352,57 +427,66 @@ static addon_id addon_alloc_reg( PublishedFileId_t workshop_id, enum addon_type
 static void addon_complete_allocation(void)
 {
    VG_MUTEX_LOCK( _addon.registry_lock );
+
+   addon_reg *reg = &_addon.registry[ _addon.registry_count ];
+   reg->alias_hash = addon_alias_hash( &reg->alias );
    _addon.registry_count ++;
+
    VG_MUTEX_UNLOCK( _addon.registry_lock );
 }
 
 static void addon_fail_allocation(void)
 {
+   vg_warn( "Addon failed allocation\n" );
 }
 
 /*
  * local addons / mods
  * ---------------------------------------------------------------------------------------------------------------------
  */
-addon_id _addon_mount_from_folder( const char *folder, enum addon_type type, const char *content_ext )
+addon_id _addon_mount_from_folder_path( const char *folder_path, enum addon_type type, const char *content_ext )
 {
    THREAD_1;
 
-   const char *folder_name = folder;
+   u32 folder_name_length = 0;
+   const char *folder_name = folder_path;
    for( u32 i=0; i<4096; i ++ )
    {
-      if( folder[i] == '/' )
+      if( folder_path[i] == '/' )
       {
-         folder_name = folder + i + 1;
+         folder_name = folder_path + i + 1;
+         folder_name_length = 0;
          break;
       }
-      else if( folder[i] == '\0' ) 
+      else if( folder_path[i] == '\0' ) 
          break;
+      else
+         folder_name_length ++;
    }
 
-   VG_MUTEX_LOCK( _addon.registry_lock );
-   u32 registry_count = _addon.registry_count;
-   VG_MUTEX_UNLOCK( _addon.registry_lock );
-   
-   u32 folder_hash = vg_strdjb2(folder_name);
-   for( u32 i=0; i<registry_count; i++ )
+   if( folder_name_length >= ADDON_FOLDERNAME_MAX )
    {
-      addon_reg *reg = &_addon.registry[i];
-      if( (reg->alias.type == type) && (reg->foldername_hash == folder_hash) )
-      {
-         if( !strcmp( reg->alias.foldername, folder_name ) )
-            return i+1;
-      }
+      vg_error( "Trying to mount addon from path: %s, foldername is too long (%u chars > %u)\n",
+                  folder_path, folder_name_length, ADDON_FOLDERNAME_MAX-1 );
+      return 0;
    }
 
-   addon_id id = addon_alloc_reg( 0, type );
+   addon_alias q = {0};
+   q.type = type;
+   strcpy( q.folder, folder_name );
+
+   addon_id id = _get_addon_by_alias( &q );
+   if( id )
+      return id;
+
+   id = addon_alloc_reg( 0, type );
    if( !id )
       return 0;
 
    addon_reg *reg = &_addon.registry[ id-1 ];
-   addon_set_foldername( reg, folder_name );
-   bool loaded_metadata = addon_try_load_metadata( reg, folder );
+   strcpy( reg->alias.folder, folder_name );
 
+   bool loaded_metadata = addon_try_load_metadata( reg, folder_path );
    if( loaded_metadata )
    {
       vg_msg msg;
@@ -417,15 +501,15 @@ addon_id _addon_mount_from_folder( const char *folder, enum addon_type type, con
    else
    {
       char path_buf[ 4096 ];
-      vg_str folder_path;
-      vg_strnull( &folder_path, path_buf, sizeof(path_buf) );
-      vg_strcat( &folder_path, folder );
+      vg_str sub_path;
+      vg_strnull( &sub_path, path_buf, sizeof(path_buf) );
+      vg_strcat( &sub_path, folder_path );
 
       vg_dir subdir;
-      enum dir_open_result result = vg_dir_open( &subdir, folder_path.buffer );
+      enum dir_open_result result = vg_dir_open( &subdir, sub_path.buffer );
       if( result != k_dir_open_ok )
       {
-         vg_error( "Failed to open '%s' (%s)\n", folder_path.buffer, dir_open_result_str[result] );
+         vg_error( "Failed to open '%s' (%s)\n", sub_path.buffer, dir_open_result_str[result] );
          addon_fail_allocation();
          return 0;
       }
@@ -435,7 +519,7 @@ addon_id _addon_mount_from_folder( const char *folder, enum addon_type type, con
       vg_msg_init( &msg, reg->metadata, sizeof(reg->metadata) );
       vg_msg_frame( &msg, "workshop" );
       {
-         vg_msg_wkvstr( &msg, "title", reg->alias.foldername );
+         vg_msg_wkvstr( &msg, "title", reg->alias.folder );
          vg_msg_wkvstr( &msg, "author", "Custom Mod" );
       }
       vg_msg_end_frame( &msg );
@@ -446,7 +530,7 @@ addon_id _addon_mount_from_folder( const char *folder, enum addon_type type, con
          if( vg_dir_entry_type(&subdir) == k_vg_entry_type_file )
          {
             const char *fname = vg_dir_entry_name(&subdir);
-            vg_str file_path = folder_path;
+            vg_str file_path = sub_path;
             vg_strcat( &file_path, "/" );
             vg_strcat( &file_path, fname );
             if( !vg_strgood( &file_path ) ) 
@@ -522,7 +606,7 @@ void _addon_mount_content_folder( enum addon_type type, const char *base_folder,
          if( !vg_strgood( &folder ) ) 
             continue;
          
-         _addon_mount_from_folder( path_buf, type, content_ext );
+         _addon_mount_from_folder_path( path_buf, type, content_ext );
       }
    }
    vg_dir_close(&dir);
@@ -601,7 +685,7 @@ static void workshop_scan_t1( vg_async_task *co_task )
       {
          for( u32 j=0; j<_addon.registry_count; j ++ )
          {
-            if( _addon.registry[j].alias.workshop_id == co_info->workshop_ids[i] )
+            if( _addon.registry[j].alias.steam_workshop_id == co_info->workshop_ids[i] )
                goto s1;
          }
          
@@ -684,15 +768,15 @@ bool addon_get_content_folder( addon_id addon_id, vg_str *folder )
    VG_ASSERT( addon_id );
 
    addon_reg *reg = addon_details( addon_id );
-   if( reg->alias.workshop_id )
+   if( reg->alias.steam_workshop_id )
    {
       ISteamUGC *hSteamUGC = SteamAPI_SteamUGC();
       u64 _size;
       u32 _ts;
-      if( !SteamAPI_ISteamUGC_GetItemInstallInfo( hSteamUGC, reg->alias.workshop_id, &_size, 
+      if( !SteamAPI_ISteamUGC_GetItemInstallInfo( hSteamUGC, reg->alias.steam_workshop_id, &_size, 
                                                    folder->buffer, folder->len, &_ts ))
       {
-         vg_error( "GetItemInstallInfo failed for addon %lu\n", reg->alias.workshop_id );
+         vg_error( "GetItemInstallInfo failed for addon %lu\n", reg->alias.steam_workshop_id );
          return 0;
       }
 
@@ -706,183 +790,11 @@ bool addon_get_content_folder( addon_id addon_id, vg_str *folder )
          return 0;
 
       vg_strcat( folder, local_folder );
-      vg_strcat( folder, reg->alias.foldername );
-      return 1;
-   }
-}
-
-#if 0
-/*
- * write the full path of the addon's folder into the vg_str
- */
-#endif
-
-#if 0
-/*
- * cache section
- * --------------------------------------------------------------------------------------------------------------------
- */
-
-/*
- * Return existing cache id if reg_index points to a registry with its cache
- * already set.
- */
-
-/*
- * Get the real item data for cache id 
- */
-
-/*
- * Get the real item data for cache id ONLY if the item is completely loaded.
- */
-
-/* 
- * Updates the item state from the main thread
- */
-void async_addon_setstate( void *_entry, u32 _state )
-{
-   addon_cache_entry *entry = _entry;
-   SDL_LockMutex( _addon.cache_lock );
-   entry->state = _state;
-   SDL_UnlockMutex( _addon.cache_lock );
-   vg_success( "   loaded (%s)\n", entry->reg_ptr->alias.foldername );
-}
-
-/*
- * Handles the loading of an individual item
- */
-static int addon_cache_load_request( enum addon_type type, u16 id,
-                                     addon_reg *reg, vg_str folder ){
-   
-   /* load content files
-    * --------------------------------- */
-   vg_str content_path = folder;
-
-   vg_msg msg;
-   vg_msg_init( &msg, reg->metadata, reg->metadata_len );
-
-   const char *kv_content = vg_msg_getkvstr( &msg, "content" );
-   if( kv_content ){
-      vg_strcat( &content_path, "/" );
-      vg_strcat( &content_path, kv_content );
-   }
-   else{
-      vg_error( "   No content paths in metadata\n" );
-      return 0;
-   }
-
-   if( !vg_strgood( &content_path ) ) {
-      vg_error( "   Metadata path too long\n" );
-      return 0;
-   }
-   
-   if( type == k_addon_type_board )
-   {
-      struct player_board *board = addon_cache_item( type, id );
-      void *arena = addon_cache_item_arena( type, id );
-      vg_linear_clear( arena );
-
-      player_board_load( board, content_path.buffer, arena );
+      vg_strcat( folder, reg->alias.folder );
       return 1;
    }
-   else if( type == k_addon_type_player )
-   {
-      struct player_model *model = addon_cache_item( type, id );
-      void *arena = addon_cache_item_arena( type, id );
-      vg_linear_clear( arena );
-
-      player_model_load( model, content_path.buffer, arena );
-      return 1;
-   }
-   else {
-      return 0;
-   }
-
-   return 0;
-}
-
-/*
- * Goes over cache item load requests and calls the above ^
- */
-static void T1_addon_cache_load_loop(void *_)
-{
-   vg_info( "Running load loop\n" );
-   char path_buf[4096];
-
-   for( u32 type=0; type<k_addon_type_max; type++ )
-   {
-      struct addon_cache *cache = &_addon.cache[type];
-
-      for( u32 id=1; id<=cache->pool.count; id++ )
-      {
-         addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
-
-         SDL_LockMutex( _addon.cache_lock );
-         if( entry->state == k_addon_cache_state_load_request )
-         {
-            vg_info( "process cache load request (%u#%u, reg:%u)\n", type, id, entry->reg_index );
-
-            if( entry->reg_index >= addon_count(type,0,0) )
-            {
-               /* should maybe have a different value for this case */
-               entry->state = k_addon_cache_state_none;
-               SDL_UnlockMutex( _addon.cache_lock );
-               continue;
-            }
-
-            SDL_UnlockMutex( _addon.cache_lock );
-
-            /* continue with the request */
-            addon_reg *reg = get_addon_from_index( type, entry->reg_index, 0,0 );
-            entry->reg_ptr = reg;
-
-            vg_str folder;
-            vg_strnull( &folder, path_buf, 4096 );
-            if( addon_get_content_folder( reg, &folder, 1 ) )
-            {
-               if( addon_cache_load_request( type, id, reg, folder ) )
-               {
-                  vg_async_call( async_addon_setstate, entry, k_addon_cache_state_loaded );
-                  continue;
-               }
-            }
-            
-            vg_warn( "cache item did not load (%u#%u)\n", type, id );
-            SDL_LockMutex( _addon.cache_lock );
-            entry->state = k_addon_cache_state_none;
-            SDL_UnlockMutex( _addon.cache_lock );
-         }
-         else
-            SDL_UnlockMutex( _addon.cache_lock );
-      }
-   }
-}
-
-void addon_system_pre_update(void)
-{
-   if( !vg_loader_availible() ) return;
-
-   SDL_LockMutex( _addon.cache_lock );
-   for( u32 type=0; type<k_addon_type_max; type++ )
-   {
-      struct addon_cache *cache = &_addon.cache[type];
-
-      for( u32 id=1; id<=cache->pool.count; id++ )
-      {
-         addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
-         if( entry->state == k_addon_cache_state_load_request )
-         {
-            SDL_UnlockMutex( _addon.cache_lock );
-            vg_loader_start( T1_addon_cache_load_loop, NULL );
-            return;
-         }
-      }
-   }
-   SDL_UnlockMutex( _addon.cache_lock );
 }
 
-#endif
-
 void *addon_cache_item_arena( enum addon_type type, addon_cache_id id )
 {
    VG_ASSERT( id );
@@ -1080,6 +992,7 @@ addon_cache_id addon_cache_create_viewer( enum addon_type type, addon_id addon_i
 
       new_entry->state = k_addon_cache_state_load_request;
       new_entry->addon_id = addon_id;
+      new_entry->local_cpart[0] = '\0';
       reg->cache_id = cache_id;
    }
 
@@ -1089,21 +1002,28 @@ addon_cache_id addon_cache_create_viewer( enum addon_type type, addon_id addon_i
    return cache_id;
 }
 
-addon_cache_id addon_cache_create_viewer_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] )
+addon_id addon_get_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] )
 {
-   THREAD_0;
+   if( uid == NULL )
+      return 0;
 
    addon_alias q;
-   if( !addon_uid_to_alias( uid, &q ) ) 
+   if( !addon_parse_uid( uid, &q ) )
       return 0;
+
    if( q.type != type ) 
       return 0;
 
-   addon_id addon_id = _get_addon_by_alias( &q );
-   if( addon_id )
-   {
-      return addon_cache_create_viewer( type, addon_id );
-   }
+   return _get_addon_by_alias( &q );
+}
+
+addon_cache_id addon_cache_create_viewer_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] )
+{
+   THREAD_0;
+
+   addon_id id = addon_get_from_uid( type, uid );
+   if( id )
+      return addon_cache_create_viewer( type, id );
    else
    {
       vg_warn( "We dont have the addon '%s' installed.\n", uid );
index 2bfdc9c4229dd5b714e4c60c0bd074e460330134..555d8c2d93f2887f92f66fe0c527685ea34afdda 100644 (file)
@@ -14,8 +14,8 @@ typedef u16 addon_cache_id;
 struct addon_alias 
 {
    enum addon_type type;
-   PublishedFileId_t workshop_id;
-   char foldername[ ADDON_FOLDERNAME_MAX ];
+   PublishedFileId_t steam_workshop_id;
+   char folder[ ADDON_FOLDERNAME_MAX ];
 };
 
 struct _addon
@@ -23,7 +23,8 @@ struct _addon
    struct addon_reg
    {
       addon_alias alias;
-      u32 foldername_hash;
+      u32 alias_hash;
+
       u8 metadata[512];  /* vg_msg buffer */
       u32 metadata_len;
       u32 flags;
@@ -49,6 +50,8 @@ struct _addon
             k_addon_cache_state_errored
          }
          state;
+
+         char local_cpart[ ADDON_CPART_MAX ];
       }
       *allocs;
       vg_pool pool;
@@ -69,22 +72,23 @@ addon_id _addon_get_filtered( enum addon_type type, u32 index, u32 whitelist, u3
 bool _addon_get_filtered_index( enum addon_type type, addon_id id, u32 whitelist, u32 blacklist, u32 *out_index );
 addon_id _get_addon_by_alias( addon_alias *alias );
 bool addon_alias_eq( addon_alias *a, addon_alias *b );
-void addon_alias_uid( addon_alias *alias, char buf[ADDON_UID_MAX] );
-bool addon_uid_to_alias( const char *uid, addon_alias *alias );
+
+void addon_make_uid( addon_id id, char buf[ ADDON_UID_MAX ] );
+void addon_make_uid_cpart( addon_id id, char buf[ ADDON_UID_MAX ], char cpart[ ADDON_CPART_MAX ] );
+void addon_make_savedata_path( addon_id id, char out_buf[ 256 ] );
+bool addon_parse_uid( const char *uid, addon_alias *alias );
 bool addon_get_content_folder( addon_id addon_id, vg_str *folder );
 
 /* Loader thread only
  * Mount a specific addon from folder path
  */
-addon_id _addon_mount_from_folder( const char *folder, enum addon_type type, const char *content_ext );
+addon_id _addon_mount_from_folder_path( const char *folder, enum addon_type type, const char *content_ext );
 
 void _addon_mount_content_folder( enum addon_type type, const char *base_folder, const char *content_ext );
 void _mount_workshop_addons( vg_async_task *co_task );
 
-
-
-
 void _addon_system_pre_update(void);
+addon_id addon_get_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] );
 addon_cache_id addon_cache_create_viewer( enum addon_type type, addon_id addon_id );
 addon_cache_id addon_cache_create_viewer_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] );
 void addon_cache_watch( enum addon_type type, addon_cache_id cache_id );
index 476ecb40d123f5171218639cbd1de0b1558896ae..aebb9968eda765038058973d78a7da7ee7bfa663 100644 (file)
@@ -14,7 +14,7 @@ struct addon_type_info addon_type_infos[] =
       .local_content_folder = "playermodels/",
       .cache_stride = sizeof(struct player_model),
       .cache_count  = 20,
-      .item_arena_size = 1024 * 8
+      .item_arena_size = 1024 * 20
    },
    [k_addon_type_world] = {
       .local_content_folder = "maps/"
index 9b08870aa5b4a0752c252135a41572e58bf01bf9..e54fc74cae6da370fb2fe4632b6d88250257f955 100644 (file)
@@ -9,8 +9,11 @@ enum addon_type{
 };
 
 #define ADDON_FOLDERNAME_MAX 64
+#define ADDON_CPART_MAX      32
+#define ADDON_PREFIX_MAX     10
+#define ADDON_UID_MAX        128
+
 #define ADDON_MOUNTED_MAX 128 /* total count that we have knowledge of */
-#define ADDON_UID_MAX 76
 
 #define ADDON_REG_HIDDEN   0x1
 #define ADDON_REG_MTZERO   0x2
index c08aef32d2a8d6bd56015df978547a2e85e38807..59443790ea10bcfe2550c916c359edcce92ddb4b 100644 (file)
@@ -26,7 +26,7 @@ struct
       bool alive, in_cutscene;
       v3f co;
       v4f q;
-      u16 playermodel_view_slot;
+      player_model_view playermodel;
       struct player_effects_data effect_data;
       player_pose pose;
       m4x3f *final_mtx;
@@ -250,8 +250,8 @@ void _ent_npc_reset(void)
    for( u32 i=0; i<4; i++ )
    {
       struct human_npc *human = &_npc.humans[i];
-      addon_cache_unwatch( k_addon_type_player, human->playermodel_view_slot );
-      human->playermodel_view_slot = 0;
+      addon_cache_unwatch( k_addon_type_player, human->playermodel.cache_slot );
+      human->playermodel.cache_slot = 0;
       human->alive = 0;
    }
 }
@@ -452,9 +452,8 @@ void _ent_npc_render( vg_camera *cam )
          if( !((_cutscene.state == k_cutscene_state_playing) && human->in_cutscene) )
          {
             m4x3f *final_mtx = human->final_mtx;
-            struct player_model *model = addon_cache_item_data( k_addon_type_player, human->playermodel_view_slot, 1 );
             struct skeleton *sk = &localplayer.skeleton;
-            render_playermodel( cam, world, 0, model, sk, final_mtx );
+            render_playermodel( cam, world, 0, &human->playermodel, sk, final_mtx );
          }
       }
    }
@@ -607,7 +606,7 @@ entity_event_result ent_npc_event( ent_event *event )
                [k_npc_fbi] = "sr003-local-skaterift_fbi",
             };
             human->alive = 1;
-            human->playermodel_view_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, addons[npc_id] );
+            playermodel_set_from_uid( &human->playermodel, addons[npc_id] );
          }
          v3_copy( npc->transform.co, human->co );
          q_copy( npc->transform.q, human->q );
index 78b1a61e2e124d789f20670551e1a726373d5e92..efdf69c60dfbebcd29b9461c27c1f86e769fae67 100644 (file)
@@ -133,7 +133,8 @@ void ent_route_leaderboard_ui( ui_context *ctx, ui_rect ref_box, u32 route_index
    {
       board->cache_time = vg.time_real;
       char mod_uid[ ADDON_UID_MAX ];
-      addon_uid( _world.main.addon_id, mod_uid );
+
+      addon_make_uid( _world.main.addon_id, mod_uid );
       network_request_scoreboard( mod_uid, af_str( &world->meta.af, route->pstr_name ),
                                   NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK, route_index );
    }
index c270fa68b43956f23919bb7e87b5808dad3a29a2..c57f9940842b7c385a97541edf5dbf794293c9b5 100644 (file)
@@ -144,16 +144,43 @@ static void skateshop_accept_board( addon_cache_id cache_id )
    }
 }
 
-static void skateshop_accept_playermodel(void)
+void charshop_quitsave(void)
 {
-   addon_id id = _addon_get_filtered( k_addon_type_player, _skateshop.selected_player_index, 0, ADDON_REG_HIDDEN );
-   if( id )
+   _skateshop.open = 0;
+   gui_helper_reset( k_gui_helper_mode_clear );
+   skateshop_playermod( 0 );
+   srinput.state = k_input_state_resume;
+
+   struct addon_cache *cache = &_addon.cache[ k_addon_type_player ];
+   struct player_model *model = addon_cache_item_data( k_addon_type_player, localplayer.playermodel.cache_slot, 1 );
+   addon_cache_entry *cache_entry = vg_pool_item( &cache->pool, localplayer.playermodel.cache_slot );
+   addon_id id = cache_entry->addon_id;
+
+
+   if( model )
    {
-      player__use_model( id );
+      /* TODO: Make sure the addon_reg is marked as 'contented', since we are saving out data which might
+       *       get loaded back in.
+       *
+       *       (PROBABLY RARE UNLESS PLAYER HAS SHIT HARD DISK)
+       */
+      
+      if( model->flags & PLAYER_MODEL_FLAG_CUSTOMIZABLE )
+      {
+         memcpy( cache_entry->local_cpart, localplayer.playermodel.cpart, ADDON_CPART_MAX );
+
+         savedata_file file;
+         init_savefile( &file, "" );
+         addon_make_savedata_path( id, file.path );
+         vg_msg_wkvstr( &file.msg, "cpart", localplayer.playermodel.cpart );
+         write_savefile( &file, 1 );
+      }
+
       network_send_item( k_netmsg_playeritem_player );
    }
 }
 
+
 void ent_skateshop_update(void)
 {
    if( _world.event != k_world_event_shop )
@@ -168,10 +195,17 @@ void ent_skateshop_update(void)
       {
          if( world_clear_event( k_world_event_shop ) )
          {
-            _skateshop.open = 0;
-            gui_helper_reset( k_gui_helper_mode_clear );
-            skateshop_playermod( 0 );
-            srinput.state = k_input_state_resume;
+            if( shop->type == k_skateshop_type_charshop )
+            {
+               charshop_quitsave();
+            }
+            else
+            {
+               _skateshop.open = 0;
+               gui_helper_reset( k_gui_helper_mode_clear );
+               skateshop_playermod( 0 );
+               srinput.state = k_input_state_resume;
+            }
             return;
          }
       }
@@ -238,44 +272,7 @@ void ent_skateshop_update(void)
             return;
          }
       }
-      else if( shop->type == k_skateshop_type_charshop )
-      {
-         int changed = 0;
-         u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
-
-         if( button_down( k_srbind_mleft ) )
-         {
-            if( _skateshop.selected_player_index > 0 )
-               _skateshop.selected_player_index --;
-            else
-               _skateshop.selected_player_index = valid_count-1;
-
-            changed = 1;
-         }
-
-         if( button_down( k_srbind_mright ) )
-         {
-            if( _skateshop.selected_player_index+1 < valid_count )
-               _skateshop.selected_player_index ++;
-            else
-               _skateshop.selected_player_index = 0;
-
-            changed = 1;
-         }
-
-         if( changed )
-            skateshop_accept_playermodel();
-
-         if( button_down( k_srbind_maccept ) )
-         {
-            if( world_clear_event( k_world_event_shop ) )
-            {
-               gui_helper_reset( k_gui_helper_mode_clear );
-               skateshop_playermod( 0 );
-               _skateshop.open = 0;
-            }
-         }
-      }
+      else if( shop->type == k_skateshop_type_charshop ) {} 
       else if( shop->type == k_skateshop_type_server )
       {
          f64 delta = vg.time_real - network_client.last_intent_change;
@@ -539,6 +536,138 @@ entity_event_result ent_skateshop_event( ent_event *event )
    else return k_entity_event_result_unhandled;
 }
 
+void charshop_gui( ui_context *ctx )
+{
+   ctx->font = &vgf_default_large;
+   int ml = button_press( k_srbind_mleft ),
+       mr = button_press( k_srbind_mright ),
+       mu = button_press( k_srbind_mup ),
+       md = button_press( k_srbind_mdown ),
+       mh = ml-mr,
+       mv = mu-md,
+       enter = button_down( k_srbind_maccept );
+
+   i32 R = menu_nav( &_skateshop.charshop_row, mv, 8 );
+
+   ui_rect panel = { 8, 8, 350, vg.window_y };
+   ui_fill( ctx, panel, ui_opacity( GUI_COL_DARK, 0.35f ) );
+   ui_outline( ctx, panel, 1, GUI_COL_NORM, 0 );
+   ui_rect_pad( panel, (ui_px[]){8,8} );
+   
+   ui_rect row;
+   menu_standard_widget( ctx, panel, row, 1 );
+
+   ui_rect bL, bR, middle;
+   ui_split( row, k_ui_axis_v, row[3], 4, bL, middle );
+   ui_split( middle, k_ui_axis_v, -row[3], 4, middle, bR );
+
+   bool changed_playermodel = 0;
+
+   if( menu_button_rect( ctx, bL, 0, 1, "<" ) )
+   {
+      u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
+      if( _skateshop.selected_player_index > 0 )
+         _skateshop.selected_player_index --;
+      else
+         _skateshop.selected_player_index = valid_count-1;
+      changed_playermodel = 1;
+   }
+
+   if( menu_button_rect( ctx, bR, 0, 1, ">" ) )
+   {
+      u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
+      if( _skateshop.selected_player_index+1 < valid_count )
+         _skateshop.selected_player_index ++;
+      else
+         _skateshop.selected_player_index = 0;
+      changed_playermodel = 1;
+   }
+
+   if( changed_playermodel )
+   {
+      addon_id id = _addon_get_filtered( k_addon_type_player, _skateshop.selected_player_index, 0, ADDON_REG_HIDDEN );
+      if( id )
+         player__use_model( id );
+   }
+
+   struct player_model *model = addon_cache_item_data( k_addon_type_player, localplayer.playermodel.cache_slot, 1 );
+   if( model )
+   {
+      if( model->flags & PLAYER_MODEL_FLAG_CUSTOMIZABLE )
+      {
+         menu_heading( ctx, panel, "Customize", 0 );
+
+         player_model_view *view = &localplayer.playermodel;
+         bool edited = 0;
+
+         u32 prop_count = af_arrcount( &model->editer_property );
+         if( prop_count > VG_ARRAY_LEN(view->property_values) )
+         {
+            ctx->font = &vgf_default_small;
+            menu_heading( ctx, panel, "error: too many lists in playermodel!", ui_colour( ctx, k_ui_red ) );
+            ctx->font = &vgf_default_large;
+
+            prop_count = VG_ARRAY_LEN(view->property_values);
+         }
+
+         for( u32 i=0; i<prop_count; i ++ )
+         {
+            editer_property *prop = af_arritm( &model->editer_property, i );
+            const char *alias = af_str( &model->mdl.af, prop->pstr_alias );
+
+            if( prop->ui_type == k_editer_type_toggle )
+            {
+               i32 temp = view->property_values[i]._u32;
+               if( menu_checkbox( ctx, panel, 0, alias, &temp ) )
+               {
+                  view->property_values[i]._u32 = temp;
+                  edited = 1;
+               }
+            }
+            else if( prop->ui_type == k_editer_type_slider )
+            {
+               f32 normalized = view->property_values[i]._f32 / prop->max._f32;
+               if( menu_slider( ctx, panel, 0, alias, 0, 25, &normalized, "%.0f" ) )
+               {
+                  view->property_values[i]._f32 = normalized * prop->max._f32;
+                  edited = 1;
+               }
+            }
+            else if( prop->ui_type == k_editer_type_selecter )
+            {
+               const char *options = af_str( &model->mdl.af, prop->max.pstr_options );
+               if( menu_options( ctx, panel, 0, 0, alias, options, &view->property_values[i]._u32 ) )
+                  edited = 1;
+            }
+            else
+            {
+               ctx->font = &vgf_default_small;
+               menu_heading( ctx, panel, "Unknown parameter type", ui_colour( ctx, k_ui_red ) );
+               ctx->font = &vgf_default_large;
+            }
+         }
+
+         if( edited )
+         {
+            playermodel_get_cpart( view, model, view->cpart );
+            vg_info( "New cpart: %s\n", view->cpart );
+            view->cpart_dirty = 1;
+         }
+      }
+      else
+         menu_heading( ctx, panel, "Uncustomizable", 0 );
+   }
+
+   if( menu_button( ctx, panel, 0, model!=NULL, "Exit" ) )
+   {
+      if( world_clear_event( k_world_event_shop ) )
+      {
+         charshop_quitsave();
+      }
+   }
+   ctx->font = &vgf_default_small;
+}
+
 void ent_skateshop_gui( ui_context *ctx )
 {
    if( _world.event == k_world_event_shop )
@@ -549,10 +678,17 @@ void ent_skateshop_gui( ui_context *ctx )
             return;
 
          ent_skateshop *shop = _skateshop.current_shop;
-         if( shop->type != k_skateshop_type_boardshop && shop->type != k_skateshop_type_charshop )
+         if( (shop->type != k_skateshop_type_boardshop) && (shop->type != k_skateshop_type_charshop) )
             return;
 
          ui_capture_mouse(ctx, 1);
+
+         if( shop->type == k_skateshop_type_charshop )
+         {
+            charshop_gui( ctx );
+            return;
+         }
+
          world_instance *world = &_world.main;
          ent_marker *rack_marker = af_arritm( &world->ent_marker, mdl_entity_id_id(shop->boards.id_rack) );
          m4x3f mmdl;
@@ -561,12 +697,6 @@ void ent_skateshop_gui( ui_context *ctx )
 
          v4f handles[3] = {{ 2.0f, 0.0f, 0.0f, 1.0f },{ -2.0f, 0.0f, 0.0f, 1.0f },{0,0,0,1}};
 
-         if( shop->type == k_skateshop_type_charshop )
-         {
-            handles[0][0] = -1.2f;
-            handles[1][0] =  1.2f;
-         }
-
          for( u32 i=0; i<3; i ++ )
          {
             m4x3_mulv( mmdl, handles[i], handles[i] );
@@ -585,7 +715,6 @@ void ent_skateshop_gui( ui_context *ctx )
          ctx->font = &vgf_default_title;
 
          u32 original_page;
-         bool changed_playermodel = 0;
 
          if( shop->type == k_skateshop_type_boardshop )
             original_page = _skateshop.selected_board_index/SKATESHOP_VIEW_SLOT_MAX;
@@ -597,17 +726,6 @@ void ent_skateshop_gui( ui_context *ctx )
                if( _skateshop.selected_board_index > 0 )
                   _skateshop.selected_board_index --;
             }
-            else if( shop->type == k_skateshop_type_charshop )
-            {
-               u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
-
-               if( _skateshop.selected_player_index > 0 )
-                  _skateshop.selected_player_index --;
-               else
-                  _skateshop.selected_player_index = valid_count-1;
-
-               changed_playermodel = 1;
-            }
          }
          if( menu_button_rect( ctx, right_box, 0,1, ">" ) )
          {
@@ -617,16 +735,6 @@ void ent_skateshop_gui( ui_context *ctx )
                if( _skateshop.selected_board_index+1 < valid_count )
                   _skateshop.selected_board_index ++;
             }
-            else if( shop->type == k_skateshop_type_charshop )
-            {
-               u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
-               if( _skateshop.selected_player_index+1 < valid_count )
-                  _skateshop.selected_player_index ++;
-               else
-                  _skateshop.selected_player_index = 0;
-
-               changed_playermodel = 1;
-            }
          }
 
          if( shop->type == k_skateshop_type_boardshop )
@@ -635,13 +743,6 @@ void ent_skateshop_gui( ui_context *ctx )
             if( original_page != new_page )
                skateshop_update_viewpage();
          }
-         else if( shop->type == k_skateshop_type_charshop )
-         {
-            if( changed_playermodel )
-            {
-               skateshop_accept_playermodel();
-            }
-         }
 
          if( menu_button_rect( ctx, accept_box, 0,1, "Pick" ) )
          {
@@ -651,16 +752,6 @@ void ent_skateshop_gui( ui_context *ctx )
                if( cache_id )
                   skateshop_accept_board( cache_id );
             }
-            else if( shop->type == k_skateshop_type_charshop )
-            {
-               if( world_clear_event( k_world_event_shop ) )
-               {
-                  _skateshop.open = 0;
-                  gui_helper_reset( k_gui_helper_mode_clear );
-                  skateshop_playermod( 0 );
-                  srinput.state = k_input_state_resume;
-               }
-            }
          }
 
          ctx->font = &vgf_default_small;
index b913ed18b7a94a045ab2870c5668b15ff0da4733..47542650f40b5b43e84512a7bd6a4195f655149a 100644 (file)
@@ -37,6 +37,8 @@ struct global_skateshop
 
    ent_skateshop *current_shop;
    bool open;
+
+   i32 charshop_row;
 }
 extern _skateshop;
 
index 802f8fdcd73be6f434921332722894a39c4dbc01..10ca6b495030aedfe2ea3d32e0cd212c41f8e8dc 100644 (file)
@@ -40,6 +40,8 @@ typedef struct file_entity_ref file_entity_ref;
 typedef struct ent_glider ent_glider;
 typedef struct ent_npc ent_npc;
 typedef struct ent_script ent_script;
+typedef struct editer_property editer_property;
+typedef struct editer_item editer_item;
 
 enum entity_alias{
    k_ent_none        = 0,
@@ -57,7 +59,7 @@ enum entity_alias{
    k_ent_skateshop   = 12,
    k_ent_camera      = 13,
    k_ent_swspreview  = 14,
-   k_ent_menuitem    = 15,
+   k_ent_deleted1    = 15, //k_ent_menuitem    = 15,
    k_ent_worldinfo   = 16,
    k_ent_ccmd        = 17,
    k_ent_objective   = 18,
@@ -75,6 +77,9 @@ enum entity_alias{
    k_ent_atom        = 30,
    k_ent_cutscene    = 31,
    k_ent_light       = 32,
+   k_mdl_mesh        = 33,
+   k_editer_property = 34,
+   k_editer_item     = 35,
    k_ent_max
 };
 
@@ -95,7 +100,7 @@ const char *_entity_alias_str[] =
    [k_ent_skateshop] = "ent_skateshop",
    [k_ent_camera] = "ent_camera",
    [k_ent_swspreview] = "ent_swspreview",
-   [k_ent_menuitem] = "ent_menuitem",
+   //[k_ent_menuitem] = "ent_menuitem",
    [k_ent_worldinfo] = "ent_worldinfo",
    [k_ent_ccmd] = "ent_ccmd",
    [k_ent_objective] = "ent_objective",
@@ -477,80 +482,6 @@ fix_ent_camera_v107( struct ent_camera_v107 *old, ent_camera *new )
 
 #endif
 
-enum ent_menuitem_type{
-   k_ent_menuitem_type_visual       = 0,
-   k_ent_menuitem_type_event_button = 1,
-   k_ent_menuitem_type_page_button  = 2,
-   k_ent_menuitem_type_toggle       = 3,
-   k_ent_menuitem_type_slider       = 4,
-   k_ent_menuitem_type_page         = 5,
-   k_ent_menuitem_type_binding      = 6,
-   k_ent_menuitem_type_visual_nocol = 7,
-   k_ent_menuitem_type_disabled     = 90
-};
-
-enum ent_menuitem_stack_behaviour{
-   k_ent_menuitem_stack_append  = 0,
-   k_ent_menuitem_stack_replace = 1
-};
-
-typedef struct ent_menuitem ent_menuitem;
-struct ent_menuitem{
-   u32 type, groups, 
-       id_links[4];  /* ent_menuitem */
-   f32 factive, fvisible;
-
-   mdl_transform transform;
-   u32 submesh_start, submesh_count;
-
-   union{ u64 _u64;  /* force storage for 64bit pointers */
-          i32 *pi32;
-          f32 *pf32;
-          void *pvoid;
-   };
-
-   union{
-      struct{
-         u32 pstr_name;
-      }
-      visual;
-
-      struct{
-         u32 id_min,    /* ent_marker */
-             id_max,    /* . */
-             id_handle, /* ent_menuitem */
-             pstr_data;
-      }
-      slider;
-
-      struct{
-         u32 pstr,
-             stack_behaviour;
-      }
-      button;
-
-      struct{
-         u32 id_check, /* ent_menuitem */
-             pstr_data;
-         v3f offset; /* relative to parent */
-      }
-      checkmark;
-
-      struct{
-         u32 pstr_name, 
-             id_entrypoint,  /* ent_menuitem */
-             id_viewpoint;   /* ent_camera */
-      }
-      page;
-
-      struct{
-         u32 pstr_bind,
-             font_variant;
-      }
-      binding;
-   };
-};
-
 enum world_flag
 {
    k_world_flag_fixed_time = 0x1,
@@ -745,6 +676,39 @@ struct ent_prop
    u32 submesh_start, submesh_count, flags, pstr_alias;
 };
 
+enum editer_type
+{
+   k_editer_type_toggle = 0,
+   k_editer_type_slider = 1,
+   k_editer_type_selecter = 2
+};
+
+struct editer_property
+{
+   u32 pstr_alias;
+   u8  ui_type;
+
+   union 
+   {
+      u32 _u32;
+      f32 _f32;
+      u32 pstr_options;
+   }
+   max;
+};
+
+struct editer_item
+{
+   mdl_transform transform;
+   u32 submesh_start, submesh_count;
+
+   u32 pstr_visibility,
+       pstr_uv_x,
+       pstr_uv_y;
+   u16 discard_send,
+       discard_mask;
+};
+
 struct ent_region 
 {
    mdl_transform transform;
index f611ba29529a4cb7aa9e0a8f63ecacbfe757f373..bf8017a2ead493ee21c58776182cb707a2c596e4 100644 (file)
@@ -74,7 +74,7 @@ void menu_on_world_change( addon_id addon )
    if( vg_msg_seekframe( &msg, "workshop" ) )
       name = vg_msg_getkvstr( &msg, "title" );
    if( !name )
-      name = reg->alias.foldername;
+      name = reg->alias.folder;
    menu.clicked_world_name = name;
    world_map_initialize_view();
 }
@@ -135,15 +135,15 @@ static void menu_decor_select( ui_context *ctx, ui_rect rect )
    ui_text( ctx, a1, "\x93", 1, k_ui_align_middle_center, 0 );
 }
 
-static void menu_standard_widget( ui_context *ctx, ui_rect inout_panel, ui_rect rect, ui_px s )
+void menu_standard_widget( ui_context *ctx, ui_rect inout_panel, ui_rect rect, ui_px s )
 {
    ui_split( inout_panel, k_ui_axis_h, ctx->font->sy*s*2, 8, rect, inout_panel );
 }
 
-static bool menu_slider( ui_context *ctx,
-                         ui_rect inout_panel, bool select, const char *label,
-                         const f32 disp_min, const f32 disp_max, f32 *value, 
-                         const char *format )
+bool menu_slider( ui_context *ctx,
+                  ui_rect inout_panel, bool select, const char *label,
+                  const f32 disp_min, const f32 disp_max, f32 *value, 
+                  const char *format )
 {
    ui_rect rect, box;
    menu_standard_widget( ctx, inout_panel, rect, 1 );
@@ -251,14 +251,14 @@ bool menu_button_rect( ui_context *ctx, ui_rect rect, bool select, bool clickabl
    else return 0;
 }
 
-static bool menu_button( ui_context *ctx, ui_rect inout_panel, bool select, bool clickable, const char *text )
+bool menu_button( ui_context *ctx, ui_rect inout_panel, bool select, bool clickable, const char *text )
 {
    ui_rect rect;
    menu_standard_widget( ctx, inout_panel, rect, 1 );
    return menu_button_rect( ctx, rect, select, clickable, text );
 }
 
-static bool menu_checkbox( ui_context *ctx, ui_rect inout_panel, bool select, const char *str_label, i32 *data )
+bool menu_checkbox( ui_context *ctx, ui_rect inout_panel, bool select, const char *str_label, i32 *data )
 {
    ui_rect rect, label, box;
    menu_standard_widget( ctx, inout_panel, rect, 1 );
@@ -324,7 +324,88 @@ static bool menu_checkbox( ui_context *ctx, ui_rect inout_panel, bool select, co
    else return 0;
 }
 
-static void menu_heading( ui_context *ctx, ui_rect inout_panel, const char *label, u32 colour )
+bool menu_options( ui_context *ctx, ui_rect inout_panel, bool select, i32 subselect_x, const char *str_label, 
+                   const char *options, u32 *data )
+{
+   ui_rect rect, box;
+   menu_standard_widget( ctx, inout_panel, rect, 1 );
+
+   if( vg_input.display_input_method == k_input_method_controller )
+   {
+      if( select )
+      {
+         menu_decor_select( ctx, rect );
+      }
+   }
+
+   ui_label( ctx, rect, str_label, 1, 8, box );
+
+   bool edited = 0;
+
+   u32 option_count = strlen( options );
+   f32 w = (f32)box[2] / (f32)option_count;
+      
+   char buf[2];
+   buf[0] = '?';
+   buf[1] = '\0';
+
+   for( u32 j=0; j<option_count; j ++ )
+   {
+      ui_px x  = w * (f32)j,
+            x1 = ceilf( w * (f32)(j+1) );
+
+      u32 cv = options[j] - (u32)'A';
+      i32 temp = *data == cv;
+      ui_rect chex = { box[0] + x, box[1], (x1-x), box[3] };
+
+      enum ui_button_state state = k_ui_button_none;
+      if( vg_input.display_input_method == k_input_method_kbm )
+         state = ui_checkbox_base( ctx, chex, &temp );
+
+      if( state == k_ui_button_holding_inside )
+      {
+         ui_fill( ctx, chex, GUI_COL_ACTIVE );
+         ui_outline( ctx, chex, -1, GUI_COL_CLICK, 0 );
+      }
+      else if( state == k_ui_button_holding_outside )
+      {
+         ui_fill( ctx, chex, GUI_COL_DARK );
+         ui_outline( ctx, chex, -1, GUI_COL_CLICK, 0 );
+      }
+      else if( state == k_ui_button_hover )
+      {
+         ui_fill( ctx, chex, GUI_COL_ACTIVE );
+         ui_outline( ctx, chex, -1, GUI_COL_HI, 0 );
+      }
+      else 
+      {
+         ui_fill( ctx, chex, select? GUI_COL_ACTIVE: GUI_COL_DARK );
+         ui_outline( ctx, chex, -1, select? GUI_COL_HI: GUI_COL_NORM, 0 );
+      }
+
+      if( temp )
+      {
+         ui_rect_pad( chex, (ui_px[2]){2,2} );
+         ui_fill( ctx, chex, GUI_COL_HI );
+      }
+
+      if( state == k_ui_button_click )
+      {
+         *data = cv;
+         edited = 1;
+         vg_audio_lock();
+         vg_audio_oneshot( &audio_ui[0], 1.0f, 0.0f, 0, 0 );
+         vg_audio_unlock();
+      }
+
+      buf[0] = options[j];
+      ui_text( ctx, chex, buf, 1, k_ui_align_middle_center, temp? ui_colour( ctx, k_ui_bg ): 0 );
+   }
+
+   return edited;
+}
+
+void menu_heading( ui_context *ctx, ui_rect inout_panel, const char *label, u32 colour )
 {
    ui_rect rect;
    menu_standard_widget( ctx, inout_panel, rect, 1 );
@@ -497,7 +578,7 @@ void menu_update_world_list(void)
             name = vg_msg_getkvstr( &msg, "title" );
 
          if( !name )
-            name = reg->alias.foldername;
+            name = reg->alias.folder;
 
          menu.world_list_names[ menu.world_list_display_count ] = name;
          menu.world_list_entries[ menu.world_list_display_count ] = addon_id;
@@ -764,8 +845,7 @@ void menu_gui( ui_context *ctx )
       ui_rect title;
       ui_split( panel, k_ui_axis_h, 28*2, 0, title, panel );
       ctx->font = &vgf_default_title;
-      ui_text( ctx, title, "Content is in the full game.", 
-               1, k_ui_align_middle_center, 0 );
+      ui_text( ctx, title, "Content is in the full game.", 1, k_ui_align_middle_center, 0 );
 
       ui_split( panel, k_ui_axis_h, 28, 0, title, panel );
       ctx->font = &vgf_default_large;
index 16869f31497a386fed83c9a89a0816dcdd6692fa..6d6c7285a95704e6ec19be6cb7004131eaa3c769 100644 (file)
@@ -87,7 +87,19 @@ bool menu_viewing_map(void);
 bool menu_viewing_preview(void);
 void menu_update_world_list(void);
 void menu_on_world_change( addon_id addon );
+void menu_heading( ui_context *ctx, ui_rect inout_panel, const char *label, u32 colour );
+bool menu_button( ui_context *ctx, ui_rect inout_panel, bool select, bool clickable, const char *text );
 bool menu_button_rect( ui_context *ctx, ui_rect rect, bool select, bool clickable, const char *text );
 //util
 i32 menu_nav( i32 *p_row, int mv, i32 max );
 void menu_close(void);
+
+
+void menu_standard_widget( ui_context *ctx, ui_rect inout_panel, ui_rect rect, ui_px s );
+bool menu_slider( ui_context *ctx,
+                  ui_rect inout_panel, bool select, const char *label,
+                  const f32 disp_min, const f32 disp_max, f32 *value, 
+                  const char *format );
+bool menu_checkbox( ui_context *ctx, ui_rect inout_panel, bool select, const char *str_label, i32 *data );
+bool menu_options( ui_context *ctx, ui_rect inout_panel, bool select, i32 subselect_x, const char *str_label, 
+                   const char *options, u32 *data );
index 3ebb1613f64af94d07020b8c31893e9685f9dde5..0059a562ad8bdb42a6a910a04fc3502814ba109d 100644 (file)
@@ -331,6 +331,7 @@ void cutscene_render_instance( struct cs_instance *ins, world_instance *world, v
    shader_model_character_view_uPv( cam->mtx.pv );
    shader_model_character_view_uDepthMode( 0 );
    shader_model_character_view_uShadeless( 0 );
+   shader_model_character_view_uUvOffset( (v2f){0,0} );
 
    WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_character_view );
 
index 961f80a63406ffc3343fa8eaca669ce8b29002b7..3352b182584393827ab1b1db303086a6084db30b 100644 (file)
@@ -450,9 +450,7 @@ void mdl_async_load_glmesh( mdl_context *mdl, glmesh *mesh, u32 *fixup_table )
             mdl_vert *vert = &job->verts[i];
             
             for( u32 j=0; j<4; j++ )
-            {
                vert->groups[j] = fixup_table[vert->groups[j]];
-            }
          }
       }
 
@@ -496,6 +494,8 @@ void mdl_async_load_glmesh( mdl_context *mdl, glmesh *mesh, u32 *fixup_table )
 /* uploads the glmesh, and textures. everything is saved into the mdl_context */
 void mdl_async_full_load_std( mdl_context *mdl, u32 *fixup_table )
 {
+   THREAD_1;
+
    mdl_async_load_glmesh( mdl, &mdl->mesh, fixup_table );
    
    for( u32 i=0; i<mdl->texture_count; i ++ )
index 818ca121009440df1c24b0fc9f31adecfc037317..79a110bedc9d12ec4bbd7ce141bcf2fda39ab83c 100644 (file)
@@ -239,7 +239,7 @@ struct mdl_mesh
    u32 submesh_start,
        submesh_count,
        pstr_name,
-       entity_id,    /* upper 16 bits: type, lower 16 bits: index */
+       entity_id,    /* upper 16 bits: type, lower 16 bits: index. hgn: 11.06.2025 Is this still used??? */
        armature_id;
 };
 
index 6e60ecb32bcdf3ed3800c2513ab2330ca70f1a13..1e36561232ca751e08ac4d34d8f8e35479f3dcdc 100644 (file)
@@ -149,7 +149,7 @@ void network_send_item( enum netmsg_playeritem_type type )
 
    if( type == k_netmsg_playeritem_world0 )
    {
-      addon_uid( _world.main.addon_id, item->uid );
+      addon_make_uid( _world.main.addon_id, item->uid );
    }
    else
    {
@@ -162,13 +162,21 @@ void network_send_item( enum netmsg_playeritem_type type )
       }
       else if( type == k_netmsg_playeritem_player )
       {
-         view_id = localplayer.playermodel_view_slot;
+         view_id = localplayer.playermodel.cache_slot;
          addon_type = k_addon_type_player;
       }
 
-      struct addon_cache *cache = &_addon.cache[addon_type];
-      addon_cache_entry *entry = vg_pool_item( &cache->pool, view_id );
-      addon_uid( entry->addon_id, item->uid );
+      if( view_id )
+      {
+         struct addon_cache *cache = &_addon.cache[addon_type];
+         addon_cache_entry *entry = vg_pool_item( &cache->pool, view_id );
+         addon_make_uid_cpart( entry->addon_id, item->uid, entry->local_cpart );
+      }
+      else
+      {
+         vg_error( "Try to send addon type %d, but the view id is 0\n", type );
+         return;
+      }
    }
 
    vg_info( "send equip: [%u] %s\n", item->type_index, item->uid );
index 54309971509c378a7e2ceb784da5c810f25b9e0d..167e90bd19dcaf4e46b7499940a2af3b1873b279 100644 (file)
@@ -116,8 +116,8 @@ void player__debugtext( ui_context *ctx, int size, const char *fmt, ... )
 
 void player__use_model( addon_id addon_id )
 {
-   addon_cache_unwatch( k_addon_type_player, localplayer.playermodel_view_slot );
-   localplayer.playermodel_view_slot = addon_cache_create_viewer( k_addon_type_player, addon_id );
+   addon_cache_unwatch( k_addon_type_player, localplayer.playermodel.cache_slot );
+   localplayer.playermodel.cache_slot = addon_cache_create_viewer( k_addon_type_player, addon_id );
 }
 
 void player__bind(void)
@@ -287,13 +287,13 @@ void player__im_gui( ui_context *ctx )
    g_player_debugger[2] = 300;
    g_player_debugger[3] = 32;
 
-   char buf[96];
+   char uid[ ADDON_UID_MAX ];
    if( _world.main.addon_id )
-      addon_uid( _world.main.addon_id, buf );
+      addon_make_uid( _world.main.addon_id, uid );
    else
-      strcpy( buf, "none" );
-   player__debugtext( ctx, 1, "world #%u: %s", 0, buf );
+      strcpy( uid, "none" );
 
+   player__debugtext( ctx, 1, "world #%u: %s", 0, uid );
    player__debugtext( ctx, 2, "director" );
    player__debugtext( ctx, 1, "client activity: %s", 
                      (const char *[]){ [k_skaterift_menu]      = "menu",
index b04df1f8f2fb3e9d8e5e979f444658283fd7dbf7..76b2d1b9de3a79fc0bdbded5979524a23f9c6ef0 100644 (file)
@@ -116,7 +116,8 @@ struct localplayer
    struct player_model    fallback_model;
    struct player_board    fallback_board;
 
-   u16 board_view_slot, playermodel_view_slot;
+   u16 board_view_slot;
+   player_model_view      playermodel;
 
    player_pose            pose;
    player_pose            holdout_pose;
index 7bb2a46fda1a1f626243c5cee59695adba2c80b9..7de565d51c9e9d52b863cafacf8837583493c105 100644 (file)
@@ -18,7 +18,7 @@ static i32 k_show_own_name = 0;
 static void player_remote_clear( u32 player_index )
 {
    struct network_player *player = &netplayers.list[ player_index ];
-   addon_cache_unwatch( k_addon_type_player, player->playermodel_view_slot );
+   addon_cache_unwatch( k_addon_type_player, player->playermodel.cache_slot );
    addon_cache_unwatch( k_addon_type_board, player->board_view_slot );
 
    bool wanted_to_spec_maybe = player->flag_spectate;
@@ -42,7 +42,7 @@ static void relink_remote_player_worlds( u32 client_id )
    struct network_player *player = &netplayers.list[client_id];
 
    addon_alias q;
-   addon_uid_to_alias( player->items[k_netmsg_playeritem_world0], &q );
+   addon_parse_uid( player->items[k_netmsg_playeritem_world0], &q );
 
    addon_reg *current_world_reg = addon_details( _world.main.addon_id );
    if( addon_alias_eq( &q, &current_world_reg->alias ) )
@@ -163,9 +163,8 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg )
          player->steamid = playerjoin->steamid;
          player_remote_update_friendflags( player );
 
-         /* TODO: interpret the uids */
          player->board_view_slot = 0;
-         player->playermodel_view_slot = 0;
+         player->playermodel.cache_slot = 0;
 
          struct interp_buffer *buf = &netplayers.interp_data[playerjoin->index];
          buf->t = -99999999.9;
@@ -364,8 +363,12 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg )
       }
       else if( item->type_index == k_netmsg_playeritem_player )
       {
-         addon_cache_unwatch( k_addon_type_player, player->playermodel_view_slot );
-         player->playermodel_view_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, uid );
+         addon_cache_unwatch( k_addon_type_player, player->playermodel.cache_slot );
+         player->playermodel.cache_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, uid );
+
+         char cpart[ ADDON_CPART_MAX ];
+         if( addon_uid_get_custom_part( uid, cpart ) )
+            playermodel_use_cpart( &player->playermodel, cpart );
       }
       else if( item->type_index == k_netmsg_playeritem_world0 )
       {
@@ -730,8 +733,7 @@ void render_remote_players( world_instance *world, vg_camera *cam )
       struct network_player *player = &netplayers.list[index];
       m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_count*index ];
 
-      struct player_model *model = addon_cache_item_data( k_addon_type_player, player->playermodel_view_slot, 1 );
-      render_playermodel( cam, world, 0, model, sk, final_mtx );
+      render_playermodel( cam, world, 0, &player->playermodel, sk, final_mtx );
 
       struct player_board *board = addon_cache_item_data( k_addon_type_board, player->board_view_slot, 1 );
       render_board( cam, world, board, final_mtx[localplayer.id_board], 
index 0d6a7def2c9afd1f9f413fbcba53d7345f7f6ad2..e44208b94d8a92c52383af3d7ff7a52ea69022f1 100644 (file)
@@ -20,9 +20,10 @@ struct global_netplayers
    {
       int active, isfriend, isblocked;
       u64 steamid;
-      u16 board_view_slot, playermodel_view_slot;
-      enum player_subsystem subsystem;
+      u16 board_view_slot;
+      player_model_view playermodel;
 
+      enum player_subsystem subsystem;
       bool flag_spectate;
 
       bool same_world;
index ab42b481d9ecf30bbd7f58e29bf4d9f14ecd6cd9..cf947d0b94ab6e8229ae08cd361d725dbfc03721 100644 (file)
@@ -173,8 +173,15 @@ void player_board_unload( struct player_board *board )
 
 void player_model_load( player_model *pm, const char *path, void *arena )
 {
+   pm->flags = 0x0;
+
    mdl_open( &pm->mdl, path, arena );
    mdl_load_metadata_block( &pm->mdl, arena );
+   AF_LOAD_ARRAY_STRUCT( &pm->mdl.af, &pm->editer_property, editer_property, arena );
+   AF_LOAD_ARRAY_STRUCT( &pm->mdl.af, &pm->editer_item, editer_item, arena );
+
+   if( af_arrcount( &pm->editer_item ) )
+      pm->flags |= PLAYER_MODEL_FLAG_CUSTOMIZABLE;
 
    VG_ASSERT( pm->mdl.armature_count );
    mdl_armature *armature = &pm->mdl.armatures[ 0 ];
@@ -469,12 +476,19 @@ void render_board( vg_camera *cam, world_instance *world,
 
 void render_playermodel( vg_camera *cam, world_instance *world,
                          int depth_compare,
-                         player_model *model,
+                         player_model_view *view,
                          struct skeleton *skeleton,
                          m4x3f *final_mtx )
 {
+   bool fallback = 0;
+   struct player_model *model = addon_cache_item_data( k_addon_type_player, view->cache_slot, 1 );
    if( !model ) 
-      model = &localplayer.fallback_model;
+   {
+      model = view->fallback;
+      if( !model )
+         model = &localplayer.fallback_model;
+      fallback = 1;
+   }
    
    shader_model_character_view_use();
 
@@ -487,6 +501,8 @@ void render_playermodel( vg_camera *cam, world_instance *world,
    shader_model_character_view_uPv( cam->mtx.pv );
    shader_model_character_view_uDepthMode( depth_compare );
    shader_model_character_view_uShadeless( 0 );
+   shader_model_character_view_uUvOffset( (v2f){0,0} );
+
    if( depth_compare )
    {
       depth_compare_bind(
@@ -504,14 +520,38 @@ void render_playermodel( vg_camera *cam, world_instance *world,
                          (const GLfloat *)final_mtx );
    
    mesh_bind( &model->mdl.mesh );
-   mesh_draw( &model->mdl.mesh );
+
+   if( !fallback && (model->flags & PLAYER_MODEL_FLAG_CUSTOMIZABLE) )
+   {
+      if( view->cpart_dirty )
+      {
+         playermodel_create_binary_configuration( view, model );
+         view->cpart_dirty = 0;
+      }
+
+      for( u32 i=0; i<view->command_count; i ++ )
+      {
+         editer_item *item = af_arritm( &model->editer_item, view->command_list[ i ].index );
+         shader_model_character_view_uUvOffset( view->command_list[ i ].uv_offset );
+
+         for( u32 j=0; j<item->submesh_count; j ++ )
+         {
+            if( (0x1 << j) & item->discard_mask & view->discard )
+               continue;
+
+            mdl_submesh *sm = &model->mdl.submeshes[ item->submesh_start + j ];
+            mdl_draw_submesh( sm );
+         }
+      }
+   }
+   else
+      mesh_draw( &model->mdl.mesh );
 }
 
 void player__render( vg_camera *cam )
 {
    world_instance *world = &_world.main;
-   struct player_model *model = addon_cache_item_data( k_addon_type_player, localplayer.playermodel_view_slot, 1 );
-   render_playermodel( cam, world, 1, model, &localplayer.skeleton, localplayer.final_mtx );
+   render_playermodel( cam, world, 1, &localplayer.playermodel, &localplayer.skeleton, localplayer.final_mtx );
 
    struct player_board *board = addon_cache_item_data( k_addon_type_board, localplayer.board_view_slot, 1 );
    render_board( cam, world, board, localplayer.final_mtx[localplayer.id_board],
@@ -550,7 +590,8 @@ void player_mirror_pose( ms_keyframe pose[32], ms_keyframe mirrored[32] )
    ms_keyframe temp[32];
 
    struct skeleton *sk = &localplayer.skeleton;
-   for( u32 i=1; i<sk->bone_count; i ++ ){
+   for( u32 i=1; i<sk->bone_count; i ++ )
+   {
       ms_keyframe *dest = &temp[i-1];
       u8 mapping = localplayer.skeleton_mirror[i];
 
@@ -562,7 +603,236 @@ void player_mirror_pose( ms_keyframe pose[32], ms_keyframe mirrored[32] )
       dest->q[1] *= -1.0f;
    }
 
-   for( u32 i=0; i<sk->bone_count-1; i ++ ){
+   for( u32 i=0; i<sk->bone_count-1; i ++ )
       mirrored[i] = temp[i];
+}
+
+void playermodel_get_cpart( player_model_view *view, struct player_model *model, char out_cpart[ ADDON_CPART_MAX ] )
+{
+   u32 count = af_arrcount( &model->editer_property );
+   for( u32 i=0; i<count; i ++ )
+   {
+      editer_property *prop = af_arritm( &model->editer_property, i );
+
+      if( prop->ui_type == k_editer_type_toggle )
+         out_cpart[i] = view->property_values[i]._u32?'Y':'N';
+      else if( prop->ui_type == k_editer_type_slider )
+      {
+         f32 normalized = view->property_values[i]._f32 / prop->max._f32;
+         out_cpart[i] = (char)((u8)'A' + (u8)(normalized * (f32)((u8)'Z' - (u8)'A')));
+      }
+      else if( prop->ui_type == k_editer_type_selecter )
+         out_cpart[i] = (char)((u8)'A' + (u8)view->property_values[i]._u32);
    }
+   out_cpart[count] = '\0';
+}
+
+void playermodel_use_cpart( player_model_view *model, const char cpart[ ADDON_CPART_MAX ] )
+{
+   model->cpart_dirty = 1;
+   memcpy( model->cpart, cpart, ADDON_CPART_MAX );
+}
+
+void playermodel_create_binary_configuration( player_model_view *view, struct player_model *model )
+{
+   /* Convert cpart into editer property values */
+   i32 cpart_index = 0;
+   for( u32 i=0; i<af_arrcount( &model->editer_property ); i ++ )
+   {
+      editer_property *prop = af_arritm( &model->editer_property, i );
+
+      u8 c = (u8)'A';
+      if( cpart_index != -1 )
+      {
+         if( cpart_index >= VG_ARRAY_LEN( view->cpart ) )
+            cpart_index = -1;
+         else
+         {
+            u8 nc = (u8)view->cpart[ cpart_index ++ ];
+            if( nc == (u8)'\0' )
+               cpart_index = -1;
+            else 
+               if( (nc >= (u8)'A') && (nc <= (u8)'Z') )
+                  c = nc;
+         }
+      }
+
+      if( prop->ui_type == k_editer_type_toggle )
+         view->property_values[i]._u32 = c == (u8)'Y'? 1: 0;
+      else if( prop->ui_type == k_editer_type_slider )
+         view->property_values[i]._f32 = ((f32)(c - (u8)'A') / (f32)((u8)'Z' - (u8)'A')) * prop->max._f32;
+      else if( prop->ui_type == k_editer_type_selecter )
+      {
+         const char *options = af_str( &model->mdl.af, prop->max.pstr_options );
+         bool valid = 0;
+         for( u32 j=0; options[j]; j ++ )
+         {
+            if( (u8)options[j] == c )
+            {
+               valid = 1;
+               break;
+            }
+         }
+            
+         if( !valid )
+            c = (u8)(options[0]? options[0]: 'A');
+
+         view->property_values[i]._u32 = c - (u8)'A';
+      }
+      else
+         view->property_values[i]._u32 = 0;
+   }
+
+   /* convert items into render commands */
+   view->command_count = 0;
+   view->discard = 0x00;
+   for( u32 i=0; i<af_arrcount( &model->editer_item ); i ++ )
+   {
+      editer_item *item = af_arritm( &model->editer_item, i );
+      const char *visibility_string = af_str( &model->mdl.af, item->pstr_visibility );
+      bool visible = 1;
+
+      u32 hash;
+      u32 j = 0, token_j;
+      u32 c;
+      u32 u32_property_value;
+      bool invert;
+
+      enum 
+      {
+         k_reset,
+         k_token,
+         k_val,
+         k_val_char
+      }
+      state = k_reset;
+
+      /* evaluate visibility */
+      while( (c = visibility_string[ j ]) )
+      {
+         if( state == k_reset )
+         {
+            hash = 5381;
+            state = k_token;
+            token_j = j;
+            invert = 0;
+         }
+
+         if( state == k_token )
+         {
+            if( c == (u32)'!' )
+            {
+               invert = 1;
+               j ++;
+               c = visibility_string[ j ];
+
+               if( c == 0 )
+                  break;
+            }
+
+            if( c == (u32)'=' )
+            {
+               u32_property_value = 0;
+               state = k_val;
+
+               for( u32 k=0; k<af_arrcount( &model->editer_property ); k ++ )
+               {
+                  editer_property *prop = af_arritm( &model->editer_property, k );
+                  if( prop->ui_type == k_editer_type_slider )
+                     continue;
+
+                  if( af_str_hash( &model->mdl.af, prop->pstr_alias ) == hash )
+                  {
+                     const char *alias = af_str( &model->mdl.af, prop->pstr_alias );
+                     bool matched = 1;
+                     for( u32 l=0; l < (j-token_j); l ++ )
+                     {
+                        if( visibility_string[token_j + l] != alias[l] )
+                        {
+                           matched = 0;
+                           break;
+                        }
+                     }
+
+                     if( matched )
+                     {
+                        u32_property_value = view->property_values[k]._u32;
+                        if( prop->ui_type == k_editer_type_selecter ) 
+                           state = k_val_char;
+                        break;
+                     }
+                  }
+               }
+
+               token_j = j+1;
+               hash = 0;
+            }
+            else
+               hash = ((hash<<5) + hash) + c;
+         }
+         else if( state == k_val )
+         {
+            if( (c == (u32)'&') || (c == (u32)';') /* hack kindof lol */ )
+            {
+               u32 u32_constant = strtoul( visibility_string + token_j, NULL, 10 );
+               if( u32_constant != u32_property_value )
+               {
+                  visible = 0;
+                  break;
+               }
+
+               state = k_reset;
+            }
+         }
+         else if( state == k_val_char )
+         {
+            if( (c >= (u32)'A') && (c <= (u32)'Z') )
+            {
+               u32 u32_constant = c - (u32)'A';
+               if( (u32_constant != u32_property_value) != invert )
+               {
+                  visible = 0;
+                  break;
+               }
+            }
+            else if( (c == (u32)'&') || (c == (u32)';') )
+               state = k_reset;
+         }
+
+         j ++;
+      }
+
+      if( visible )
+      {
+         if( view->command_count == VG_ARRAY_LEN(view->command_list) )
+            break;
+
+         f32 uv_x = 0.0f,
+             uv_y = 0.0f;
+
+         for( u32 j=0; j<af_arrcount( &model->editer_property ); j ++ )
+         {
+            editer_property *prop = af_arritm( &model->editer_property, j );
+            if( item->pstr_uv_x && (prop->pstr_alias == item->pstr_uv_x) )
+               uv_x = view->property_values[j]._f32;
+
+            if( item->pstr_uv_y && (prop->pstr_alias == item->pstr_uv_y) )
+               uv_y = view->property_values[j]._f32;
+         }
+
+         view->discard |= item->discard_send;
+         view->command_list[ view->command_count ].index = i;
+         view->command_list[ view->command_count ].uv_offset[0] = uv_x;
+         view->command_list[ view->command_count ].uv_offset[1] = uv_y;
+         view->command_count ++;
+      }
+   }
+}
+
+void playermodel_set_from_uid( player_model_view *model, const char uid[ ADDON_UID_MAX ] )
+{
+   model->cache_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, uid );
+   char cpart[ ADDON_CPART_MAX ];
+   addon_uid_get_custom_part( uid, cpart );
+   playermodel_use_cpart( model, cpart );
 }
index 29aa352b86fec5a0034f0aae134bdc202037c71e..f3c57acbf0debcc6e8778d2ce21a2529fc219ceb 100644 (file)
@@ -32,10 +32,42 @@ struct player_board
                board;
 };
 
+#define PLAYER_MODEL_FLAG_CUSTOMIZABLE 0x1
+
 typedef struct player_model player_model;
 struct player_model
 {
    mdl_context mdl;
+   u32 flags;
+   array_file_ptr editer_property, editer_item;
+   u32 list_id_heads;
+};
+
+typedef struct player_model_view player_model_view;
+struct player_model_view
+{
+   addon_cache_id cache_slot;
+   player_model *fallback;
+
+   char cpart[ ADDON_CPART_MAX ];
+   bool cpart_dirty; /* we havn't processed cpart into the binary configuration yet. */
+
+   union
+   {
+      u32 _u32;
+      f32 _f32;
+   }
+   property_values[ 16 ];
+
+   struct
+   {
+      u16 index;
+      v2f uv_offset;
+   }
+   command_list[ 16 ];
+   u8 command_count;
+
+   u16 discard;
 };
 
 enum board_shader{
@@ -54,10 +86,11 @@ void render_board( vg_camera *cam, world_instance *world,
                       struct player_board_pose *pose,
                       enum board_shader shader );
 
-void render_playermodel( vg_camera *cam, world_instance *world, int depth_compare,
-                            player_model *model,
-                            struct skeleton *skeleton,
-                            m4x3f *final_mtx );
+void render_playermodel( vg_camera *cam, world_instance *world,
+                         int depth_compare,
+                         player_model_view *playermodel,
+                         struct skeleton *skeleton,
+                         m4x3f *final_mtx );
 void apply_full_skeleton_pose( struct skeleton *sk, player_pose *pose, m4x3f *final_mtx );
 void lerp_player_pose( player_pose *pose0, player_pose *pose1, f32 t, player_pose *posed );
 void player_mirror_pose( ms_keyframe pose[32], ms_keyframe mirrored[32] );
@@ -67,3 +100,8 @@ void player_load_animation_reference( const char *path );
 void player__render( vg_camera *cam );
 void player__animate(void);
 void player__pre_render(void);
+
+void playermodel_get_cpart( player_model_view *view, struct player_model *model, char out_cpart[ ADDON_CPART_MAX ] );
+void playermodel_use_cpart( player_model_view *model, const char cpart[ ADDON_CPART_MAX ] );
+void playermodel_create_binary_configuration( player_model_view *view, struct player_model *model );
+void playermodel_set_from_uid( player_model_view *model, const char uid[ ADDON_UID_MAX ] );
index e4934c265952459a1afc6fcfe9bd945ef1a82787..29e3b35db44eca93f80b4301f9427194d9407f6f 100644 (file)
@@ -3082,8 +3082,7 @@ void player__skate_animate(void){
 
    if( ((int)roundf(animator->board_euler[0]/VG_PIf)) % 2 ) lean = -lean;
    lean = vg_clampf( lean, -1.0f, 1.0f );
-   animator->board_lean = 
-      vg_lerpf(animator->board_lean, lean, vg.time_delta*18.0f);
+   animator->board_lean = vg_lerpf(animator->board_lean, lean, vg.time_delta*18.0f);
 
    /* feet placement */
    struct player_board *board = addon_cache_item_data( k_addon_type_board, localplayer.board_view_slot, 1 );
@@ -3244,7 +3243,6 @@ void player__skate_pose( void *_animator, player_pose *pose ){
           stand_blend = animator->offset[1]*-2.0f;
 
       pose->board.lean = animator->board_lean;
-
       stand_blend = vg_clampf( 1.0f-animator->local_cog[1], 0, 1 );
 
       skeleton_sample_anim( sk, &player_skate.anim_stand, dir_frame, apose );
@@ -3401,10 +3399,8 @@ void player__skate_pose( void *_animator, player_pose *pose ){
    q_mul( kf_board->q, qtrick, kf_board->q );
    q_normalize( kf_board->q );
 
-   kf_foot_l->co[2] = vg_lerpf( kf_foot_l->co[2], animator->foot_offset[0],
-                                 0.5f * animator->weight );
-   kf_foot_r->co[2] = vg_lerpf( kf_foot_r->co[2], animator->foot_offset[1],
-                                -0.5f * animator->weight );
+   kf_foot_l->co[2] = vg_lerpf( kf_foot_l->co[2], animator->foot_offset[0], 0.5f * animator->weight );
+   kf_foot_r->co[2] = vg_lerpf( kf_foot_r->co[2], animator->foot_offset[1], -0.5f * animator->weight );
 
    kf_foot_l->co[1] += animator->slap;
    kf_foot_r->co[1] += animator->slap;
@@ -3433,6 +3429,11 @@ void player__skate_pose( void *_animator, player_pose *pose ){
       kf_foot_r->co[1] -= animator->trick_foot * 0.18f;
    }
 
+   v4f qroll2;
+   q_axis_angle( qroll2, (v3f){0.0f,0.0f,1.0f}, animator->board_lean * 0.6f );
+   keyframe_rotate_around( kf_foot_l, (v3f){0.0f,0.11f,0.0f}, (v3f){0.0f,0.0f,0.0f}, qroll2 );
+   keyframe_rotate_around( kf_foot_r, (v3f){0.0f,0.11f,0.0f}, (v3f){0.0f,0.0f,0.0f}, qroll2 );
+
    /* 
     * animation wishlist:
     *    boardslide/grind jump animations
index bbd1adfe4ffe8319873572a118c12316c51719d3..5f9050f29c75ab8f80904bd708c3116ad492846a 100644 (file)
@@ -59,18 +59,19 @@ void _replay2_open_player( enum replay_type type, bool end )
    if( type == k_replay_type_local )
    {
       _replay2.highlight = 0;
-      _replay2.playermodel_cache_id = localplayer.playermodel_view_slot;
       _replay2.board_cache_id = localplayer.board_view_slot;
-      addon_cache_watch( k_addon_type_player, _replay2.playermodel_cache_id );
+
+      memcpy( &_replay2.playermodel, &localplayer.playermodel, sizeof(player_model_view) );
+      addon_cache_watch( k_addon_type_player, _replay2.playermodel.cache_slot );
       addon_cache_watch( k_addon_type_board, _replay2.board_cache_id );
    }
 }
 
 void replay2_close_player(void)
 {
-   addon_cache_unwatch( k_addon_type_player, _replay2.playermodel_cache_id );
+   addon_cache_unwatch( k_addon_type_player, _replay2.playermodel.cache_slot );
    addon_cache_unwatch( k_addon_type_board, _replay2.board_cache_id );
-   _replay2.playermodel_cache_id = 0;
+   _replay2.playermodel.cache_slot = 0;
    _replay2.board_cache_id = 0;
    _remote_replay.state = k_remote_replay_state_none;
    localplayer.immobile = 0;
@@ -143,8 +144,8 @@ static void replay_download_callback( void *data, u32 data_size, u64 userdata, e
 
             if( playermodel_str )
             {
-               addon_cache_unwatch( k_addon_type_player, _replay2.playermodel_cache_id );
-               _replay2.playermodel_cache_id = addon_cache_create_viewer_from_uid( k_addon_type_player, playermodel_str );
+               addon_cache_unwatch( k_addon_type_player, _replay2.playermodel.cache_slot );
+               playermodel_set_from_uid( &_replay2.playermodel, playermodel_str );
             }
 
             if( board_str )
@@ -589,9 +590,8 @@ void _replay2_render_player( world_instance *world, vg_camera *cam )
    if( skaterift.activity != k_skaterift_replay ) 
       return;
 
-   struct player_model *model = addon_cache_item_data( k_addon_type_player, _replay2.playermodel_cache_id, 1 );
    struct skeleton *sk = &localplayer.skeleton;
-   render_playermodel( cam, world, 0, model, sk, _replay2.final_mtx );
+   render_playermodel( cam, world, 0, &_replay2.playermodel, sk, _replay2.final_mtx );
 
    struct player_board *board = addon_cache_item_data( k_addon_type_board, _replay2.board_cache_id, 1 );
    render_board( cam, world, board, _replay2.final_mtx[localplayer.id_board], 
index 103420a7e20d4905c06e73c53bcc171ec5d89e68..4231dd314c016a68e35be1f7a193523dda1bbe5f 100644 (file)
@@ -117,8 +117,8 @@ struct _replay2
    f64 highlight_start, highlight_length;
 
    bool animation_dirty;
-   addon_cache_id board_cache_id,
-                  playermodel_cache_id;
+   addon_cache_id board_cache_id;
+   player_model_view playermodel;
 
    struct player_effects_data effect_data;
    bool render_glider;
index 7f180fff17c5a2d07afeabff7260e9d561bd01b7..6b0147e57572e076556beba210095f4d565ac1ae 100644 (file)
@@ -16,7 +16,7 @@ void savedata_file_write( savedata_file *file )
    FILE *fp = fopen( sav->path, "wb" );
    if( fp )
    {
-      fwrite( sav->buf, sav->len, 1, fp );
+      fwrite( sav->buf, sav->msg.cur.co, 1, fp );
       fclose( fp );
       vg_success( "savedata written to '%s'\n", sav->path );
    }
@@ -24,8 +24,9 @@ void savedata_file_write( savedata_file *file )
       vg_error( "Error writing savedata (%s)\n", sav->path );
 }
 
-void savedata_group_write( savedata_group *group )
+static void savedata_file_write_task( vg_async_task *task )
 {
+   savedata_file_write( (void *)task->data );
 }
 
 void savedata_file_read( savedata_file *file )
@@ -33,160 +34,113 @@ void savedata_file_read( savedata_file *file )
    FILE *fp = fopen( file->path, "rb" );
    if( fp )
    {
-      file->len = fread( file->buf, 1, sizeof(file->buf), fp );
+      u32 len = fread( file->buf, 1, sizeof(file->buf), fp );
+      vg_msg_init( &file->msg, file->buf, len );
       fclose( fp );
    }
    else
    {
-      file->len = 0;
+      vg_msg_init( &file->msg, file->buf, 0 );
       vg_warn( "Error reading savedata (%s)\n", file->path );
    }
 }
 
-static void skaterift_write_addon_alias( vg_msg *msg, const char *key, addon_alias *alias )
+static void skaterift_write_addon( vg_msg *msg, const char *key, addon_id id )
 {
-   if( alias->workshop_id ) 
-      vg_msg_wkvnum( msg, key, k_vg_msg_u64, 1, &alias->workshop_id );
-   else
-      vg_msg_wkvstr( msg, key, alias->foldername );
+   char uid[ ADDON_UID_MAX ];
+   addon_make_uid( id, uid );
+   vg_msg_wkvstr( msg, key, uid );
 }
 
 static void skaterift_write_viewslot( vg_msg *msg, const char *key, enum addon_type type, u16 cache_id )
 {
    THREAD_0;
-
    if( !cache_id ) 
       return;
 
    struct addon_cache *cache = &_addon.cache[type];
    addon_cache_entry *entry = vg_pool_item( &cache->pool, cache_id );
-   
-   addon_reg *reg = addon_details( entry->addon_id );
-   if( reg )
-      skaterift_write_addon_alias( msg, key, &reg->alias );
+
+   char uid[ ADDON_UID_MAX ];
+   addon_make_uid_cpart( entry->addon_id, uid, entry->local_cpart );
+   vg_msg_wkvstr( msg, key, uid );
 }
 
-static bool skaterift_read_addon_alias( vg_msg *msg, const char *key, enum addon_type type, addon_alias *alias )
+void init_savefile( savedata_file *file, const char *path )
 {
-   alias->foldername[0] = '\0';
-   alias->workshop_id = 0;
-   alias->type = type;
-
-   vg_msg_cmd kv;
-   if( vg_msg_getkvcmd( msg, key, &kv ) )
-   {
-      if( kv.code == k_vg_msg_kvstring )
-         vg_strncpy( kv.value, alias->foldername, sizeof(alias->foldername), k_strncpy_allow_cutoff );
-      else
-         vg_msg_cast( kv.value, kv.code, &alias->workshop_id, k_vg_msg_u64 );
-
-      return 1;
-   }
-
-   return 0;
+   memset( file, 0, sizeof(savedata_file) );
+   strcpy( file->path, path );
+   vg_msg_init( &file->msg, file->buf, sizeof(file->buf) );
 }
 
-static void skaterift_populate_main_savedata( savedata_file *file )
+void write_savefile( savedata_file *file, bool async )
 {
-   strcpy( file->path, str_skaterift_main_save );
-
-   vg_msg sav;
-   vg_msg_init( &sav, file->buf, sizeof(file->buf) );
-   vg_msg_wkvnum( &sav, "ach", k_vg_msg_u32, 1, &skaterift.achievements );
-
-   vg_msg_frame( &sav, "player" );
+   if( async ) 
    {
-      skaterift_write_viewslot( &sav, "board", k_addon_type_board, localplayer.board_view_slot );
-      skaterift_write_viewslot( &sav, "playermodel", k_addon_type_player, localplayer.playermodel_view_slot );
-
-      if( skaterift.no_save_location == 0 )
-      {
-         addon_reg *reg = addon_details( _world.main.addon_id );
-         if( reg )
-            skaterift_write_addon_alias( &sav, "location", &reg->alias );
-         else
-            vg_error( "Tried to reference un-registered world in save file.\n" );
-      }
+      vg_async_task *task = vg_allocate_async_task( &vg.loader_tasks, sizeof(savedata_file), 1 );
+      memcpy( task->data, file, sizeof(savedata_file) );
+      vg_async_task_dispatch( task, savedata_file_write_task );
    }
-   vg_msg_end_frame( &sav );
-
-   vg_msg_frame( &sav, "atoms" );
-   {
-      serialize_atoms( k_atom_list_global, &sav );
-   }
-   vg_msg_end_frame( &sav );
-
-   file->len = sav.cur.co;
+   else  
+      savedata_file_write( file );
 }
 
-static void skaterift_populate_world_savedata( savedata_file *file )
+void skaterift_write_all_savedata( bool async )
 {
-   file->path[0] = '\0';
-   file->len = 0;
+   savedata_file file;
 
-   if( !_world.main.addon_id )
+   /* main file */
    {
-      vg_error( "Tried to save unspecified world (id was null)\n" );
-      return;
-   }
-
-   skaterift_world_get_save_path( _world.main.addon_id, file->path );
+      init_savefile( &file, str_skaterift_main_save );
 
-   vg_msg sav;
-   vg_msg_init( &sav, file->buf, sizeof(file->buf) );
+      vg_msg_wkvnum( &file.msg, "ach", k_vg_msg_u32, 1, &skaterift.achievements );
+      vg_msg_frame( &file.msg, "player" );
+      {
+         skaterift_write_viewslot( &file.msg, "board", k_addon_type_board, localplayer.board_view_slot );
+         skaterift_write_viewslot( &file.msg, "playermodel", k_addon_type_player, localplayer.playermodel.cache_slot );
 
-   world_instance *instance = &_world.main;
-   world_entity_serialize( instance, &sav );
+         if( skaterift.no_save_location == 0 )
+         {
+            if( _world.main.addon_id )
+               skaterift_write_addon( &file.msg, "location", _world.main.addon_id );
+            else
+               vg_error( "Tried to reference un-registered world in save file. ???\n" );
+         }
+      }
+      vg_msg_end_frame( &file.msg );
 
-   vg_msg_frame( &sav, "player" );
-   {
-      /* 30.05.25: this was changed to 'co' to unfuck existing players from falling through modified worlds etc */
-      //vg_msg_wkvnum( &sav, "position", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
-      vg_msg_wkvnum( &sav, "co", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
+      vg_msg_frame( &file.msg, "atoms" );
+      {
+         serialize_atoms( k_atom_list_global, &file.msg );
+      }
+      vg_msg_end_frame( &file.msg );
+      write_savefile( &file, async );
    }
-   vg_msg_end_frame( &sav );
 
-   vg_msg_frame( &sav, "atoms" );
+   /* world file */
+   if( ((_world.loader_state == k_world_loader_done) || (_world.loader_state == k_world_loader_saving_current)) &&
+        _world.main.addon_id )
    {
-      serialize_atoms( k_atom_list_world, &sav );
-   }
-   vg_msg_end_frame( &sav );
+      init_savefile( &file, "" );
+      addon_make_savedata_path( _world.main.addon_id, file.path );
 
-   file->len = sav.cur.co;
-}
+      world_instance *instance = &_world.main;
+      world_entity_serialize( instance, &file.msg );
 
-static void savedata_group_write_task( vg_async_task *task )
-{
-   savedata_group *group = (void *)task->data;
-   for( u32 i=0; i<group->file_count; i++ )
-      savedata_file_write( &group->files[i] );
-}
-
-void skaterift_write_all_savedata( bool async )
-{
-   u32 save_data_size = sizeof(savedata_group) + sizeof(savedata_file)*2;
-   vg_async_task *task = NULL;
-   savedata_group *group;
-   if( async )
-   {
-      task = vg_allocate_async_task( &vg.loader_tasks, sizeof(save_data_size), 1 );
-      group = (void *)task->data;
-   }
-   else
-      group = alloca( save_data_size );
-   group->file_count = 1;
-
-   skaterift_populate_main_savedata( &group->files[0] );
-
-   if( (_world.loader_state == k_world_loader_done) || (_world.loader_state == k_world_loader_saving_current) )
-      skaterift_populate_world_savedata( &group->files[ group->file_count ++ ] );
+      vg_msg_frame( &file.msg, "player" );
+      {
+         /* 30.05.25: this was changed to 'co' to unfuck existing players from falling through modified worlds etc */
+         //vg_msg_wkvnum( &file.msg, "position", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
+         vg_msg_wkvnum( &file.msg, "co", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
+      }
+      vg_msg_end_frame( &file.msg );
 
-   if( async )
-      vg_async_task_dispatch( task, savedata_group_write_task );
-   else
-   {
-      for( u32 i=0; i<group->file_count; i++ )
-         savedata_file_write( &group->files[i] );
+      vg_msg_frame( &file.msg, "atoms" );
+      {
+         serialize_atoms( k_atom_list_world, &file.msg );
+      }
+      vg_msg_end_frame( &file.msg );
+      write_savefile( &file, async );
    }
 }
 
@@ -206,73 +160,73 @@ void skaterift_autosave_update( void )
 
 struct equip_saved_items_info
 {
-   addon_id board_id, player_id;
+   addon_id board_id, 
+            player_id;
+
+   char player_cpart[ ADDON_CPART_MAX ];
 };
 static void equip_async( vg_async_task *task )
 {
+   THREAD_0;
+
    struct equip_saved_items_info *info = (void *)task->data;
    localplayer.board_view_slot = addon_cache_create_viewer( k_addon_type_board, info->board_id );
-   localplayer.playermodel_view_slot = addon_cache_create_viewer( k_addon_type_player, info->player_id );
+   localplayer.playermodel.cache_slot = addon_cache_create_viewer( k_addon_type_player, info->player_id );
+
+   playermodel_use_cpart( &localplayer.playermodel, info->player_cpart );
+
+   /* kinda jank.. */
+   struct addon_cache *cache = &_addon.cache[ k_addon_type_player ];
+   addon_cache_entry *cache_entry = vg_pool_item( &cache->pool, localplayer.playermodel.cache_slot );
+   memcpy( cache_entry->local_cpart, info->player_cpart, ADDON_CPART_MAX );
 }
    
 void skaterift_load_mainsave(void)
 {
    THREAD_1;
 
-   savedata_file sav;
-   strcpy( sav.path, str_skaterift_main_save );
-   savedata_file_read( &sav );
-
-   vg_msg kvsav;
-   vg_msg_init( &kvsav, sav.buf, sav.len );
+   savedata_file file;
+   strcpy( file.path, str_skaterift_main_save );
+   savedata_file_read( &file );
 
    u32 ach;
-   vg_msg_getkvintg( &kvsav, "ach", k_vg_msg_u32, &ach, NULL );
+   vg_msg_getkvintg( &file.msg, "ach", k_vg_msg_u32, &ach, NULL );
    skaterift.achievements |= ach;
 
+   vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct equip_saved_items_info), 1 );
+   struct equip_saved_items_info *info = (void *)task->data;
+
    u32 board_index = time(NULL) % _addon_filtered_count( k_addon_type_board, 0,0 ),
        player_index = (time(NULL)+44) % _addon_filtered_count( k_addon_type_player, 0,0 );
-   addon_id board_id = _addon_get_filtered( k_addon_type_board, board_index, 0,0 ),
-            player_id =_addon_get_filtered( k_addon_type_player,player_index, 0,0 );
+   info->board_id = _addon_get_filtered( k_addon_type_board, board_index, 0,0 ),
+   //info->player_id =_addon_get_filtered( k_addon_type_player,player_index, 0,0 );
+   info->player_id = _addon_mount_from_folder_path( "playermodels/sr2t", k_addon_type_player, ".mdl" );
 
-   vg_msg_cursor orig = kvsav.cur;
-   if( vg_msg_seekframe( &kvsav, "player" ) )
+   vg_msg_cursor orig = file.msg.cur;
+   if( vg_msg_seekframe( &file.msg, "player" ) )
    {
-      addon_alias q;
-      if( skaterift_read_addon_alias( &kvsav, "board", k_addon_type_board, &q ) )
-         board_id = _get_addon_by_alias( &q );
+      info->board_id = addon_get_from_uid( k_addon_type_board, vg_msg_getkvstr( &file.msg, "board" ) );
 
-      if( skaterift_read_addon_alias( &kvsav, "playermodel", k_addon_type_player, &q ) )
-         player_id = _get_addon_by_alias( &q );
+      const char *player_uid = vg_msg_getkvstr( &file.msg, "playermodel" );
+      info->player_id = addon_get_from_uid( k_addon_type_player, player_uid );
+      addon_uid_get_custom_part( player_uid, info->player_cpart );
 
-      if( skaterift_read_addon_alias( &kvsav, "location", k_addon_type_world, &q ) )
+      addon_id world_id = addon_get_from_uid( k_addon_type_world, vg_msg_getkvstr( &file.msg, "location" ) );
+      if( world_id )
       {
          vg_info( "Loading client world from save.\n" );
-         addon_id world_id = _get_addon_by_alias( &q );
-
-         if( world_id )
-            _world.load_addon = world_id;
-         else
-         {
-            char buf[ADDON_UID_MAX];
-            addon_alias_uid( &q, buf );
-            vg_error( "While loading player location from save file, couldn't find addon '%s'\n", buf );
-         }
+         _world.load_addon = world_id;
       }
+      else
+         vg_error( "While loading player location from save file, couldn't find addon\n" );
    }
-
-   vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct equip_saved_items_info), 1 );
-   struct equip_saved_items_info *info = (void *)task->data;
-   info->board_id = board_id;
-   info->player_id = player_id;
    vg_async_task_dispatch( task, equip_async );
 
-   kvsav.cur = orig;
-
-   if( vg_msg_seekframe( &kvsav, "atoms" ) )
+   file.msg.cur = orig;
+   if( vg_msg_seekframe( &file.msg, "atoms" ) )
    {
       vg_msg_cmd cmd;
-      while( vg_msg_next( &kvsav, &cmd ) )
+      while( vg_msg_next( &file.msg, &cmd ) )
       {
          if( cmd.code == k_vg_msg_endframe ) break;
          i32 value = 0;
@@ -283,6 +237,6 @@ void skaterift_load_mainsave(void)
    else
    {
       vg_info( "Starting new story!\n" );
-      _world.load_addon = _addon_mount_from_folder( "maps/dev_heaven", k_addon_type_world, ".mdl" );
+      _world.load_addon = _addon_mount_from_folder_path( "maps/dev_heaven", k_addon_type_world, ".mdl" );
    }
 }
index 39feea71096363df6b0eda6ec2ff48a3ec403463..8ea303a5362e548d171f45906f9496060efe9c11 100644 (file)
@@ -4,23 +4,21 @@
 #include "addon.h"
 
 typedef struct savedata_file savedata_file;
-typedef struct savedata_group savedata_group;
 
-struct savedata_group {
-   u32 file_count;
-   struct savedata_file {
-      char path[128];
-      u8  buf[2048];
-      u32 len;
-   }
-   files[];
+struct savedata_file 
+{
+   char path[256];
+   u8  buf[2048];
+   vg_msg msg;
 };
 
 void savedata_file_read( savedata_file *file );
 void savedata_file_write( savedata_file *file );
-void savedata_group_write( savedata_group *group );
 void skaterift_autosave_synchronous(void);
 void skaterift_autosave_update(void);
 
 void skaterift_write_all_savedata( bool async );
 void skaterift_load_mainsave(void);
+
+void init_savefile( savedata_file *file, const char *path );
+void write_savefile( savedata_file *file, bool async );
index 4ea22b7450ed3da4a7e2085b140c0af10820ff3e..9e9fe123c221c712621757bca5f4fff72e137e47 100644 (file)
@@ -152,7 +152,7 @@ static void game_load_co( vg_coroutine *co )
       vg_loader_set_user_information( "Loading content files" );
       vg_loader_step( audio_init, NULL );
 
-      _world.default_hub_addon = _addon_mount_from_folder( "maps/dev_hub", k_addon_type_world, ".mdl" );
+      _world.default_hub_addon = _addon_mount_from_folder_path( "maps/dev_hub", k_addon_type_world, ".mdl" );
       VG_ASSERT( _world.default_hub_addon );
 
       vg_console_load_autos();
@@ -178,7 +178,7 @@ static void game_load_co( vg_coroutine *co )
       skaterift_load_mainsave();
 
       if( skaterift.override_load_world )
-         _world.load_addon = _addon_mount_from_folder( skaterift.override_load_world, k_addon_type_world, ".mdl" );
+         _world.load_addon = _addon_mount_from_folder_path( skaterift.override_load_world, k_addon_type_world, ".mdl" );
 
       _world.loader_instance = &_world.main;
       _world.loader_preview_mode = 0;
index 0ac1927adf6976b47ba7fdf83ad5c993afcc4955..8f6d31984921504e52538f2f62c3c1a8f7338957 100644 (file)
@@ -529,7 +529,11 @@ static void _workshop_form_load_t1( void *userdata )
    if( workshop_form.submission.type == k_addon_type_board )
       player_board_load( &workshop_form.board_model, path_buf, arena );
    else if( workshop_form.submission.type == k_addon_type_player )
+   {
+      workshop_form.playermodel_view.cache_slot = 0;
+      workshop_form.playermodel_view.fallback = &workshop_form.player_model;
       player_model_load( &workshop_form.player_model, path_buf, arena );
+   }
 
    vg_async_call( &vg.main_tasks, workshop_form_loadmodel_async_complete, NULL );
 }
@@ -925,7 +929,7 @@ static void workshop_render_player_preview(void)
    vg_camera_update_projection( &cam, WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT );
    vg_camera_finalize( &cam );
 
-   render_playermodel( &cam, &_world.main, 0, &workshop_form.player_model, sk, localplayer.final_mtx );
+   render_playermodel( &cam, &_world.main, 0, &workshop_form.playermodel_view, sk, localplayer.final_mtx );
 
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    glViewport( 0,0, vg.window_x, vg.window_y );
index c644fce38f769d38898c03771838ef66cf7f97bc..9cdec71dc6ca3b73e79231c2b99baa4ca2a0b59e 100644 (file)
@@ -73,7 +73,9 @@ struct workshop_form
    char addon_folder[128];
    void *model_arena;
    struct player_board board_model;
+
    struct player_model player_model;
+   struct player_model_view playermodel_view;
 
    /* what does the user want to do with the image preview? */
    enum workshop_form_file_intent{
index e5d5de7bdfdc381b750cfa43862ece9a3fb492a3..6a7b4d37c93a07548877591b9a94f4c46d592ced 100644 (file)
@@ -29,13 +29,6 @@ void world_init(void)
    _world.preview_heap = vg_create_linear_allocator( vg_mem.rtmemory, max_size, VG_MEMORY_SYSTEM );
 }
 
-void skaterift_world_get_save_path( addon_id addon_id, char buf[128] )
-{
-   char uid[76];
-   addon_uid( addon_id, uid );
-   snprintf( buf, 128, "savedata/%s.bkv", uid );
-}
-
 void world_update( world_instance *world, v3f pos )
 {
    _ent_update();
index 7a7228b505406c3486abb00cb20e7ecd5902f4a4..20ddd516bf647c82a15e2c91c679ea2242ae4db2 100644 (file)
@@ -28,8 +28,6 @@ struct leaderboard_cache
 
 typedef struct world_instance world_instance;
 
-void skaterift_world_get_save_path( addon_id world_id, char buf[128] );
-
 /* submodule headers */
 #include "world_entity.h"
 #include "world_gate.h"
index c81a8a46a1f15a921be956bb08fc4bfdb47344f6..d607fc8f6a3a82ad1184d9e13b709f508acfad0b 100644 (file)
@@ -315,8 +315,8 @@ entity_event_result ent_gate_event( ent_event *event )
 
 void nonlocal_gate_cubemap_path( addon_id world_addon_id, const char *gate_key, char path[256] )
 {
-   char uid[76];
-   addon_uid( world_addon_id, uid );
+   char uid[ ADDON_UID_MAX ];
+   addon_make_uid( world_addon_id, uid );
 
    vg_str path_str;
    vg_strnull( &path_str, path, 256 );
@@ -351,16 +351,17 @@ void world_link_gates( world_instance *world )
             const char *dest_world = af_str( &world->meta.af, gate->target );
             
             addon_alias q;
-            addon_uid_to_alias( dest_world, &q );
-
-            gate->remote_addon_id = _get_addon_by_alias( &q );
-            if( gate->remote_addon_id )
+            if( addon_parse_uid( dest_world, &q ) )
             {
-               gate->flags |= k_ent_gate_linked;
-               vg_info( "Linked non-local gate to addon #%u\n", (u32)gate->remote_addon_id );
+               gate->remote_addon_id = _get_addon_by_alias( &q );
+               if( gate->remote_addon_id )
+               {
+                  gate->flags |= k_ent_gate_linked;
+                  vg_info( "Linked non-local gate to addon #%u\n", (u32)gate->remote_addon_id );
+               }
+               else
+                  vg_error( "Reference in non-local gate to other world '%s' was not found.\n", dest_world );
             }
-            else
-               vg_error( "Reference in non-local gate to other world '%s' was not found.\n", dest_world );
          }
 
          if( _world.travelled_through_nonlocal_gate )
index 0c913b5957518303a98e763099a44e303cdc7d49..299e5e2257afe278e75b8a8e30a8078db69c6281 100644 (file)
@@ -249,7 +249,7 @@ void skaterift_world_load_t1( vg_async_task *task )
    _world.loader_instance->addon_id = _world.load_addon;
 
    char uid[ADDON_UID_MAX];
-   addon_alias_uid( &reg->alias, uid );
+   addon_make_uid( _world.load_addon, uid );
    vg_info( "LOAD WORLD %s\n", uid );
 
    char path_buf[ 4096 ];
@@ -346,7 +346,7 @@ void async_worldsave_go( vg_async_task *task )
    struct world_savedata_info *info = (void *)task->data;
 
    vg_msg sav;
-   vg_msg_init( &sav, info->save.buf, info->save.len );
+   vg_msg_init( &sav, info->save.buf, info->save.msg.max );
 
    /* start entities in the world */
    world_entity_start( info->instance, &sav );
@@ -362,7 +362,7 @@ void async_worldsave_go( vg_async_task *task )
    {
       bool restored_player_position = 0;
 
-      vg_msg_init( &sav, info->save.buf, info->save.len );
+      vg_msg_init( &sav, info->save.buf, info->save.msg.max );
       vg_msg player_frame = sav;
       if( vg_msg_seekframe( &player_frame, "player" ) )
       {
@@ -395,7 +395,8 @@ void load_world_savedata_t1( void *userdata )
    struct world_savedata_info *info = (void *)task->data;
 
    info->instance = _world.loader_instance;
-   skaterift_world_get_save_path( _world.load_addon, info->save.path );
+
+   addon_make_savedata_path( _world.load_addon, info->save.path );
    savedata_file_read( &info->save );
    vg_async_task_dispatch( task, async_worldsave_go );
 }
@@ -501,9 +502,9 @@ void skaterift_load_world_start( addon_id addon_id, bool preview )
    else
       _world.loader_state = k_world_loader_ready;
 
-   char buf[76];
-   addon_uid( addon_id, buf );
-   vg_info( "loading world: %s %s\n", buf, preview? "(preview mode)": "" );
+   char uid[ADDON_UID_MAX];
+   addon_make_uid( addon_id, uid );
+   vg_info( "loading world: %s %s\n", uid, preview? "(preview mode)": "" );
 
    vg_linear_clear( vg_mem.scratch ); /* ?? */
 
@@ -569,15 +570,14 @@ int skaterift_load_world_command( int argc, const char *argv[] )
       }
 
       addon_alias q;
-      addon_uid_to_alias( argv[0], &q );
-
-      addon_id addon_id = _get_addon_by_alias( &q );
-      if( addon_id )
+      if( addon_parse_uid( argv[0], &q ) )
       {
-         skaterift_load_world_start( addon_id, 0 );
+         addon_id addon_id = _get_addon_by_alias( &q );
+         if( addon_id )
+            skaterift_load_world_start( addon_id, 0 );
+         else 
+            vg_error( "Addon '%s' is not installed or not found.\n", argv[0] );
       }
-      else 
-         vg_error( "Addon '%s' is not installed or not found.\n", argv[0] );
    }
    else 
    {
@@ -589,13 +589,9 @@ int skaterift_load_world_command( int argc, const char *argv[] )
          addon_id ai = _addon_get_filtered( k_addon_type_world, i, 0,0 );
          addon_reg *reg = addon_details(ai);
 
-         char buf[ADDON_UID_MAX];
-         addon_alias_uid( &reg->alias, buf );
-
-         if( reg->flags & ADDON_REG_HIDDEN )
-            vg_info( "  %s [hidden]\n", buf );
-         else
-            vg_info( "  %s\n", buf );
+         char uid[ADDON_UID_MAX];
+         addon_make_uid( ai, uid );
+         vg_info( "  %s %s\n", uid, (reg->flags & ADDON_REG_HIDDEN)? "[hidden]": "" );
       }
    }
 
index d7b9f5206d1e2667d8120b8d517c5f771128c023..3907a2205b64a180a872509d6d85bf17fcaa0241 100644 (file)
@@ -154,7 +154,7 @@ static void world_routes_time_lap( u32 route_index )
       }
 
       char mod_uid[ ADDON_UID_MAX ];
-      addon_uid( _world.main.addon_id, mod_uid );
+      addon_make_uid( _world.main.addon_id, mod_uid );
       network_publish_laptime( mod_uid, af_str( &world->meta.af, route->pstr_name ), lap_time );
    }
 
index 4e23411dd93a6fd63f68a1dabc959ed2f60f6850..7f0a1c940f63b6ed8fdd418d6263c2217b0484e2 100644 (file)
@@ -228,8 +228,7 @@ void world_sfd_update( world_instance *world, v3f pos )
             ent_route *route = af_arritm( &world->ent_route, closest );
 
             char mod_uid[ ADDON_UID_MAX ];
-            addon_uid( _world.main.addon_id, mod_uid );
-
+            addon_make_uid( _world.main.addon_id, mod_uid );
             network_request_scoreboard( mod_uid, af_str( &world->meta.af, route->pstr_name ),
                                         NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK, closest );
          }