npcs and tutorial stuff
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index d8fb6aec0ba0e5609e853d1287146b82b741783d..b1e7677fb232cf5c071a74ff4cff5f6a26af3e21 100644 (file)
@@ -37,14 +37,22 @@ sr_entity_list = [
    ('ent_menuitem',     'Menu Item',      '', 15 ),
    ('ent_worldinfo',    'World Info',     '', 16 ),
    ('ent_ccmd',         'CCmd',           '', 17 ),
-   ('ent_challenge',    'Challenge',      '', 18 ),
-   ('ent_unlock',       'Unlockable',     '', 19 ),
-   ('ent_relay',        'Relay',          '', 20 )
+   ('ent_objective',    'Objective',      '', 18 ),
+   ('ent_challenge',    'Challenge',      '', 19 ),
+   ('ent_relay',        'Relay',          '', 20 ),
+   ('ent_miniworld',    'Mini World',     '', 22 ),
+   ('ent_prop',         'Prop',           '', 23 ),
+   ('ent_list',         'Entity List',    '', 24 ),
+   ('ent_region',       'Region',         '', 25 ),
+   ('ent_glider',       'Glider',         '', 26 ),
+   ('ent_npc',          'npc',            '', 27 )
 ]
 
-MDL_VERSION_NR = 102
-SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_unlock', \
-                   'ent_relay', 'ent_skateshop', 'ent_challenge' ]
+MDL_VERSION_NR = 105
+SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_challenge', \
+                   'ent_relay', 'ent_skateshop', 'ent_objective', 'ent_route',\
+                   'ent_miniworld', 'ent_region', 'ent_glider', 'ent_list',\
+                   'ent_npc' ]
 
 def get_entity_enum_id( alias ):
 #{
@@ -239,12 +247,26 @@ class union_file_audio_clip(Union):
                ("reserved",vg_audio_clip)]
 #}
 
+# NOTE: not really an entity. no reason for ent_ -- makes more sense as file_,
+#       but then again, too late to change because compat.
 class ent_audio_clip(Structure):
 #{
    _fields_ = [("_anon",union_file_audio_clip),
                ("probability",c_float)]
 #}
 
+class ent_list(Structure):
+#{
+   _fields_ = [("entity_ref_start",c_uint32),
+               ("entity_ref_count",c_uint32)]
+#}
+
+# used in ent_list
+class file_entity_ref(Structure):
+#{
+   _fields_ = [("index",c_uint32)]
+#}
+
 class ent_checkpoint(Structure):
 #{
    _fields_ = [("gate_index",c_uint16),
@@ -263,7 +285,31 @@ class ent_route(Structure):
                ("factive",c_float),
                ("board_transform",(c_float*3)*4),
                ("sm",mdl_submesh),
-               ("latest_pass",c_double)]
+               ("latest_pass",c_double),
+               ("id_camera",c_uint32), # v103+ 
+               ]
+
+   sr_functions = { 0: 'view' }
+#}
+
+class ent_list(Structure):#{
+   _fields_ = [("start",c_uint16),("count",c_uint16)]
+#}
+
+class ent_glider(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("flags",c_uint32),
+               ("cooldown",c_float)]
+   sr_functions = { 0: 'unlock',
+                    1: 'equip' }
+#}
+
+class ent_npc(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("id",c_uint32),
+               ("context",c_uint32),
+               ("camera",c_uint32)]
+   sr_functions = { 0: 'proximity', -1: 'leave' }
 #}
 
 class ent_water(Structure):
@@ -277,7 +323,7 @@ class ent_water(Structure):
 class volume_trigger(Structure):
 #{
    _fields_ = [("event",c_uint32),
-               ("blank",c_uint32)]
+               ("event_leave",c_int32)]
 #}
 
 class volume_particles(Structure):
@@ -375,17 +421,24 @@ class ent_skateshop_worlds(Structure):
    _fields_ = [("id_display",c_uint32),
                ("id_info",c_uint32)]
 #}
+class ent_skateshop_server(Structure):
+#{
+   _fields_ = [("id_lever",c_uint32)]
+#}
 class ent_skateshop_anon_union(Union):
 #{
    _fields_ = [("boards",ent_skateshop_boards),
                ("character",ent_skateshop_characters),
-               ("worlds",ent_skateshop_worlds)]
+               ("worlds",ent_skateshop_worlds),
+               ("server",ent_skateshop_server)]
 #}
 class ent_skateshop(Structure):
 #{
    _fields_ = [("transform",mdl_transform), ("type",c_uint32),
                ("id_camera",c_uint32), 
                ("_anonymous_union",ent_skateshop_anon_union)]
+
+   sr_functions = { 0: 'trigger' }
 #}
 
 class ent_swspreview(Structure):
@@ -397,6 +450,10 @@ class ent_swspreview(Structure):
 
 # Menu
 # -----------------------------------------------------------------
+class ent_menuitem_visual(Structure):
+#{
+   _fields_ = [("pstr_name",c_uint32)]
+#}
 class ent_menuitem_slider(Structure):
 #{
    _fields_ = [("id_min",c_uint32),
@@ -406,7 +463,8 @@ class ent_menuitem_slider(Structure):
 #}
 class ent_menuitem_button(Structure):
 #{
-   _fields_ = [("pstr",c_uint32)]
+   _fields_ = [("pstr",c_uint32),
+               ("stack_behaviour",c_uint32)]
 #}
 class ent_menuitem_checkmark(Structure):
 #{
@@ -420,12 +478,19 @@ class ent_menuitem_page(Structure):
                ("id_entrypoint",c_uint32),
                ("id_viewpoint",c_uint32)]
 #}
