From: hgn Date: Sun, 15 Jun 2025 20:15:51 +0000 (+0100) Subject: SHIT X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=1aea881b5a30014486df2b8012e244d382e3a30c;p=carveJwlIkooP6JGAAIwe30JlM.git SHIT --- diff --git a/content_skaterift/maps/dev_hub/main.mdl b/content_skaterift/maps/dev_hub/main.mdl index 1a63b81..35aa0d3 100644 Binary files a/content_skaterift/maps/dev_hub/main.mdl and b/content_skaterift/maps/dev_hub/main.mdl differ diff --git a/content_skaterift/maps/mp_mtzero/before.mdl b/content_skaterift/maps/mp_mtzero/before.mdl index a4fbcbc..9894515 100644 Binary files a/content_skaterift/maps/mp_mtzero/before.mdl and b/content_skaterift/maps/mp_mtzero/before.mdl differ diff --git a/content_skaterift/maps/mp_mtzero/main.mdl b/content_skaterift/maps/mp_mtzero/main.mdl index 3bf242e..3748ac6 100644 Binary files a/content_skaterift/maps/mp_mtzero/main.mdl and b/content_skaterift/maps/mp_mtzero/main.mdl differ diff --git a/content_skaterift/maps/mp_spawn/main.mdl b/content_skaterift/maps/mp_spawn/main.mdl index 20b72b4..fd79f69 100644 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 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 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 index 0000000..d6a248e Binary files /dev/null and b/content_skaterift/playermodels/sr2t/ch_sr2t.mdl differ diff --git a/shaders/common_world.glsl b/shaders/common_world.glsl index 0bc4e7b..b6a644b 100644 --- a/shaders/common_world.glsl +++ b/shaders/common_world.glsl @@ -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 ) { diff --git a/shaders/model_character_view.fs b/shaders/model_character_view.fs index 42fadcb..f03ccfd 100644 --- a/shaders/model_character_view.fs +++ b/shaders/model_character_view.fs @@ -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 diff --git a/shaders/model_skinned.vs b/shaders/model_skinned.vs index c0775eb..53393c0 100644 --- a/shaders/model_skinned.vs +++ b/shaders/model_skinned.vs @@ -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; diff --git a/skaterift_blender/sr_main.py b/skaterift_blender/sr_main.py index d272cbd..937fa40 100644 --- a/skaterift_blender/sr_main.py +++ b/skaterift_blender/sr_main.py @@ -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 \ diff --git a/skaterift_blender/sr_mdl.py b/skaterift_blender/sr_mdl.py index 9b35d22..3a53b88 100644 --- a/skaterift_blender/sr_mdl.py +++ b/skaterift_blender/sr_mdl.py @@ -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 ) diff --git a/src/addon.c b/src/addon.c index 786b324..cf932b3 100644 --- a/src/addon.c +++ b/src/addon.c @@ -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; itype == 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( ®->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 = ®->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; itype = 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; ifolder[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( ®->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( ®->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= 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; typepool.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; typepool.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 ); diff --git a/src/addon.h b/src/addon.h index 2bfdc9c..555d8c2 100644 --- a/src/addon.h +++ b/src/addon.h @@ -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 ); diff --git a/src/addon_types.c b/src/addon_types.c index 476ecb4..aebb996 100644 --- a/src/addon_types.c +++ b/src/addon_types.c @@ -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/" diff --git a/src/addon_types.h b/src/addon_types.h index 9b08870..e54fc74 100644 --- a/src/addon_types.h +++ b/src/addon_types.h @@ -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 diff --git a/src/ent_npc.c b/src/ent_npc.c index c08aef3..5944379 100644 --- a/src/ent_npc.c +++ b/src/ent_npc.c @@ -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 ); diff --git a/src/ent_route.c b/src/ent_route.c index 78b1a61..efdf69c 100644 --- a/src/ent_route.c +++ b/src/ent_route.c @@ -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 ); } diff --git a/src/ent_skateshop.c b/src/ent_skateshop.c index c270fa6..c57f994 100644 --- a/src/ent_skateshop.c +++ b/src/ent_skateshop.c @@ -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; iediter_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; diff --git a/src/ent_skateshop.h b/src/ent_skateshop.h index b913ed1..4754265 100644 --- a/src/ent_skateshop.h +++ b/src/ent_skateshop.h @@ -37,6 +37,8 @@ struct global_skateshop ent_skateshop *current_shop; bool open; + + i32 charshop_row; } extern _skateshop; diff --git a/src/entity.h b/src/entity.h index 802f8fd..10ca6b4 100644 --- a/src/entity.h +++ b/src/entity.h @@ -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; diff --git a/src/menu.c b/src/menu.c index f611ba2..bf8017a 100644 --- a/src/menu.c +++ b/src/menu.c @@ -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; jalias.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; diff --git a/src/menu.h b/src/menu.h index 16869f3..6d6c728 100644 --- a/src/menu.h +++ b/src/menu.h @@ -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 ); diff --git a/src/metascene.c b/src/metascene.c index 3ebb161..0059a56 100644 --- a/src/metascene.c +++ b/src/metascene.c @@ -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 ); diff --git a/src/model.c b/src/model.c index 961f80a..3352b18 100644 --- a/src/model.c +++ b/src/model.c @@ -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; itexture_count; i ++ ) diff --git a/src/model.h b/src/model.h index 818ca12..79a110b 100644 --- a/src/model.h +++ b/src/model.h @@ -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; }; diff --git a/src/network.c b/src/network.c index 6e60ecb..1e36561 100644 --- a/src/network.c +++ b/src/network.c @@ -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 ); diff --git a/src/player.c b/src/player.c index 5430997..167e90b 100644 --- a/src/player.c +++ b/src/player.c @@ -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", diff --git a/src/player.h b/src/player.h index b04df1f..76b2d1b 100644 --- a/src/player.h +++ b/src/player.h @@ -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; diff --git a/src/player_remote.c b/src/player_remote.c index 7bb2a46..7de565d 100644 --- a/src/player_remote.c +++ b/src/player_remote.c @@ -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, ¤t_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], diff --git a/src/player_remote.h b/src/player_remote.h index 0d6a7de..e44208b 100644 --- a/src/player_remote.h +++ b/src/player_remote.h @@ -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; diff --git a/src/player_render.c b/src/player_render.c index ab42b48..cf947d0 100644 --- a/src/player_render.c +++ b/src/player_render.c @@ -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; icommand_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; jsubmesh_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; ibone_count; i ++ ){ + for( u32 i=1; ibone_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; ibone_count-1; i ++ ){ + for( u32 i=0; ibone_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; iediter_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; iediter_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; iediter_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; kediter_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; jediter_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 ); } diff --git a/src/player_render.h b/src/player_render.h index 29aa352..f3c57ac 100644 --- a/src/player_render.h +++ b/src/player_render.h @@ -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 ] ); diff --git a/src/player_skate.c b/src/player_skate.c index e4934c2..29e3b35 100644 --- a/src/player_skate.c +++ b/src/player_skate.c @@ -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 diff --git a/src/replay2.c b/src/replay2.c index bbd1adf..5f9050f 100644 --- a/src/replay2.c +++ b/src/replay2.c @@ -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], diff --git a/src/replay2.h b/src/replay2.h index 103420a..4231dd3 100644 --- a/src/replay2.h +++ b/src/replay2.h @@ -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; diff --git a/src/save.c b/src/save.c index 7f180ff..6b0147e 100644 --- a/src/save.c +++ b/src/save.c @@ -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, ®->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", ®->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; ifile_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; ifile_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" ); } } diff --git a/src/save.h b/src/save.h index 39feea7..8ea303a 100644 --- a/src/save.h +++ b/src/save.h @@ -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 ); diff --git a/src/skaterift.c b/src/skaterift.c index 4ea22b7..9e9fe12 100644 --- a/src/skaterift.c +++ b/src/skaterift.c @@ -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; diff --git a/src/workshop.c b/src/workshop.c index 0ac1927..8f6d319 100644 --- a/src/workshop.c +++ b/src/workshop.c @@ -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 ); diff --git a/src/workshop.h b/src/workshop.h index c644fce..9cdec71 100644 --- a/src/workshop.h +++ b/src/workshop.h @@ -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{ diff --git a/src/world.c b/src/world.c index e5d5de7..6a7b4d3 100644 --- a/src/world.c +++ b/src/world.c @@ -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(); diff --git a/src/world.h b/src/world.h index 7a7228b..20ddd51 100644 --- a/src/world.h +++ b/src/world.h @@ -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" diff --git a/src/world_gate.c b/src/world_gate.c index c81a8a4..d607fc8 100644 --- a/src/world_gate.c +++ b/src/world_gate.c @@ -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 ) diff --git a/src/world_load.c b/src/world_load.c index 0c913b5..299e5e2 100644 --- a/src/world_load.c +++ b/src/world_load.c @@ -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( ®->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( ®->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]": "" ); } } diff --git a/src/world_routes.c b/src/world_routes.c index d7b9f52..3907a22 100644 --- a/src/world_routes.c +++ b/src/world_routes.c @@ -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 ); } diff --git a/src/world_sfd.c b/src/world_sfd.c index 4e23411..7f0a1c9 100644 --- a/src/world_sfd.c +++ b/src/world_sfd.c @@ -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 ); }