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;
}
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 );
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 );
}
}
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 )
{
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 );
if( g_light_preview == 1 )
diffuse = vec3(0.75);
+ //normal = vec3(0,1,0);
+
// Lighting
vec3 halfview = uCamera - co;
float fdist = length(halfview);
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
#include "motion_vectors_vs.glsl"
+uniform vec2 uUvOffset;
uniform mat4 uPv;
uniform mat4x3 uTransforms[32];
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;
('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
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
#}
("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)]
# 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()
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 '' )
#}
#}
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)
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,
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 \
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 ):
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
#}
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:#{
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 )
return 0;
}
+static u32 addon_alias_hash( addon_alias *alias )
+{
+ u32 hash = 5381;
+ for( u32 i=0; i<sizeof(addon_alias)-ADDON_CPART_MAX; i ++ )
+ hash = ((hash << 5) + hash) + ((u8 *)alias)[i];
+ return hash;
+}
+
+/*
+ * equality check (excluding custom part)
+ */
+bool addon_alias_eq( addon_alias *a, addon_alias *b )
+{
+ if( a->type == b->type )
+ {
+ if( a->steam_workshop_id == b->steam_workshop_id )
+ {
+ if( a->steam_workshop_id )
+ return 1;
+ else
+ return !strcmp( a->folder, b->folder );
+ }
+ }
+
+ return 0;
+}
+
addon_id _get_addon_by_alias( addon_alias *alias )
{
if( alias->type == k_addon_type_none )
return 0;
- u32 foldername_djb2 = 0;
- if( !alias->workshop_id )
- foldername_djb2 = vg_strdjb2( alias->foldername );
-
VG_MUTEX_LOCK( _addon.registry_lock );
u32 registry_count = _addon.registry_count;
VG_MUTEX_UNLOCK( _addon.registry_lock );
- u32 count = 0;
+ u32 hash = addon_alias_hash( alias );
for( u32 i=0; i < registry_count; i++ )
{
addon_reg *reg = &_addon.registry[i];
- if( reg->alias.type == alias->type )
+ if( reg->alias_hash == hash )
{
- if( alias->workshop_id )
- {
- if( alias->workshop_id == reg->alias.workshop_id )
- return i+1;
- }
- else
- {
- if( reg->foldername_hash == foldername_djb2 )
- {
- if( !strcmp( reg->alias.foldername, alias->foldername ) )
- return i+1;
- }
- }
-
- count ++;
+ if( addon_alias_eq( ®->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; i<ADDON_UID_MAX-1; i ++ )
+ {
+ char c = uid[i];
+ if( c == '\0' )
+ {
+ out_cpart[ count ] = '\0';
+ ends = 1;
+ break;
+ }
+
+ if( c == ':' )
+ {
+ begins = 1;
+ count = 0;
+ }
+ else
+ if( begins )
+ out_cpart[ count ++ ] = c;
+ }
+
+ if( begins )
+ {
+ if( ends )
+ {
+ return count;
+ }
+ else
+ {
+ vg_error( "While extracting custom part from uid '%s', encounted too long custom string.\n", uid );
+ return 0;
+ }
+ }
+ else return 0;
}
/*
* parse uid to alias. returns 1 if successful
*/
-bool addon_uid_to_alias( const char *uid, addon_alias *alias )
+bool addon_parse_uid( const char *uid, addon_alias *alias )
{
/* 1
* 01234567890123
- * sr&&&-@@@@@-#*
+ * sr000-#####-#
* | | |
- * type | id
- * |
+ * type | id
+ * |
* location
*/
- alias->type = k_addon_type_none;
- alias->workshop_id = 0;
- alias->foldername[0] = '\0';
+ memset( alias, 0, sizeof(addon_alias) );
if( strlen(uid) < 13 )
return 0;
char location[6];
memcpy( location, uid+6, 5 );
location[5] = '\0';
+ char *endptr = NULL;
if( !strcmp(location,"steam") )
- alias->workshop_id = atoll( uid+12 );
+ {
+ alias->steam_workshop_id = strtoull( uid+12, &endptr, 10 );
+ }
else if( !strcmp(location,"local") )
{
- alias->workshop_id = 0;
- vg_strncpy( uid+12, alias->foldername, 64, k_strncpy_always_add_null );
+ const char *folder = uid+12;
+ for( u32 i=0; i<ADDON_FOLDERNAME_MAX-1; i ++ )
+ {
+ char c = folder[i];
+ if( (c == ':') || (c == '\0') )
+ {
+ alias->folder[i+1] = '\0';
+ /* ffs */
+ endptr = ((char *)uid)+12+i;
+ break;
+ }
+ else
+ {
+ alias->folder[i] = c;
+ }
+ }
}
else
return 0;
+ if( endptr == NULL )
+ {
+ vg_error( "While parsing uid string, we encountered a folder which is too long (>63 chrs).\n"
+ " or a broken steamid\n" );
+ return 0;
+ }
+
return 1;
}
}
}
-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;
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;
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<registry_count; i++ )
+ if( folder_name_length >= ADDON_FOLDERNAME_MAX )
{
- addon_reg *reg = &_addon.registry[i];
- if( (reg->alias.type == type) && (reg->foldername_hash == folder_hash) )
- {
- if( !strcmp( reg->alias.foldername, folder_name ) )
- return i+1;
- }
+ vg_error( "Trying to mount addon from path: %s, foldername is too long (%u chars > %u)\n",
+ folder_path, folder_name_length, ADDON_FOLDERNAME_MAX-1 );
+ return 0;
}
- addon_id id = addon_alloc_reg( 0, type );
+ addon_alias q = {0};
+ q.type = type;
+ strcpy( q.folder, folder_name );
+
+ addon_id id = _get_addon_by_alias( &q );
+ if( id )
+ return id;
+
+ id = addon_alloc_reg( 0, type );
if( !id )
return 0;
addon_reg *reg = &_addon.registry[ id-1 ];
- addon_set_foldername( reg, folder_name );
- bool loaded_metadata = addon_try_load_metadata( reg, folder );
+ strcpy( reg->alias.folder, folder_name );
+ bool loaded_metadata = addon_try_load_metadata( reg, folder_path );
if( loaded_metadata )
{
vg_msg msg;
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;
}
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 );
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 ) )
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);
{
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;
}
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;
}
return 0;
vg_strcat( folder, local_folder );
- vg_strcat( folder, reg->alias.foldername );
- return 1;
- }
-}
-
-#if 0
-/*
- * write the full path of the addon's folder into the vg_str
- */
-#endif
-
-#if 0
-/*
- * cache section
- * --------------------------------------------------------------------------------------------------------------------
- */
-
-/*
- * Return existing cache id if reg_index points to a registry with its cache
- * already set.
- */
-
-/*
- * Get the real item data for cache id
- */
-
-/*
- * Get the real item data for cache id ONLY if the item is completely loaded.
- */
-
-/*
- * Updates the item state from the main thread
- */
-void async_addon_setstate( void *_entry, u32 _state )
-{
- addon_cache_entry *entry = _entry;
- SDL_LockMutex( _addon.cache_lock );
- entry->state = _state;
- SDL_UnlockMutex( _addon.cache_lock );
- vg_success( " loaded (%s)\n", entry->reg_ptr->alias.foldername );
-}
-
-/*
- * Handles the loading of an individual item
- */
-static int addon_cache_load_request( enum addon_type type, u16 id,
- addon_reg *reg, vg_str folder ){
-
- /* load content files
- * --------------------------------- */
- vg_str content_path = folder;
-
- vg_msg msg;
- vg_msg_init( &msg, reg->metadata, reg->metadata_len );
-
- const char *kv_content = vg_msg_getkvstr( &msg, "content" );
- if( kv_content ){
- vg_strcat( &content_path, "/" );
- vg_strcat( &content_path, kv_content );
- }
- else{
- vg_error( " No content paths in metadata\n" );
- return 0;
- }
-
- if( !vg_strgood( &content_path ) ) {
- vg_error( " Metadata path too long\n" );
- return 0;
- }
-
- if( type == k_addon_type_board )
- {
- struct player_board *board = addon_cache_item( type, id );
- void *arena = addon_cache_item_arena( type, id );
- vg_linear_clear( arena );
-
- player_board_load( board, content_path.buffer, arena );
+ vg_strcat( folder, reg->alias.folder );
return 1;
}
- else if( type == k_addon_type_player )
- {
- struct player_model *model = addon_cache_item( type, id );
- void *arena = addon_cache_item_arena( type, id );
- vg_linear_clear( arena );
-
- player_model_load( model, content_path.buffer, arena );
- return 1;
- }
- else {
- return 0;
- }
-
- return 0;
-}
-
-/*
- * Goes over cache item load requests and calls the above ^
- */
-static void T1_addon_cache_load_loop(void *_)
-{
- vg_info( "Running load loop\n" );
- char path_buf[4096];
-
- for( u32 type=0; type<k_addon_type_max; type++ )
- {
- struct addon_cache *cache = &_addon.cache[type];
-
- for( u32 id=1; id<=cache->pool.count; id++ )
- {
- addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
-
- SDL_LockMutex( _addon.cache_lock );
- if( entry->state == k_addon_cache_state_load_request )
- {
- vg_info( "process cache load request (%u#%u, reg:%u)\n", type, id, entry->reg_index );
-
- if( entry->reg_index >= addon_count(type,0,0) )
- {
- /* should maybe have a different value for this case */
- entry->state = k_addon_cache_state_none;
- SDL_UnlockMutex( _addon.cache_lock );
- continue;
- }
-
- SDL_UnlockMutex( _addon.cache_lock );
-
- /* continue with the request */
- addon_reg *reg = get_addon_from_index( type, entry->reg_index, 0,0 );
- entry->reg_ptr = reg;
-
- vg_str folder;
- vg_strnull( &folder, path_buf, 4096 );
- if( addon_get_content_folder( reg, &folder, 1 ) )
- {
- if( addon_cache_load_request( type, id, reg, folder ) )
- {
- vg_async_call( async_addon_setstate, entry, k_addon_cache_state_loaded );
- continue;
- }
- }
-
- vg_warn( "cache item did not load (%u#%u)\n", type, id );
- SDL_LockMutex( _addon.cache_lock );
- entry->state = k_addon_cache_state_none;
- SDL_UnlockMutex( _addon.cache_lock );
- }
- else
- SDL_UnlockMutex( _addon.cache_lock );
- }
- }
-}
-
-void addon_system_pre_update(void)
-{
- if( !vg_loader_availible() ) return;
-
- SDL_LockMutex( _addon.cache_lock );
- for( u32 type=0; type<k_addon_type_max; type++ )
- {
- struct addon_cache *cache = &_addon.cache[type];
-
- for( u32 id=1; id<=cache->pool.count; id++ )
- {
- addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
- if( entry->state == k_addon_cache_state_load_request )
- {
- SDL_UnlockMutex( _addon.cache_lock );
- vg_loader_start( T1_addon_cache_load_loop, NULL );
- return;
- }
- }
- }
- SDL_UnlockMutex( _addon.cache_lock );
}
-#endif
-
void *addon_cache_item_arena( enum addon_type type, addon_cache_id id )
{
VG_ASSERT( id );
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;
}
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 );
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
struct addon_reg
{
addon_alias alias;
- u32 foldername_hash;
+ u32 alias_hash;
+
u8 metadata[512]; /* vg_msg buffer */
u32 metadata_len;
u32 flags;
k_addon_cache_state_errored
}
state;
+
+ char local_cpart[ ADDON_CPART_MAX ];
}
*allocs;
vg_pool pool;
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 );
.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/"
};
#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
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;
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;
}
}
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 );
}
}
}
[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 );
{
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 );
}
}
}
-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 )
{
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;
}
}
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;
else return k_entity_event_result_unhandled;
}
+void charshop_gui( ui_context *ctx )
+{
+ ctx->font = &vgf_default_large;
+ int ml = button_press( k_srbind_mleft ),
+ mr = button_press( k_srbind_mright ),
+ mu = button_press( k_srbind_mup ),
+ md = button_press( k_srbind_mdown ),
+ mh = ml-mr,
+ mv = mu-md,
+ enter = button_down( k_srbind_maccept );
+
+ i32 R = menu_nav( &_skateshop.charshop_row, mv, 8 );
+
+ ui_rect panel = { 8, 8, 350, vg.window_y };
+ ui_fill( ctx, panel, ui_opacity( GUI_COL_DARK, 0.35f ) );
+ ui_outline( ctx, panel, 1, GUI_COL_NORM, 0 );
+ ui_rect_pad( panel, (ui_px[]){8,8} );
+
+ ui_rect row;
+ menu_standard_widget( ctx, panel, row, 1 );
+
+ ui_rect bL, bR, middle;
+ ui_split( row, k_ui_axis_v, row[3], 4, bL, middle );
+ ui_split( middle, k_ui_axis_v, -row[3], 4, middle, bR );
+
+ bool changed_playermodel = 0;
+
+ if( menu_button_rect( ctx, bL, 0, 1, "<" ) )
+ {
+ u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
+ if( _skateshop.selected_player_index > 0 )
+ _skateshop.selected_player_index --;
+ else
+ _skateshop.selected_player_index = valid_count-1;
+ changed_playermodel = 1;
+ }
+
+ if( menu_button_rect( ctx, bR, 0, 1, ">" ) )
+ {
+ u32 valid_count = _addon_filtered_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
+ if( _skateshop.selected_player_index+1 < valid_count )
+ _skateshop.selected_player_index ++;
+ else
+ _skateshop.selected_player_index = 0;
+ changed_playermodel = 1;
+ }
+
+ if( changed_playermodel )
+ {
+ addon_id id = _addon_get_filtered( k_addon_type_player, _skateshop.selected_player_index, 0, ADDON_REG_HIDDEN );
+ if( id )
+ player__use_model( id );
+ }
+
+ struct player_model *model = addon_cache_item_data( k_addon_type_player, localplayer.playermodel.cache_slot, 1 );
+ if( model )
+ {
+ if( model->flags & PLAYER_MODEL_FLAG_CUSTOMIZABLE )
+ {
+ menu_heading( ctx, panel, "Customize", 0 );
+
+ player_model_view *view = &localplayer.playermodel;
+ bool edited = 0;
+
+ u32 prop_count = af_arrcount( &model->editer_property );
+ if( prop_count > VG_ARRAY_LEN(view->property_values) )
+ {
+ ctx->font = &vgf_default_small;
+ menu_heading( ctx, panel, "error: too many lists in playermodel!", ui_colour( ctx, k_ui_red ) );
+ ctx->font = &vgf_default_large;
+
+ prop_count = VG_ARRAY_LEN(view->property_values);
+ }
+
+ for( u32 i=0; i<prop_count; i ++ )
+ {
+ editer_property *prop = af_arritm( &model->editer_property, i );
+ const char *alias = af_str( &model->mdl.af, prop->pstr_alias );
+
+ if( prop->ui_type == k_editer_type_toggle )
+ {
+ i32 temp = view->property_values[i]._u32;
+ if( menu_checkbox( ctx, panel, 0, alias, &temp ) )
+ {
+ view->property_values[i]._u32 = temp;
+ edited = 1;
+ }
+ }
+ else if( prop->ui_type == k_editer_type_slider )
+ {
+ f32 normalized = view->property_values[i]._f32 / prop->max._f32;
+ if( menu_slider( ctx, panel, 0, alias, 0, 25, &normalized, "%.0f" ) )
+ {
+ view->property_values[i]._f32 = normalized * prop->max._f32;
+ edited = 1;
+ }
+ }
+ else if( prop->ui_type == k_editer_type_selecter )
+ {
+ const char *options = af_str( &model->mdl.af, prop->max.pstr_options );
+ if( menu_options( ctx, panel, 0, 0, alias, options, &view->property_values[i]._u32 ) )
+ edited = 1;
+ }
+ else
+ {
+ ctx->font = &vgf_default_small;
+ menu_heading( ctx, panel, "Unknown parameter type", ui_colour( ctx, k_ui_red ) );
+ ctx->font = &vgf_default_large;
+ }
+ }
+
+ if( edited )
+ {
+ playermodel_get_cpart( view, model, view->cpart );
+ vg_info( "New cpart: %s\n", view->cpart );
+ view->cpart_dirty = 1;
+ }
+ }
+ else
+ menu_heading( ctx, panel, "Uncustomizable", 0 );
+ }
+
+ if( menu_button( ctx, panel, 0, model!=NULL, "Exit" ) )
+ {
+ if( world_clear_event( k_world_event_shop ) )
+ {
+ charshop_quitsave();
+ }
+ }
+ ctx->font = &vgf_default_small;
+}
+
void ent_skateshop_gui( ui_context *ctx )
{
if( _world.event == k_world_event_shop )
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;
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] );
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;
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, ">" ) )
{
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 )
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" ) )
{
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;
ent_skateshop *current_shop;
bool open;
+
+ i32 charshop_row;
}
extern _skateshop;
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,
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,
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
};
[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",
#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,
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;
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();
}
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 );
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 );
else return 0;
}
-static void menu_heading( ui_context *ctx, ui_rect inout_panel, const char *label, u32 colour )
+bool menu_options( ui_context *ctx, ui_rect inout_panel, bool select, i32 subselect_x, const char *str_label,
+ const char *options, u32 *data )
+{
+ ui_rect rect, box;
+ menu_standard_widget( ctx, inout_panel, rect, 1 );
+
+ if( vg_input.display_input_method == k_input_method_controller )
+ {
+ if( select )
+ {
+ menu_decor_select( ctx, rect );
+ }
+ }
+
+ ui_label( ctx, rect, str_label, 1, 8, box );
+
+ bool edited = 0;
+
+ u32 option_count = strlen( options );
+ f32 w = (f32)box[2] / (f32)option_count;
+
+ char buf[2];
+ buf[0] = '?';
+ buf[1] = '\0';
+
+ for( u32 j=0; j<option_count; j ++ )
+ {
+ ui_px x = w * (f32)j,
+ x1 = ceilf( w * (f32)(j+1) );
+
+ u32 cv = options[j] - (u32)'A';
+ i32 temp = *data == cv;
+ ui_rect chex = { box[0] + x, box[1], (x1-x), box[3] };
+
+ enum ui_button_state state = k_ui_button_none;
+ if( vg_input.display_input_method == k_input_method_kbm )
+ state = ui_checkbox_base( ctx, chex, &temp );
+
+ if( state == k_ui_button_holding_inside )
+ {
+ ui_fill( ctx, chex, GUI_COL_ACTIVE );
+ ui_outline( ctx, chex, -1, GUI_COL_CLICK, 0 );
+ }
+ else if( state == k_ui_button_holding_outside )
+ {
+ ui_fill( ctx, chex, GUI_COL_DARK );
+ ui_outline( ctx, chex, -1, GUI_COL_CLICK, 0 );
+ }
+ else if( state == k_ui_button_hover )
+ {
+ ui_fill( ctx, chex, GUI_COL_ACTIVE );
+ ui_outline( ctx, chex, -1, GUI_COL_HI, 0 );
+ }
+ else
+ {
+ ui_fill( ctx, chex, select? GUI_COL_ACTIVE: GUI_COL_DARK );
+ ui_outline( ctx, chex, -1, select? GUI_COL_HI: GUI_COL_NORM, 0 );
+ }
+
+ if( temp )
+ {
+ ui_rect_pad( chex, (ui_px[2]){2,2} );
+ ui_fill( ctx, chex, GUI_COL_HI );
+ }
+
+ if( state == k_ui_button_click )
+ {
+ *data = cv;
+ edited = 1;
+ vg_audio_lock();
+ vg_audio_oneshot( &audio_ui[0], 1.0f, 0.0f, 0, 0 );
+ vg_audio_unlock();
+ }
+
+ buf[0] = options[j];
+ ui_text( ctx, chex, buf, 1, k_ui_align_middle_center, temp? ui_colour( ctx, k_ui_bg ): 0 );
+ }
+
+ return edited;
+}
+
+void menu_heading( ui_context *ctx, ui_rect inout_panel, const char *label, u32 colour )
{
ui_rect rect;
menu_standard_widget( ctx, inout_panel, rect, 1 );
name = vg_msg_getkvstr( &msg, "title" );
if( !name )
- name = reg->alias.foldername;
+ name = reg->alias.folder;
menu.world_list_names[ menu.world_list_display_count ] = name;
menu.world_list_entries[ menu.world_list_display_count ] = addon_id;
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;
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 );
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 );
mdl_vert *vert = &job->verts[i];
for( u32 j=0; j<4; j++ )
- {
vert->groups[j] = fixup_table[vert->groups[j]];
- }
}
}
/* uploads the glmesh, and textures. everything is saved into the mdl_context */
void mdl_async_full_load_std( mdl_context *mdl, u32 *fixup_table )
{
+ THREAD_1;
+
mdl_async_load_glmesh( mdl, &mdl->mesh, fixup_table );
for( u32 i=0; i<mdl->texture_count; i ++ )
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;
};
if( type == k_netmsg_playeritem_world0 )
{
- addon_uid( _world.main.addon_id, item->uid );
+ addon_make_uid( _world.main.addon_id, item->uid );
}
else
{
}
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 );
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)
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",
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;
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;
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 ) )
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;
}
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 )
{
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],
{
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;
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 ];
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();
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(
(const GLfloat *)final_mtx );
mesh_bind( &model->mdl.mesh );
- mesh_draw( &model->mdl.mesh );
+
+ if( !fallback && (model->flags & PLAYER_MODEL_FLAG_CUSTOMIZABLE) )
+ {
+ if( view->cpart_dirty )
+ {
+ playermodel_create_binary_configuration( view, model );
+ view->cpart_dirty = 0;
+ }
+
+ for( u32 i=0; i<view->command_count; i ++ )
+ {
+ editer_item *item = af_arritm( &model->editer_item, view->command_list[ i ].index );
+ shader_model_character_view_uUvOffset( view->command_list[ i ].uv_offset );
+
+ for( u32 j=0; j<item->submesh_count; j ++ )
+ {
+ if( (0x1 << j) & item->discard_mask & view->discard )
+ continue;
+
+ mdl_submesh *sm = &model->mdl.submeshes[ item->submesh_start + j ];
+ mdl_draw_submesh( sm );
+ }
+ }
+ }
+ else
+ mesh_draw( &model->mdl.mesh );
}
void player__render( vg_camera *cam )
{
world_instance *world = &_world.main;
- struct player_model *model = addon_cache_item_data( k_addon_type_player, localplayer.playermodel_view_slot, 1 );
- render_playermodel( cam, world, 1, model, &localplayer.skeleton, localplayer.final_mtx );
+ render_playermodel( cam, world, 1, &localplayer.playermodel, &localplayer.skeleton, localplayer.final_mtx );
struct player_board *board = addon_cache_item_data( k_addon_type_board, localplayer.board_view_slot, 1 );
render_board( cam, world, board, localplayer.final_mtx[localplayer.id_board],
ms_keyframe temp[32];
struct skeleton *sk = &localplayer.skeleton;
- for( u32 i=1; i<sk->bone_count; i ++ ){
+ for( u32 i=1; i<sk->bone_count; i ++ )
+ {
ms_keyframe *dest = &temp[i-1];
u8 mapping = localplayer.skeleton_mirror[i];
dest->q[1] *= -1.0f;
}
- for( u32 i=0; i<sk->bone_count-1; i ++ ){
+ for( u32 i=0; i<sk->bone_count-1; i ++ )
mirrored[i] = temp[i];
+}
+
+void playermodel_get_cpart( player_model_view *view, struct player_model *model, char out_cpart[ ADDON_CPART_MAX ] )
+{
+ u32 count = af_arrcount( &model->editer_property );
+ for( u32 i=0; i<count; i ++ )
+ {
+ editer_property *prop = af_arritm( &model->editer_property, i );
+
+ if( prop->ui_type == k_editer_type_toggle )
+ out_cpart[i] = view->property_values[i]._u32?'Y':'N';
+ else if( prop->ui_type == k_editer_type_slider )
+ {
+ f32 normalized = view->property_values[i]._f32 / prop->max._f32;
+ out_cpart[i] = (char)((u8)'A' + (u8)(normalized * (f32)((u8)'Z' - (u8)'A')));
+ }
+ else if( prop->ui_type == k_editer_type_selecter )
+ out_cpart[i] = (char)((u8)'A' + (u8)view->property_values[i]._u32);
}
+ out_cpart[count] = '\0';
+}
+
+void playermodel_use_cpart( player_model_view *model, const char cpart[ ADDON_CPART_MAX ] )
+{
+ model->cpart_dirty = 1;
+ memcpy( model->cpart, cpart, ADDON_CPART_MAX );
+}
+
+void playermodel_create_binary_configuration( player_model_view *view, struct player_model *model )
+{
+ /* Convert cpart into editer property values */
+ i32 cpart_index = 0;
+ for( u32 i=0; i<af_arrcount( &model->editer_property ); i ++ )
+ {
+ editer_property *prop = af_arritm( &model->editer_property, i );
+
+ u8 c = (u8)'A';
+ if( cpart_index != -1 )
+ {
+ if( cpart_index >= VG_ARRAY_LEN( view->cpart ) )
+ cpart_index = -1;
+ else
+ {
+ u8 nc = (u8)view->cpart[ cpart_index ++ ];
+ if( nc == (u8)'\0' )
+ cpart_index = -1;
+ else
+ if( (nc >= (u8)'A') && (nc <= (u8)'Z') )
+ c = nc;
+ }
+ }
+
+ if( prop->ui_type == k_editer_type_toggle )
+ view->property_values[i]._u32 = c == (u8)'Y'? 1: 0;
+ else if( prop->ui_type == k_editer_type_slider )
+ view->property_values[i]._f32 = ((f32)(c - (u8)'A') / (f32)((u8)'Z' - (u8)'A')) * prop->max._f32;
+ else if( prop->ui_type == k_editer_type_selecter )
+ {
+ const char *options = af_str( &model->mdl.af, prop->max.pstr_options );
+ bool valid = 0;
+ for( u32 j=0; options[j]; j ++ )
+ {
+ if( (u8)options[j] == c )
+ {
+ valid = 1;
+ break;
+ }
+ }
+
+ if( !valid )
+ c = (u8)(options[0]? options[0]: 'A');
+
+ view->property_values[i]._u32 = c - (u8)'A';
+ }
+ else
+ view->property_values[i]._u32 = 0;
+ }
+
+ /* convert items into render commands */
+ view->command_count = 0;
+ view->discard = 0x00;
+ for( u32 i=0; i<af_arrcount( &model->editer_item ); i ++ )
+ {
+ editer_item *item = af_arritm( &model->editer_item, i );
+ const char *visibility_string = af_str( &model->mdl.af, item->pstr_visibility );
+ bool visible = 1;
+
+ u32 hash;
+ u32 j = 0, token_j;
+ u32 c;
+ u32 u32_property_value;
+ bool invert;
+
+ enum
+ {
+ k_reset,
+ k_token,
+ k_val,
+ k_val_char
+ }
+ state = k_reset;
+
+ /* evaluate visibility */
+ while( (c = visibility_string[ j ]) )
+ {
+ if( state == k_reset )
+ {
+ hash = 5381;
+ state = k_token;
+ token_j = j;
+ invert = 0;
+ }
+
+ if( state == k_token )
+ {
+ if( c == (u32)'!' )
+ {
+ invert = 1;
+ j ++;
+ c = visibility_string[ j ];
+
+ if( c == 0 )
+ break;
+ }
+
+ if( c == (u32)'=' )
+ {
+ u32_property_value = 0;
+ state = k_val;
+
+ for( u32 k=0; k<af_arrcount( &model->editer_property ); k ++ )
+ {
+ editer_property *prop = af_arritm( &model->editer_property, k );
+ if( prop->ui_type == k_editer_type_slider )
+ continue;
+
+ if( af_str_hash( &model->mdl.af, prop->pstr_alias ) == hash )
+ {
+ const char *alias = af_str( &model->mdl.af, prop->pstr_alias );
+ bool matched = 1;
+ for( u32 l=0; l < (j-token_j); l ++ )
+ {
+ if( visibility_string[token_j + l] != alias[l] )
+ {
+ matched = 0;
+ break;
+ }
+ }
+
+ if( matched )
+ {
+ u32_property_value = view->property_values[k]._u32;
+ if( prop->ui_type == k_editer_type_selecter )
+ state = k_val_char;
+ break;
+ }
+ }
+ }
+
+ token_j = j+1;
+ hash = 0;
+ }
+ else
+ hash = ((hash<<5) + hash) + c;
+ }
+ else if( state == k_val )
+ {
+ if( (c == (u32)'&') || (c == (u32)';') /* hack kindof lol */ )
+ {
+ u32 u32_constant = strtoul( visibility_string + token_j, NULL, 10 );
+ if( u32_constant != u32_property_value )
+ {
+ visible = 0;
+ break;
+ }
+
+ state = k_reset;
+ }
+ }
+ else if( state == k_val_char )
+ {
+ if( (c >= (u32)'A') && (c <= (u32)'Z') )
+ {
+ u32 u32_constant = c - (u32)'A';
+ if( (u32_constant != u32_property_value) != invert )
+ {
+ visible = 0;
+ break;
+ }
+ }
+ else if( (c == (u32)'&') || (c == (u32)';') )
+ state = k_reset;
+ }
+
+ j ++;
+ }
+
+ if( visible )
+ {
+ if( view->command_count == VG_ARRAY_LEN(view->command_list) )
+ break;
+
+ f32 uv_x = 0.0f,
+ uv_y = 0.0f;
+
+ for( u32 j=0; j<af_arrcount( &model->editer_property ); j ++ )
+ {
+ editer_property *prop = af_arritm( &model->editer_property, j );
+ if( item->pstr_uv_x && (prop->pstr_alias == item->pstr_uv_x) )
+ uv_x = view->property_values[j]._f32;
+
+ if( item->pstr_uv_y && (prop->pstr_alias == item->pstr_uv_y) )
+ uv_y = view->property_values[j]._f32;
+ }
+
+ view->discard |= item->discard_send;
+ view->command_list[ view->command_count ].index = i;
+ view->command_list[ view->command_count ].uv_offset[0] = uv_x;
+ view->command_list[ view->command_count ].uv_offset[1] = uv_y;
+ view->command_count ++;
+ }
+ }
+}
+
+void playermodel_set_from_uid( player_model_view *model, const char uid[ ADDON_UID_MAX ] )
+{
+ model->cache_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, uid );
+ char cpart[ ADDON_CPART_MAX ];
+ addon_uid_get_custom_part( uid, cpart );
+ playermodel_use_cpart( model, cpart );
}
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{
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] );
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 ] );
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 );
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 );
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;
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
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;
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 )
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],
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;
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 );
}
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 )
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; i<group->file_count; i++ )
- savedata_file_write( &group->files[i] );
-}
-
-void skaterift_write_all_savedata( bool async )
-{
- u32 save_data_size = sizeof(savedata_group) + sizeof(savedata_file)*2;
- vg_async_task *task = NULL;
- savedata_group *group;
- if( async )
- {
- task = vg_allocate_async_task( &vg.loader_tasks, sizeof(save_data_size), 1 );
- group = (void *)task->data;
- }
- else
- group = alloca( save_data_size );
- group->file_count = 1;
-
- skaterift_populate_main_savedata( &group->files[0] );
-
- if( (_world.loader_state == k_world_loader_done) || (_world.loader_state == k_world_loader_saving_current) )
- skaterift_populate_world_savedata( &group->files[ group->file_count ++ ] );
+ vg_msg_frame( &file.msg, "player" );
+ {
+ /* 30.05.25: this was changed to 'co' to unfuck existing players from falling through modified worlds etc */
+ //vg_msg_wkvnum( &file.msg, "position", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
+ vg_msg_wkvnum( &file.msg, "co", k_vg_msg_float|k_vg_msg_32b, 3, localplayer.rb.co );
+ }
+ vg_msg_end_frame( &file.msg );
- if( async )
- vg_async_task_dispatch( task, savedata_group_write_task );
- else
- {
- for( u32 i=0; i<group->file_count; i++ )
- savedata_file_write( &group->files[i] );
+ vg_msg_frame( &file.msg, "atoms" );
+ {
+ serialize_atoms( k_atom_list_world, &file.msg );
+ }
+ vg_msg_end_frame( &file.msg );
+ write_savefile( &file, async );
}
}
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;
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" );
}
}
#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 );
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();
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;
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 );
}
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 );
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{
_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();
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"
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 );
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 )
_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 ];
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 );
{
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" ) )
{
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 );
}
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 ); /* ?? */
}
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
{
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]": "" );
}
}
}
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 );
}
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 );
}