+class ent_menuitem_binding(Structure):
+#{
+   _fields_ = [("pstr_bind",c_uint32),
+               ("font_variant",c_uint32)]
+#}
 class ent_menuitem_anon_union(Union):
 #{
    _fields_ = [("slider",ent_menuitem_slider),
                ("button",ent_menuitem_button),
                ("checkmark",ent_menuitem_checkmark),
-               ("page",ent_menuitem_page)]
+               ("page",ent_menuitem_page),
+               ("visual",ent_menuitem_visual),
+               ("binding",ent_menuitem_binding)]
 #}
 class ent_menuitem(Structure):
 #{
@@ -451,7 +516,9 @@ class ent_worldinfo(Structure):
    _fields_ = [("pstr_name",c_uint32),
                ("pstr_author",c_uint32),    # unused
                ("pstr_desc",c_uint32),      # unused
-               ("timezone",c_float)]
+               ("timezone",c_float),
+               ("pstr_skybox",c_uint32),
+               ("flags",c_uint32)]
 #}
 
 class ent_ccmd(Structure):
@@ -459,34 +526,50 @@ class ent_ccmd(Structure):
    _fields_ = [("pstr_command",c_uint32)]
 #}
 
-class ent_challenge(Structure):#{
+class ent_objective(Structure):#{
    _fields_ = [("transform",mdl_transform),
                ("submesh_start",c_uint32), ("submesh_count",c_uint32),
                ("flags",c_uint32),
                ("id_next",c_uint32),
                ("filter",c_uint32),("filter2",c_uint32),
                ("id_win",c_uint32),
-               ("win_event",c_uint32),
+               ("win_event",c_int32),
                ("time_limit",c_float)]
 
    sr_functions = { 0: 'trigger',
-                    1: 'start_challenge',
                     2: 'show',
                     3: 'hide' }
 #}
 
-class ent_unlock(Structure):#{
+class ent_challenge(Structure):#{
    _fields_ = [("transform",mdl_transform),
                ("pstr_alias",c_uint32),
+               ("flags",c_uint32),
                ("target",c_uint32),
-               ("target_event",c_uint32),
-               ("status",c_uint32)]
-   sr_functions = { 0: 'unlock' }
+               ("target_event",c_int32),
+               ("reset",c_uint32),
+               ("reset_event",c_int32),
+               ("first",c_uint32),
+               ("camera",c_uint32),
+               ("status",c_uint32)] #runtime
+   sr_functions = { 0: 'unlock',
+                    1: 'view/reset' }
+#}
+
+class ent_region(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("submesh_start",c_uint32), ("submesh_count",c_uint32),
+               ("pstr_title",c_uint32),
+               ("flags",c_uint32),
+               ("zone_volume",c_uint32),
+               #105+
+               ("target0",c_uint32*2)]
+   sr_functions = { 0: 'enter', 1: 'leave' }
 #}
 
 class ent_relay(Structure):#{
    _fields_ = [("targets",(c_uint32*2)*4),
-               ("targets_events",c_uint32*4)]
+               ("targets_events",c_int32*4)]
    sr_functions = { 0: 'trigger' }
 #}
 
@@ -500,6 +583,25 @@ class ent_cubemap(Structure):#{
                ("placeholder",c_uint32*2)]
 #}
 
+print( sizeof(ent_cubemap) )
+
+class ent_miniworld(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("pstr_world",c_uint32),
+               ("camera",c_uint32),
+               ("proxy",c_uint32)]
+
+   sr_functions = { 0: 'zone', 1: 'leave' }
+#}
+
+class ent_prop(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("submesh_start",c_uint32),
+               ("submesh_count",c_uint32),
+               ("flags",c_uint32),
+               ("pstr_alias",c_uint32)]
+#}
+
 def obj_ent_type( obj ):
 #{
    if obj.type == 'ARMATURE': return 'mdl_armature'
@@ -670,14 +772,17 @@ def material_info(mat):
       if node == None:#{
          _graph_read.extracted = []
 
+         done = False
          for node_idname in node_def:#{
             for n in mat.node_tree.nodes:#{
                if n.name == node_idname:#{
                   node_def = node_def[node_idname]
                   node = n
+                  done = True
                   break
                #}
             #}
+            if done: break
          #}
       #}
 
@@ -805,6 +910,7 @@ def sr_compile_material( mat ):#{
 
    if mat.SR_data.shader == 'standard': m.shader = 0
    if mat.SR_data.shader == 'standard_cutout': m.shader = 1
+   if mat.SR_data.shader == 'foliage': m.shader = 10
    if mat.SR_data.shader == 'terrain_blend':#{
       m.shader = 2
 
@@ -864,11 +970,16 @@ def sr_compile_material( mat ):#{
    #}
    
    if mat.SR_data.shader in ['standard', 'standard_cutout', 'terrain_blend', \
-                             'vertex_blend', 'fxglow', 'cubemap' ]: #{
+                             'vertex_blend', 'fxglow', 'cubemap', \
+                             'foliage' ]: #{
       if 'tex_diffuse' in inf: 
          m.tex_diffuse = sr_compile_texture(inf['tex_diffuse'])
    #}
 
+   if mat.SR_data.tex_diffuse_rt >= 0:#{
+      m.tex_diffuse = 0x80000000 | mat.SR_data.tex_diffuse_rt
+   #}
+
    sr_compile.material_data.extend( bytearray(m) )
    return index
 #}
@@ -1311,9 +1422,14 @@ def sr_compile_menus( collection ):
                sr_compile_mesh_internal( obj )
       #}
 
-      if item.type == 1 or item.type == 2:#{
+      if item.type == 1 or item.type == 2 or item.type == 7:#{
          item_button = item._anonymous_union.button
          item_button.pstr = sr_compile_string( obj_data.string )
+         item_button.stack_behaviour = int( obj_data.stack_behaviour )
+      #}
+      elif item.type == 0:#{
+         item_visual = item._anonymous_union.visual
+         item_visual.pstr_name = sr_compile_string( obj_data.string )
       #}
       elif item.type == 3:#{
          item_checkmark = item._anonymous_union.checkmark
@@ -1337,6 +1453,11 @@ def sr_compile_menus( collection ):
          item_page.id_entrypoint = sr_entity_id( obj_data.newloc )
          item_page.id_viewpoint = sr_entity_id( obj_data.camera )
       #}
+      elif item.type == 6:#{
+         item_binding = item._anonymous_union.binding
+         item_binding.pstr_bind = sr_compile_string( obj_data.string )
+         item_binding.font_variant = obj_data.font_variant
+      #}
 
       if obj_data.link0:
          item.id_links[0] = sr_entity_id( obj_data.link0 )
@@ -1623,10 +1744,12 @@ def sr_compile( collection ):
          # entity ignore mesh list
          #
          if ent_type == 'ent_traffic': continue
+         if ent_type == 'ent_prop': continue
          if ent_type == 'ent_font': continue
          if ent_type == 'ent_font_variant': continue
          if ent_type == 'ent_menuitem': continue
-         if ent_type == 'ent_challenge': continue
+         if ent_type == 'ent_objective': continue
+         if ent_type == 'ent_region': continue
 
          #TODO: This is messy.
          if ent_type == 'ent_gate':#{
@@ -1635,12 +1758,13 @@ def sr_compile( collection ):
          #}
          #--------------------------
 
-         print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
+         print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}' )
          sr_compile_mesh( obj )
       #}
    #}
 
    audio_clip_count = 0
+   entity_file_ref_count = 0
 
    for ent_type, arr in sr_compile.entities.items():#{
       print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
@@ -1799,6 +1923,7 @@ def sr_compile( collection ):
             if obj_data.target:#{
                volume.target = sr_entity_id( obj_data.target )
                volume._anon.trigger.event = obj_data.target_event
+               volume._anon.trigger.event_leave = obj_data.target_event_leave
             #}
 
             sr_ent_push(volume)
@@ -1829,6 +1954,10 @@ def sr_compile( collection ):
                worldshop.id_display = sr_entity_id( obj_data.mark_display )
                worldshop.id_info = sr_entity_id( obj_data.mark_info )
             #}
+            elif skateshop.type == 3:#{
+               server = skateshop._anonymous_union.server
+               server.id_lever = sr_entity_id( obj_data.mark_display )
+            #}
             skateshop.id_camera = sr_entity_id( obj_data.cam )
             compile_obj_transform( obj, skateshop.transform )
             sr_ent_push(skateshop)
@@ -1847,7 +1976,18 @@ def sr_compile( collection ):
             worldinfo.pstr_name = sr_compile_string( obj_data.name )
             worldinfo.pstr_author = sr_compile_string( obj_data.author )
             worldinfo.pstr_desc = sr_compile_string( obj_data.desc )
-            worldinfo.timezone = obj_data.timezone
+
+            flags = 0x00
+
+            if obj_data.fix_time:#{
+               worldinfo.timezone = obj_data.fixed_time
+               flags |= 0x1
+            #}
+            else:
+               worldinfo.timezone = obj_data.timezone
+
+            worldinfo.flags = flags
+            worldinfo.pstr_skybox = sr_compile_string( obj_data.skybox )
             sr_ent_push( worldinfo )
          #}
          elif ent_type == 'ent_ccmd':#{
@@ -1856,31 +1996,49 @@ def sr_compile( collection ):
             ccmd.pstr_command = sr_compile_string( obj_data.command )
             sr_ent_push( ccmd )
          #}
+         elif ent_type == 'ent_objective':#{
+            objective = ent_objective()
+            obj_data = obj.SR_data.ent_objective[0]
+            objective.id_next = sr_entity_id( obj_data.proxima )
+            objective.id_win = sr_entity_id( obj_data.target )
+            objective.win_event = obj_data.target_event
+            objective.filter = int(obj_data.filtrar)
+            objective.filter2 = 0
+            objective.time_limit = obj_data.time_limit
+
+            compile_obj_transform( obj, objective.transform )
+            objective.submesh_start, objective.submesh_count, _ = \
+                  sr_compile_mesh_internal( obj )
+
+            sr_ent_push( objective )
+         #}
          elif ent_type == 'ent_challenge':#{
             challenge = ent_challenge()
             obj_data = obj.SR_data.ent_challenge[0]
-            challenge.id_next = sr_entity_id( obj_data.proxima )
-            challenge.id_win = sr_entity_id( obj_data.target )
-            challenge.win_event = obj_data.target_event
-            challenge.filter = int(obj_data.filtrar)
-            challenge.filter2 = 0
-            challenge.time_limit = obj_data.time_limit
-
             compile_obj_transform( obj, challenge.transform )
-            challenge.submesh_start, challenge.submesh_count, _ = \
-                  sr_compile_mesh_internal( obj )
-
+            challenge.pstr_alias = sr_compile_string( obj_data.alias )
+            challenge.target = sr_entity_id( obj_data.target )
+            challenge.target_event = obj_data.target_event
+            challenge.reset = sr_entity_id( obj_data.reset )
+            challenge.reset_event = obj_data.reset_event
+            challenge.first = sr_entity_id( obj_data.first )
+            challenge.flags = 0x00
+            challenge.camera = sr_entity_id( obj_data.camera )
+            if obj_data.time_limit: challenge.flags |= 0x01
+            challenge.status = 0
             sr_ent_push( challenge )
          #}
-         elif ent_type == 'ent_unlock':#{
-            unlock = ent_unlock()
-            obj_data = obj.SR_data.ent_unlock[0]
-            compile_obj_transform( obj, unlock.transform )
-            unlock.pstr_alias = sr_compile_string( obj_data.alias )
-            unlock.target = sr_entity_id( obj_data.target )
-            unlock.target_event = obj_data.target_event
-            unlock.status = 0
-            sr_ent_push( unlock )
+         elif ent_type == 'ent_region':#{
+            region = ent_region()
+            obj_data = obj.SR_data.ent_region[0]
+            compile_obj_transform( obj, region.transform )
+            region.submesh_start, region.submesh_count, _ = \
+                  sr_compile_mesh_internal( obj )
+            region.pstr_title = sr_compile_string( obj_data.title )
+            region.zone_volume = sr_entity_id( obj_data.zone_volume )
+            region.target0[0] = sr_entity_id( obj_data.target0 )
+            region.target0[1] = obj_data.target0_event
+            sr_ent_push( region )
          #}
          elif ent_type == 'ent_relay':#{
             relay = ent_relay()
@@ -1895,6 +2053,36 @@ def sr_compile( collection ):
             relay.targets[3][1] = obj_data.target3_event
             sr_ent_push( relay )
          #}
+         # elif ent_type == 'ent_list':#{
+         #    lista = ent_list()
+         #    obj_data = obj.SR_data.ent_list[0]
+
+         #    lista.entity_ref_start = entity_file_ref_count
+         #    lista.entity_ref_count = len( obj_data.entities )
+         #    entity_file_ref_count += lista.entity_ref_count
+
+         #    for child in obj_data.entities:#{
+         #       reference_struct = file_entity_ref()
+         #       reference_struct.index = sr_entity_id( child.target )
+         #       sr_ent_push( reference_struct )
+         #    #}
+
+         #    sr_ent_push( lista )
+         # #}
+         elif ent_type == 'ent_glider':#{
+            glider = ent_glider()
+            compile_obj_transform( obj, glider.transform )
+            sr_ent_push( glider )
+         #}
+         elif ent_type == 'ent_npc':#{
+            obj_data = obj.SR_data.ent_npc[0]
+            npc = ent_npc()
+            compile_obj_transform( obj, npc.transform )
+            npc.id = obj_data.au
+            npc.context = obj_data.context
+            npc.camera = sr_entity_id( obj_data.cam )
+            sr_ent_push( npc )
+         #}
          elif ent_type == 'ent_cubemap':#{
             cubemap = ent_cubemap()
             co = obj.matrix_world @ Vector((0,0,0))
@@ -1905,6 +2093,26 @@ def sr_compile( collection ):
             cubemap.live = 60
             sr_ent_push( cubemap )
          #}
+         elif ent_type == 'ent_miniworld':#{
+            miniworld = ent_miniworld()
+            obj_data = obj.SR_data.ent_miniworld[0]
+
+            compile_obj_transform( obj, miniworld.transform )
+            miniworld.pstr_world = sr_compile_string( obj_data.world )
+            miniworld.proxy = sr_entity_id( obj_data.proxy )
+            miniworld.camera = sr_entity_id( obj_data.camera )
+            sr_ent_push( miniworld )
+         #}
+         elif ent_type == 'ent_prop':#{
+            prop = ent_prop()
+            obj_data = obj.SR_data.ent_prop[0]
+            compile_obj_transform( obj, prop.transform )
+            prop.submesh_start, prop.submesh_count, _ = \
+                  sr_compile_mesh_internal( obj )
+            prop.flags = obj_data.flags
+            prop.pstr_alias = sr_compile_string( obj_data.alias )
+            sr_ent_push( prop )
+         #}
       #}
    #}
 
@@ -1956,6 +2164,7 @@ def sr_compile( collection ):
          route.pstr_name = sr_compile_string( obj_data.alias )
          route.checkpoints_start = checkpoint_count
          route.checkpoints_count = 0
+         route.id_camera = sr_entity_id( obj_data.cam )
 
          for ci in range(3):
             route.colour[ci] = obj_data.colour[ci]
@@ -2137,7 +2346,7 @@ class SR_SCENE_SETTINGS(bpy.types.PropertyGroup):
 #{
    use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
    export_dir: bpy.props.StringProperty( name="Export Dir", subtype='DIR_PATH' )
-   gizmos: bpy.props.BoolProperty( name="Draw Gizmos", default=True )
+   gizmos: bpy.props.BoolProperty( name="Draw Gizmos", default=False )
 
    panel: bpy.props.EnumProperty(
         name='Panel',
@@ -2456,6 +2665,10 @@ class SR_MATERIAL_PANEL(bpy.types.Panel):
          box.prop( active_mat.SR_data, "cubemap" )
          box.prop( active_mat.SR_data, "tint" )
       #}
+
+      _.layout.label( text="" )
+      _.layout.label( text="advanced (you probably don't want to edit these)" )
+      _.layout.prop( active_mat.SR_data, "tex_diffuse_rt" )
    #}
 #}
 
@@ -2540,6 +2753,17 @@ class SR_OBJECT_ENT_ROUTE_ENTRY(bpy.types.PropertyGroup):
                poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate']))
 #}
 
+class SR_OBJECT_ENT_MINIWORLD(bpy.types.PropertyGroup):
+#{
+   world: bpy.props.StringProperty( name='world UID' )
+   proxy: bpy.props.PointerProperty( \
+               type=bpy.types.Object, name='proxy', \
+               poll=lambda self,obj: sr_filter_ent_type(obj,['ent_prop']))
+   camera: bpy.props.PointerProperty( \
+           type=bpy.types.Object, name="Camera", \
+           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
+#}
+
 class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList):
 #{
    bl_idname = 'SR_UL_ROUTE_NODE_LIST'
@@ -2856,11 +3080,16 @@ class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
           name="Alias",\
           default="Untitled Course")
 
+   cam: bpy.props.PointerProperty( \
+           type=bpy.types.Object, name="Viewpoint", \
+           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
+
    @staticmethod
    def sr_inspector( layout, data ):
    #{
       layout.prop( data[0], 'alias' )
       layout.prop( data[0], 'colour' )
+      layout.prop( data[0], 'cam' )
 
       layout.label( text='Checkpoints' )
       layout.template_list('SR_UL_ROUTE_NODE_LIST', 'Checkpoints', \
@@ -2872,6 +3101,77 @@ class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
    #}
 #}
 
+
+class SR_OT_ENT_LIST_NEW_ITEM(bpy.types.Operator):#{
+   bl_idname = "skaterift.ent_list_new_entry"
+   bl_label = "Add entity"
+   
+   def execute(self, context):#{
+      return internal_listadd_execute(self,context,'ent_list','entities')
+   #}
+#}
+
+class SR_OT_ENT_LIST_DEL_ITEM(bpy.types.Operator):#{
+   bl_idname = "skaterift.ent_list_del_entry"
+   bl_label = "Remove entity"
+
+   @classmethod 
+   def poll(cls, context):#{
+      active_object = context.active_object
+      if obj_ent_type(active_object) == 'ent_list':#{
+         return active_object.SR_data.ent_list[0].entities
+      #}
+      else: return False
+   #}
+   
+   def execute(self, context):#{
+      return internal_listdel_execute(self,context,'ent_list','entities')
+   #}
+#}
+
+class SR_OBJECT_ENT_LIST_ENTRY(bpy.types.PropertyGroup):
+#{
+   target: bpy.props.PointerProperty( \
+               type=bpy.types.Object, name='target' )
+#}
+
+class SR_UL_ENT_LIST(bpy.types.UIList):#{
+   bl_idname = 'SR_UL_ENT_LIST'
+
+   def draw_item(_,context,layout,data,item,icon,active_data,active_propname):#{
+      layout.prop( item, 'target', text='', emboss=False )
+   #}
+#}
+
+class SR_OBJECT_ENT_LIST(bpy.types.PropertyGroup):#{
+   entities: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_LIST_ENTRY)
+   entities_index: bpy.props.IntProperty()
+
+   @staticmethod
+   def sr_inspector( layout, data ):#{
+      layout.label( text='Entities' )
+      layout.template_list('SR_UL_ENT_LIST', 'Entities', \
+                            data[0], 'entities', data[0], \
+                            'entities_index', rows=5)
+
+      row = layout.row()
+      row.operator( 'skaterift.ent_list_new_entry', text='Add' )
+      row.operator( 'skaterift.ent_list_del_entry', text='Remove' )
+   #}
+#}
+
+class SR_OBJECT_ENT_GLIDER(bpy.types.PropertyGroup):#{
+   nothing: bpy.props.StringProperty()
+#}
+
+class SR_OBJECT_ENT_NPC(bpy.types.PropertyGroup):#{
+   au: bpy.props.IntProperty()
+   context: bpy.props.IntProperty()
+   cam: bpy.props.PointerProperty( \
+           type=bpy.types.Object, name="Viewpoint", \
+           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
+#}
+
 class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{
    subtype: bpy.props.EnumProperty(
       name="Subtype",
@@ -2882,40 +3182,44 @@ class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{
    target: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Target", \
            poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
-   target_event: bpy.props.IntProperty( name="Event/Method" )
+   target_event: bpy.props.IntProperty( name="Enter Ev" )
+   target_event_leave: bpy.props.IntProperty( name="Leave Ev", default=-1 )
 
    @staticmethod
-   def inspect_target( layout, data, propname ):#{
+   def inspect_target( layout, data, propname, evs = ['_event'] ):#{
       box = layout.box()
       box.prop( data[0], propname )
 
-      row = box.row()
-      row.prop( data[0], propname + '_event')
-
-      target = getattr( data[0], propname )
-      if target:#{
-         tipo = target.SR_data.ent_type
-         cls = globals()[ tipo ]
-
-         table = getattr( cls, 'sr_functions', None )
-         if table:#{
-            index = getattr( data[0], propname+'_event')
-            if index in table:
-               row.label( text=table[index] )
-            else:
-               row.label( text="undefined function" )
+      for evname in evs:#{
+         row = box.row()
+         row.prop( data[0], propname + evname )
+
+         target = getattr( data[0], propname )
+         if target:#{
+            tipo = target.SR_data.ent_type
+            cls = globals()[ tipo ]
+
+            table = getattr( cls, 'sr_functions', None )
+            if table:#{
+               index = getattr( data[0], propname + evname )
+               if index in table:
+                  row.label( text=table[index] )
+               else:
+                  row.label( text="undefined function" )
+            #}
+         #}
+         else:#{
+            row.label( text="..." )
+            row.enabled=False
          #}
-      #}
-      else:#{
-         row.label( text="..." )
-         row.enabled=False
       #}
    #}
 
    @staticmethod
    def sr_inspector( layout, data ):#{
       layout.prop( data[0], 'subtype' )
-      SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target' )
+      SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target', \
+            ['_event','_event_leave'] )
    #}
 #}
 
@@ -2998,6 +3302,7 @@ class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup):
 class SR_OBJECT_ENT_MARKER(bpy.types.PropertyGroup):
 #{
    alias: bpy.props.StringProperty()
+   flags: bpy.props.IntProperty()
 #}
 
 class SR_OBJECT_ENT_GLYPH(bpy.types.PropertyGroup):
@@ -3063,7 +3368,8 @@ class SR_OBJECT_ENT_SKATESHOP(bpy.types.PropertyGroup):
    tipo: bpy.props.EnumProperty( name='Type',
                                  items=[('0','boards',''),
                                         ('1','character',''),
-                                        ('2','world','')] )
+                                        ('2','world',''),
+                                        ('4','server','')] )
    mark_rack: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Board Rack", \
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
@@ -3072,7 +3378,8 @@ class SR_OBJECT_ENT_SKATESHOP(bpy.types.PropertyGroup):
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
    mark_info: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Selected Board Info", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
+           poll=lambda self,obj: sr_filter_ent_type(obj,\
+           ['ent_marker','ent_prop']))
    cam: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Viewpoint", \
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
@@ -3109,6 +3416,10 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
    newloc: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="New location", \
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
+   stack_behaviour: bpy.props.EnumProperty( name='Stack Behaviour',
+                                 items=[('0','append',''),
+                                        ('1','replace','')])
+
    camera: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Camera", \
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
@@ -3127,6 +3438,8 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
            type=bpy.types.Object, name="Checked", \
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
 
+   font_variant: bpy.props.IntProperty( name="Font Variant" )
+
    string: bpy.props.StringProperty( name="String" )
    tipo: bpy.props.EnumProperty( name='Type',
                                  items=[('0','visual',''),
@@ -3134,7 +3447,9 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
                                         ('2','page button',''),
                                         ('3','toggle', ''),
                                         ('4','slider',''),
-                                        ('5','page','')])
+                                        ('5','page',''),
+                                        ('6','binding',''),
+                                        ('7','visual(no colourize)','')])
 
    @staticmethod
    def sr_inspector( layout, data ):
@@ -3143,7 +3458,8 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
       box = layout.box()
       box.prop( data, 'tipo' )
 
-      if data.tipo == '0':#{
+      if data.tipo == '0' or data.tipo == '7':#{
+         box.prop( data, 'string', text='Name' )
          return
       #}
       elif data.tipo == '1':#{
@@ -3151,7 +3467,7 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
       #}
       elif data.tipo == '2':#{
          box.prop( data, 'string', text='Page' )
-         box.prop( data, 'newloc' )
+         box.prop( data, 'stack_behaviour' )
       #}
       elif data.tipo == '3':#{
          box.prop( data, 'string', text='Data (i32)' )
@@ -3174,6 +3490,11 @@ class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
          box.prop( data, 'camera', text='Viewpoint' )
          return
       #}
+      elif data.tipo == '6':#{
+         box.prop( data, 'string', text='ID' )
+         box.prop( data, 'font_variant' )
+         return
+      #}
       
       box = box.box()
       box.label( text="Links" )
@@ -3189,7 +3510,24 @@ class SR_OBJECT_ENT_WORLD_INFO(bpy.types.PropertyGroup):
    name: bpy.props.StringProperty(name="Name")
    desc: bpy.props.StringProperty(name="Description")
    author: bpy.props.StringProperty(name="Author")
+   skybox: bpy.props.StringProperty(name="Skybox")
+
+   fix_time: bpy.props.BoolProperty(name="Fix Time")
    timezone: bpy.props.FloatProperty(name="Timezone(hrs) (UTC0 +hrs)")
+   fixed_time: bpy.props.FloatProperty(name="Fixed Time (0-1)")
+
+   @staticmethod
+   def sr_inspector( layout, data ):#{
+      layout.prop( data[0], 'name' )
+      layout.prop( data[0], 'desc' )
+      layout.prop( data[0], 'author' )
+
+      layout.prop( data[0], 'fix_time' )
+      if data[0].fix_time:
+         layout.prop( data[0], 'fixed_time' )
+      else:
+         layout.prop( data[0], 'timezone' )
+   #}
 #}
 
 class SR_OBJECT_ENT_CCMD(bpy.types.PropertyGroup):
@@ -3197,10 +3535,10 @@ class SR_OBJECT_ENT_CCMD(bpy.types.PropertyGroup):
    command: bpy.props.StringProperty(name="Command Line")
 #}
 
-class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
+class SR_OBJECT_ENT_OBJECTIVE(bpy.types.PropertyGroup):#{
    proxima: bpy.props.PointerProperty( \
             type=bpy.types.Object, name="Next", \
-            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_challenge']))
+            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_objective']))
    target: bpy.props.PointerProperty( \
            type=bpy.types.Object, name="Win", \
            poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
@@ -3218,7 +3556,8 @@ class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
              (str(0x20),'grind_truck_any',''),
              (str(0x40),'grind_board_any',''),
              (str(0x20|0x40),'grind_any',''),
-             (str(0x80),'footplant','')
+             (str(0x80),'footplant',''),
+             (str(0x100),'passthrough',''),
              ])
 
    @staticmethod
@@ -3230,17 +3569,56 @@ class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
    #}
 #}
 
-class SR_OBJECT_ENT_UNLOCK(bpy.types.PropertyGroup):#{
+class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
    alias: bpy.props.StringProperty( name="Alias" )
+
    target: bpy.props.PointerProperty( \
-            type=bpy.types.Object, name="Target", \
+            type=bpy.types.Object, name="On Complete", \
             poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
    target_event: bpy.props.IntProperty( name="Event/Method" )
+   reset: bpy.props.PointerProperty( \
+           type=bpy.types.Object, name="On Reset", \
+           poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+   reset_event: bpy.props.IntProperty( name="Event/Method" )
+
+   time_limit: bpy.props.BoolProperty( name="Time Limit" )
+
+   first: bpy.props.PointerProperty( \
+            type=bpy.types.Object, name="First Objective", \
+            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_objective']))
+
+   camera: bpy.props.PointerProperty( \
+           type=bpy.types.Object, name="Camera", \
+           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
+
 
    @staticmethod
    def sr_inspector( layout, data ):#{
       layout.prop( data[0], 'alias' )
+      layout.prop( data[0], 'camera' )
+      layout.prop( data[0], 'first' )
+      layout.prop( data[0], 'time_limit' )
       SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target' )
+      SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'reset' )
+   #}
+#}
+
+class SR_OBJECT_ENT_REGION(bpy.types.PropertyGroup):#{
+   title: bpy.props.StringProperty( name="Title" )
+   zone_volume: bpy.props.PointerProperty(
+            type=bpy.types.Object, name="Zone Volume", \
+            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_volume']))
+
+   target0: bpy.props.PointerProperty( \
+            type=bpy.types.Object, name="Triger on unlock", \
+            poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+   target0_event: bpy.props.IntProperty( name="Event/Method" )
+
+   @staticmethod
+   def sr_inspector( layout, data ):#{
+      layout.prop( data[0], 'title' )
+      layout.prop( data[0], 'zone_volume' )
+      SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target0' )
    #}
 #}
 
@@ -3280,6 +3658,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    ent_volume: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_VOLUME)
    ent_audio: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO)
    ent_marker: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MARKER)
+   ent_prop: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MARKER)
    ent_glyph: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH)
    ent_font: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT)
    ent_traffic: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_TRAFFIC)
@@ -3289,9 +3668,14 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    ent_menuitem: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MENU_ITEM)
    ent_worldinfo: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORLD_INFO)
    ent_ccmd: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CCMD)
+   ent_objective: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_OBJECTIVE)
    ent_challenge: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CHALLENGE)
-   ent_unlock: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_UNLOCK)
+   ent_region: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_REGION)
    ent_relay: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_RELAY)
+   ent_miniworld: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MINIWORLD)
+   ent_list: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_LIST)
+   ent_glider: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLIDER)
+   ent_npc: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_NPC)
 
    ent_type: bpy.props.EnumProperty(
       name="Type",
@@ -3369,7 +3753,8 @@ class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
       ('boundary','Boundary',''),
       ('fxglow','FX Glow',''),
       ('cubemap','Cubemap',''),
-      ('walking','Walking','')
+      ('walking','Walking',''),
+      ('foliage','Foliage','')
       ])
 
    surface_prop: bpy.props.EnumProperty(
@@ -3379,7 +3764,9 @@ class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
       ('1','wood',''),
       ('2','grass',''),
       ('3','tiles',''),
-      ('4','metal','')
+      ('4','metal',''),
+      ('5','snow (low friction)',''),
+      ('6','sand (medium friction)','')
       ])
    
    collision: bpy.props.BoolProperty( \
@@ -3447,6 +3834,8 @@ class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
    cubemap: bpy.props.PointerProperty( \
                type=bpy.types.Object, name="cubemap", \
                poll=lambda self,obj: sr_filter_ent_type(obj,['ent_cubemap']))
+
+   tex_diffuse_rt: bpy.props.IntProperty( name="diffuse: RT index", default=-1 )
 #}
 
 # ---------------------------------------------------------------------------- #
@@ -3911,6 +4300,14 @@ def draw_skeleton_helpers( obj ):
    #}
 #}
 
+def cv_draw_wireframe( mdl, points, colour ):#{
+   for i in range(len(points)//2):#{
+      p0 = mdl@points[i*2+0]
+      p1 = mdl@points[i*2+1]
+      cv_draw_line( p0, p1, colour )
+   #}
+#}
+
 def cv_ent_gate( obj ):
 #{
    global cv_view_verts, cv_view_colours
@@ -4253,10 +4650,10 @@ def cv_draw():#{
          elif ent_type == 'ent_volume':#{
             cv_ent_volume( obj )
          #}
-         elif ent_type == 'ent_challenge':#{
-            data = obj.SR_data.ent_challenge[0]
+         elif ent_type == 'ent_objective':#{
+            data = obj.SR_data.ent_objective[0]
             if data.proxima:#{
-               cv_draw_arrow( obj.location, data.proxima.location, (1,1,1) )
+               cv_draw_arrow( obj.location, data.proxima.location, (1,0.6,0.2) )
             #}
             if data.target:
                cv_draw_arrow( obj.location, data.target.location, (1,1,1) )
@@ -4272,14 +4669,21 @@ def cv_draw():#{
             if data.target3:
                cv_draw_arrow( obj.location, data.target3.location, (1,1,1) )
          #}
-         elif ent_type == 'ent_unlock':#{
-            data = obj.SR_data.ent_unlock[0]
+         elif ent_type == 'ent_challenge':#{
+            data = obj.SR_data.ent_challenge[0]
             if data.target:
                cv_draw_arrow( obj.location, data.target.location, (1,1,1) )
+            if data.reset:
+               cv_draw_arrow( obj.location, data.reset.location, (0.9,0,0) )
+            if data.first:
+               cv_draw_arrow( obj.location, data.first.location, (1,0.6,0.2) )
+
             cc1 = (0.4,0.3,0.2)
             info_cu = Vector((1.2,0.01,0.72))*0.5
             info_co = Vector((0.0,0.0,0.72))*0.5
             cv_draw_ucube( obj.matrix_world, cc1, info_cu, info_co)
+            if data.camera:
+               cv_draw_line_dotted( obj.location, data.camera.location, (1,1,1))
 
             vs = [Vector((-0.2,0.0,0.10)),Vector((-0.2,0.0,0.62)),\
                   Vector(( 0.2,0.0,0.62)),Vector((-0.2,0.0,0.30)),\
@@ -4318,6 +4722,20 @@ def cv_draw():#{
                #}
             #}
          #}
+         elif ent_type == 'ent_glider':#{
+            mesh = [Vector((-1.13982, 0.137084, -0.026358)), \
+                    Vector(( 1.13982, 0.137084, -0.026358)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector(( 0.0, -3.0, 1.0)), \
+                    Vector(( -3.45, -1.78, 0.9)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector((  3.45, -1.78, 0.9)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector((  3.45, -1.78, 0.9)), \
+                    Vector(( -3.45, -1.78, 0.9))]
+
+            cv_draw_wireframe( obj.matrix_world, mesh, (1,1,1) )
+         #}
          elif ent_type == 'ent_skateshop':#{
             data = obj.SR_data.ent_skateshop[0]
             display = data.mark_display
@@ -4355,6 +4773,16 @@ def cv_draw():#{
                info_cu = Vector((1.2,0.01,0.3))*0.5
                info_co = Vector((0.0,0.0,0.0))*0.5
             #}
+            elif data.tipo == '3':#{
+               rack = None
+               display = None
+               info = None
+            #}
+            elif data.tipo == '4':#{
+               rack = None
+               display = None
+               info = None
+            #}
 
             if rack:
                cv_draw_ucube( rack.matrix_world, cc, rack_cu, rack_co )
@@ -4375,6 +4803,22 @@ def cv_draw():#{
             if display1:
                cv_draw_ucube(display1.matrix_world, cc1, display_cu, display_co)
          #}
+         # elif ent_type == 'ent_list':#{
+         #    data = obj.SR_data.ent_list[0]
+         #    for child in data.entities:#{
+         #       if child.target:#{
+         #          cv_draw_arrow( obj.location, child.target.location, \
+         #                         (.5,.5,.5), 0.1 )
+         #       #}
+         #    #}
+         # #}
+         elif ent_type == 'ent_region':#{
+            data = obj.SR_data.ent_region[0]
+            if data.target0:#{
+               cv_draw_arrow( obj.location, data.target0.location, \
+                              (.5,.5,.5), 0.1 )
+            #}
+         #}
          elif ent_type == 'ent_menuitem':#{
             for i,col in enumerate(obj.users_collection):#{
                colour32 = hash_djb2( col.name )
@@ -4506,7 +4950,12 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
             SR_OBJECT_ENT_FONT,SR_OBJECT_ENT_TRAFFIC,SR_OBJECT_ENT_SKATESHOP,\
             SR_OBJECT_ENT_WORKSHOP_PREVIEW,SR_OBJECT_ENT_MENU_ITEM,\
             SR_OBJECT_ENT_WORLD_INFO,SR_OBJECT_ENT_CCMD,\
-            SR_OBJECT_ENT_CHALLENGE,SR_OBJECT_ENT_UNLOCK,SR_OBJECT_ENT_RELAY,\
+            SR_OBJECT_ENT_OBJECTIVE,SR_OBJECT_ENT_CHALLENGE,\
+            SR_OBJECT_ENT_REGION,\
+            SR_OBJECT_ENT_RELAY,SR_OBJECT_ENT_MINIWORLD,\
+            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_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, 
             SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \