large reduction, redoing things
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index d0a1c8aa50932b67a7cd4389865dedc983aed23c..091718d15d5b660c240d4b8017197832dbbfba1c 100644 (file)
@@ -171,8 +171,161 @@ class mdl_keyframe(Structure):
 #  def editor_interface( layout, obj ):
 #
 
-# Classtype 1
+# 000: Intrinsic
+# ---------------------------------------------------------------------------- #
+
+#  Purpose: intrinsic bone type, stores collision information and limits too
+#
+class classtype_bone(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("flags",c_uint32),
+               ("ik_target",c_uint32),
+               ("ik_pole",c_uint32),
+               ("hitbox",(c_float*3)*2),
+               ("conevx",c_float*3),
+               ("conevy",c_float*3),
+               ("coneva",c_float*3),
+               ("conet",c_float)]
+
+   def encode_obj(_, node,node_def):
+   #{
+      node.classtype = 1
+
+      armature_def = node_def['linked_armature']
+      obj = node_def['bone']
+      
+      _.flags = node_def['deform']
+      
+      if 'ik_target' in node_def:
+      #{
+         _.flags |= 0x2
+         _.ik_target = armature_def['bones'].index( node_def['ik_target'] )
+         _.ik_pole   = armature_def['bones'].index( node_def['ik_pole'] )
+      #}
+      
+      # For ragdolls
+      #
+      if obj.cv_data.collider != 'collider_none':
+      #{
+         if obj.cv_data.collider == 'collider_box':
+            _.flags |= 0x4
+         else:
+            _.flags |= 0x8
+
+         _.hitbox[0][0] =  obj.cv_data.v0[0]
+         _.hitbox[0][1] =  obj.cv_data.v0[2]
+         _.hitbox[0][2] = -obj.cv_data.v1[1]
+         _.hitbox[1][0] =  obj.cv_data.v1[0]
+         _.hitbox[1][1] =  obj.cv_data.v1[2]
+         _.hitbox[1][2] = -obj.cv_data.v0[1]
+      #}
+
+      if obj.cv_data.con0:
+      #{
+         _.flags |= 0x100
+         _.conevx[0] =  obj.cv_data.conevx[0]
+         _.conevx[1] =  obj.cv_data.conevx[2]
+         _.conevx[2] = -obj.cv_data.conevx[1]
+         _.conevy[0] =  obj.cv_data.conevy[0]
+         _.conevy[1] =  obj.cv_data.conevy[2]
+         _.conevy[2] = -obj.cv_data.conevy[1]
+         _.coneva[0] =  obj.cv_data.coneva[0]
+         _.coneva[1] =  obj.cv_data.coneva[2]
+         _.coneva[2] = -obj.cv_data.coneva[1]
+         _.conet = obj.cv_data.conet
+      #}
+   #}
+#}
+
+#  Purpose: defines the allocation requirements for a skeleton
+#
+class classtype_skeleton(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("channels",c_uint32),
+               ("ik_count",c_uint32),
+               ("collider_count",c_uint32),
+               ("anim_start",c_uint32),
+               ("anim_count",c_uint32)]
+
+   def encode_obj(_, node,node_def):
+   #{
+      node.classtype = 2
+      
+      _.channels        = len( node_def['bones'] )
+      _.ik_count        = node_def['ik_count']
+      _.collider_count  = node_def['collider_count']
+      _.anim_start      = node_def['anim_start']
+      _.anim_count      = node_def['anim_count']
+   #}
+#}
+
+#  Purpose: links an mesh node to a type 11
+#
+class classtype_skin(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("skeleton",c_uint32)]
+
+   def encode_obj(_, node,node_def):
+   #{
+      node.classtype = 3
+      
+      armature_def = node_def['linked_armature']
+      _.skeleton = armature_def['obj'].cv_data.uid
+   #}
+#}
+
+#  Purpose: world light
 #
+class classtype_world_light( Structure ):
+#{
+   _pack_ = 1
+   _fields_ = [("type",c_uint32),
+               ("colour",c_float*4),
+               ("angle",c_float),
+               ("range",c_float)]
+
+   def encode_obj(_, node, node_def):
+   #{
+      node.classtype = 4
+
+      obj  = node_def['obj']
+      data = obj.data
+      _.colour[0] = data.color[0]
+      _.colour[1] = data.color[1]
+      _.colour[2] = data.color[2]
+      _.colour[3] = data.energy
+      _.range = data.cutoff_distance # this has to be manually set
+                                     # TODO: At some point, automate a min
+                                     #       threshold value
+
+      if obj.data.type == 'POINT':
+      #{
+         _.type = 0
+         _.angle = 0.0
+      #}
+      elif obj.data.type == 'SPOT':
+      #{
+         _.type = 1
+         _.angle = data.spot_size*0.5
+      #}
+
+      if data.cv_data.bp0:
+         _.type += 2
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      pass
+   #}
+#}
+
+# 100: Gates
+# ---------------------------------------------------------------------------- #
+
 #  Purpose: A rift. must target another gate, the target gate can not have more 
 #           than one target nodes of its own.
 #
@@ -184,7 +337,7 @@ class classtype_gate(Structure):
 
    def encode_obj(_, node,node_def):
    #{
-      node.classtype = 1
+      node.classtype = 100
 
       obj = node_def['obj']
 
@@ -259,7 +412,7 @@ class classtype_nonlocal_gate(classtype_gate):
 #{
    def encode_obj(_,node,node_def):
    #{
-      node.classtype = 300
+      node.classtype = 101
 
       obj = node_def['obj']
       _.target = encoder_process_pstr( node_def['obj'].cv_data.strp )
@@ -289,8 +442,9 @@ class classtype_nonlocal_gate(classtype_gate):
    #}
 #}
 
-# Classtype 3
-#
+# 200: Spawns/Waypoints
+# ---------------------------------------------------------------------------- #
+
 #  Purpose: player can reset here, its a safe place
 #           spawns can share the same name, the closest one will be picked
 #           
@@ -303,7 +457,7 @@ class classtype_spawn(Structure):
 
    def encode_obj(_, node,node_def):
    #{
-      node.classtype = 3
+      node.classtype = 200
       _.pstr_alias = encoder_process_pstr( node_def['obj'].cv_data.strp )
    #}
 
@@ -339,8 +493,9 @@ class classtype_spawn(Structure):
    #}
 #}
 
-# Classtype 4
-#
+# 300: Water
+# ---------------------------------------------------------------------------- #
+
 #  Purpose: Tells the game to draw water HERE, at this entity. 
 #
 class classtype_water(Structure):
@@ -350,710 +505,156 @@ class classtype_water(Structure):
 
    def encode_obj(_, node,node_def):
    #{
-      node.classtype = 4
+      node.classtype = 300
       # no data, spooky
    #}
 #}
 
-# Classtype 8
-#
-#  Purpose: Defines a route node and links to up to two more nodes
-#
-class classtype_route_node(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("target",c_uint32),
-               ("target1",c_uint32)]
-
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 8
-      obj = node_def['obj']
-
-      if obj.cv_data.target != None:
-         _.target = obj.cv_data.target.cv_data.uid
-      if obj.cv_data.target1 != None: 
-         _.target1 = obj.cv_data.target1.cv_data.uid
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-
-      sw = Vector((0.4,0.4,0.4,0.2))
-      sw2 = Vector((1.5,0.2,0.2,0.0))
-      if obj.cv_data.target != None:
-         cv_draw_bpath( obj, obj.cv_data.target, sw, sw )
-      if obj.cv_data.target1 != None:
-         cv_draw_bpath( obj, obj.cv_data.target1, sw, sw )
-
-      cv_draw_bhandle( obj,  1.0, (0.8,0.8,0.8,1.0) )
-      cv_draw_bhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
-
-      p1 = obj.location+ \
-            obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
-      cv_draw_arrow( obj.location, p1, sw )
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="Left" )
-      layout.prop( obj.cv_data, "target1", text="Right" )
-   #}
-#}
-
-# Classtype 9
-#
-#  Purpose: Defines a route, its 'starting' point, and the colour to use for it
-#
-class classtype_route(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("id_start",c_uint32),
-               ("pstr_name",c_uint32),
-               ("colour",c_float*3)]
-
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 9
-      obj = node_def['obj']
-
-      _.colour[0] = obj.cv_data.colour[0]
-      _.colour[1] = obj.cv_data.colour[1]
-      _.colour[2] = obj.cv_data.colour[2]
-      _.pstr_name = encoder_process_pstr( obj.cv_data.strp )
-
-      if obj.cv_data.target != None: 
-         _.id_start = obj.cv_data.target.cv_data.uid
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours, cv_view_course_i
-
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, [1,1,1,1] )
-      
-      # Tries to simulate how we do it in the game
-      #
-      stack = [None]*64
-      stack_i = [0]*64
-      stack[0] = obj.cv_data.target
-      si = 1
-      loop_complete = False
-
-      while si > 0:
-      #{
-         if stack_i[si-1] == 2:
-         #{
-            si -= 1
-            continue
-
-            if si == 0: # Loop failed to complete
-               break
-         #}
-
-         node = stack[si-1]
-
-         targets = [None,None]
-         targets[0] = node.cv_data.target
-
-         if node.cv_data.classtype == 'classtype_route_node':
-         #{
-            targets[1] = node.cv_data.target1
-         #}
-         
-         nextnode = targets[stack_i[si-1]]
-         stack_i[si-1] += 1
-
-         if nextnode != None: # branch
-         #{
-            if nextnode == stack[0]: # Loop completed
-            #{
-               loop_complete = True
-               break
-            #}
-
-            valid=True
-            for sj in range(si):
-            #{
-               if stack[sj] == nextnode: # invalidated path
-               #{
-                  valid=False
-                  break
-               #}
-            #}
-
-            if valid:
-            #{
-               stack_i[si] = 0
-               stack[si] = nextnode
-               si += 1
-               continue
-            #}
-         #}
-      #}
-
-      if loop_complete:
-      #{
-         cc = Vector((obj.cv_data.colour[0],\
-                      obj.cv_data.colour[1],\
-                      obj.cv_data.colour[2],\
-                      1.0))
-
-         for sj in range(si):
-         #{
-            sk = (sj+1)%si
-
-            if stack[sj].cv_data.classtype == 'classtype_gate' and \
-               stack[sk].cv_data.classtype == 'classtype_gate':
-            #{
-               dist = (stack[sj].location-stack[sk].location).magnitude
-               cv_draw_sbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
-            #}
-            else:
-               cv_draw_bpath( stack[sj], stack[sk], cc, cc )
-         #}
-
-         cv_view_course_i += 1
-      #}
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="'Start' from" )
-      layout.prop( obj.cv_data, "colour" )
-      layout.prop( obj.cv_data, "strp", text="Name" )
-   #}
-#}
-
-# Classtype 12
-#
-#  Purpose: links an mesh node to a type 11
-#
-class classtype_skin(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("skeleton",c_uint32)]
-
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 12
-      
-      armature_def = node_def['linked_armature']
-      _.skeleton = armature_def['obj'].cv_data.uid
-   #}
-#}
-
-# Classtype 11
-#
-#  Purpose: defines the allocation requirements for a skeleton
-#
-class classtype_skeleton(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("channels",c_uint32),
-               ("ik_count",c_uint32),
-               ("collider_count",c_uint32),
-               ("anim_start",c_uint32),
-               ("anim_count",c_uint32)]
-
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 11
-      
-      _.channels        = len( node_def['bones'] )
-      _.ik_count        = node_def['ik_count']
-      _.collider_count  = node_def['collider_count']
-      _.anim_start      = node_def['anim_start']
-      _.anim_count      = node_def['anim_count']
-   #}
-#}
-
-
-# Classtype 10
-#
-#  Purpose: intrinsic bone type, stores collision information and limits too
-#
-class classtype_bone(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("flags",c_uint32),
-               ("ik_target",c_uint32),
-               ("ik_pole",c_uint32),
-               ("hitbox",(c_float*3)*2),
-               ("conevx",c_float*3),
-               ("conevy",c_float*3),
-               ("coneva",c_float*3),
-               ("conet",c_float)]
-
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 10
-
-      armature_def = node_def['linked_armature']
-      obj = node_def['bone']
-      
-      _.flags = node_def['deform']
-      
-      if 'ik_target' in node_def:
-      #{
-         _.flags |= 0x2
-         _.ik_target = armature_def['bones'].index( node_def['ik_target'] )
-         _.ik_pole   = armature_def['bones'].index( node_def['ik_pole'] )
-      #}
-      
-      # For ragdolls
-      #
-      if obj.cv_data.collider != 'collider_none':
-      #{
-         if obj.cv_data.collider == 'collider_box':
-            _.flags |= 0x4
-         else:
-            _.flags |= 0x8
-
-         _.hitbox[0][0] =  obj.cv_data.v0[0]
-         _.hitbox[0][1] =  obj.cv_data.v0[2]
-         _.hitbox[0][2] = -obj.cv_data.v1[1]
-         _.hitbox[1][0] =  obj.cv_data.v1[0]
-         _.hitbox[1][1] =  obj.cv_data.v1[2]
-         _.hitbox[1][2] = -obj.cv_data.v0[1]
-      #}
-
-      if obj.cv_data.con0:
-      #{
-         _.flags |= 0x100
-         _.conevx[0] =  obj.cv_data.conevx[0]
-         _.conevx[1] =  obj.cv_data.conevx[2]
-         _.conevx[2] = -obj.cv_data.conevx[1]
-         _.conevy[0] =  obj.cv_data.conevy[0]
-         _.conevy[1] =  obj.cv_data.conevy[2]
-         _.conevy[2] = -obj.cv_data.conevy[1]
-         _.coneva[0] =  obj.cv_data.coneva[0]
-         _.coneva[1] =  obj.cv_data.coneva[2]
-         _.coneva[2] = -obj.cv_data.coneva[1]
-         _.conet = obj.cv_data.conet
-      #}
-   #}
-#}
-
-# Classtype 100
-#
-#  Purpose: sends a signal to another entity
-#
-class classtype_trigger(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("target",c_uint32)]
-
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 100
-      if node_def['obj'].cv_data.target:
-         _.target = node_def['obj'].cv_data.target.cv_data.uid
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-      cv_draw_ucube( obj.matrix_world, [0,1,0,1] )
-
-      white = (1,1,1,1)
-
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="Triggers" )
-   #}
-#}
-
-# Classtype 101
-# 
-#  Purpose: Gives the player an achievement.
-#           No cheating! You shouldn't use this entity anyway, since only ME can
-#           add achievements to the steam ;)
-#
-class classtype_logic_achievement(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("pstr_name",c_uint32)]
-
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 101
-      _.pstr_name = encoder_process_pstr( node_def['obj'].cv_data.strp )
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "strp", text="Achievement ID" )
-   #}
-#}
-
-class union_128bit_data(Union):
-#{
-   _pack_ = 1
-   _fields_ = [("f32",c_float),
-               ("u32",c_uint32),
-               ("i32",c_int32),
-               ("v4f",c_float*4)]
-#}
-
-# Class type 105
-#
-#  Purpose:
-#
-class classtype_logic_wire(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("next",c_uint32),
-               ("function",c_uint32),
-               ("data",union_128bit_data),
-               ("data_type",c_uint32),
-               ("enabled",c_uint32)]
-
-   function_enum = [('0',"pass along",""),
-                    ('1',"enable",""),
-                    ('2',"disable",""),
-                    ('3',"","")]
-
-   def encode_obj(_,node,node_def):
-   #{
-      node.classtype = 105
-
-      obj = node_def['obj']
-
-      if obj.cv_data.target:  _.next = obj.cv_data.target.cv_data.uid
-
-      _.data_type = obj.cv_data.intp1
-      _.function = int(obj.cv_data.function)
-      _.enabled = obj.cv_data.bp0
-
-      if _.data_type == 1:                  # an integer
-         _.data.i32 = obj.cv_data.intp
-      elif _.data_type == 2:                # a number
-         _.data.f32 = obj.cv_data.fltp
-      elif _.data_type == 3:                # a target
-         if obj.cv_data.target2:
-            _.data.u32 = obj.cv_data.target2.cv_data.uid
-      elif _.data_type == 4:                # a string
-         _.data.u32 = encoder_process_pstr( obj.cv_data.strp )
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "bp0", text="Start disabled" )
-      box = layout.box()
-      box.label( text="Target" )
-      box.prop( obj.cv_data, "target", text="connection" )
-
-      row = box.row()
-      if not obj.cv_data.target:
-         row.enabled=False
-      row.prop( obj.cv_data, "function", text="function" )
-
-      box = layout.box()
-      box.label( text="Data packet" )
-      box.prop( obj.cv_data, "intp1", text="type" )
-
-      if obj.cv_data.intp1 == 1:
-         box.prop( obj.cv_data, "intp", text="Signed Integer" )
-      elif obj.cv_data.intp1 == 2:
-         box.prop( obj.cv_data, "fltp", text="Float" )
-      elif obj.cv_data.intp1 == 3:
-         box.prop( obj.cv_data, "target2", text="Object reference" )
-      elif obj.cv_data.intp1 == 4:
-         box.prop( obj.cv_data, "strp", text="String" )
-      else:
-      #{
-         row = box.row()
-         row.enabled=False
-         row.label( text="this wire will not impart any data" )
-      #}
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-
-      white = (1,1,1,1)
-      purple = (0.5,0.2,1,1)
-
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
-      if (obj.cv_data.target2) and (obj.cv_data.intp1 == 3):
-         cv_draw_arrow( obj.cv_data.target2.location, obj.location,purple, 0.7 )
-   #}
-
-   @staticmethod
-   def get_targeted_methods( scene, context ):
-   #{
-      obj = context.object
-      invalid = [('0',"",""),
-                 ('1',"",""),
-                 ('2',"",""),
-                 ('3',"","")]
-
-      if obj.cv_data.target:
-      #{
-         classtype = obj.cv_data.target.cv_data.classtype
-         if classtype == 'classtype_none' or classtype not in globals():
-         #{
-            return invalid
-         #}
-         else:
-         #{
-            cl = globals()[ classtype ]
-            if getattr( cl, "function_enum", None ):
-            #{
-               return cl.function_enum
-            #}
-            else:
-            #{
-               return invalid
-            #}
-         #}
-      #}
-      else:
-      #{
-         return invalid
-      #}
-   #}
-#}
-
-# Class type 108
-#
-#  Purpose:
-#
-class classtype_particle_box(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("target",c_uint32),
-               ("rate",c_float)]
-
-   function_enum = [('0',"set rate",""),
-                    ('1',"",""),
-                    ('2',"",""),
-                    ('3',"","")]
-
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 108
-
-      obj = node_def['obj']
-
-      _.rate = obj.cv_data.fltp
-      if obj.cv_data.target:
-         _.target = obj.cv_data.target.cv_data.uid
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-      cv_draw_ucube( obj.matrix_world, [1,0.8,0,1] )
-
-      white = (1,1,1,1)
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="Triggers" )
-      layout.prop( obj.cv_data, "fltp", text="count per second" )
-   #}
-#}
-
-# Class type 109
-#
-#  Purpose:
-#
-class classtype_signal_splitter(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("next",c_uint32*4)]
-
-   function_enum = [('0',"pass along",""),
-                    ('1',"",""),
-                    ('2',"",""),
-                    ('3',"","")]
-
-   def encode_obj(_,node,node_def):
-   #{
-      node.classtype = 109
-
-      obj = node_def['obj']
-
-      if obj.cv_data.target:   _.next[0] = obj.cv_data.target.cv_data.uid
-      if obj.cv_data.target1:  _.next[1] = obj.cv_data.target1.cv_data.uid
-      if obj.cv_data.target2:  _.next[2] = obj.cv_data.target2.cv_data.uid
-      if obj.cv_data.target3:  _.next[3] = obj.cv_data.target3.cv_data.uid
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.label( text="The split signals will run in order" )
-      layout.prop( obj.cv_data, "target", text="#0" )
-      layout.prop( obj.cv_data, "target1", text="#1" )
-      layout.prop( obj.cv_data, "target2", text="#2" )
-      layout.prop( obj.cv_data, "target3", text="#3" )
-   #}
-
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-
-      c0 = (1,0.5,0.2,1)
-      c1 = (0.8,1,0.1,1)
-      c2 = (0.3,0.9,0.4,1)
-      c3 = (0.1,0.4,1.0,1)
-
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, c0, 0.7 )
-      if obj.cv_data.target1:
-         cv_draw_arrow( obj.location, obj.cv_data.target1.location, c1, 0.7 )
-      if obj.cv_data.target2:
-         cv_draw_arrow( obj.location, obj.cv_data.target2.location, c2, 0.7 )
-      if obj.cv_data.target3:
-         cv_draw_arrow( obj.location, obj.cv_data.target3.location, c3, 0.7 )
-   #}
-#}
+# 400: Routes
+# ---------------------------------------------------------------------------- #
 
-# Class type 106
-#
-#  Purpose:
+#  Purpose: Defines a route, its 'starting' point, and the colour to use for it
 #
-class classtype_soundscape(Structure):
+class classtype_route(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("max_instances",c_uint32),
-               ("allow_transitions",c_uint32),
-               ("transition_duration",c_float),
-               ("label",c_uint32)]
-
-   function_enum = [('0',"play",""),
-                    ('1',"set position",""),
-                    ('2',"",""),
-                    ('3',"","")]
+   _fields_ = [("id_start",c_uint32),
+               ("pstr_name",c_uint32),
+               ("colour",c_float*3)]
 
-   def encode_obj(_,node,node_def):
+   def encode_obj(_, node,node_def):
    #{
-      node.classtype = 106
-
+      node.classtype = 400
       obj = node_def['obj']
 
-      _.max_instances = obj.cv_data.intp
-      _.allow_transitions = obj.cv_data.bp0
-      _.transition_duration = obj.cv_data.fltp
-      _.label = encoder_process_pstr( obj.cv_data.strp )
+      _.colour[0] = obj.cv_data.colour[0]
+      _.colour[1] = obj.cv_data.colour[1]
+      _.colour[2] = obj.cv_data.colour[2]
+      _.pstr_name = encoder_process_pstr( obj.cv_data.strp )
+
+      if obj.cv_data.target != None: 
+         _.id_start = obj.cv_data.target.cv_data.uid
    #}
 
    @staticmethod
-   def editor_interface( layout, obj ):
+   def draw_scene_helpers( obj ):
    #{
-      layout.prop( obj.cv_data, "intp", text="max instances" )
-      layout.prop( obj.cv_data, "strp", text="label" )
+      global cv_view_verts, cv_view_colours, cv_view_course_i
 
-      box = layout.box()
-      box.label( text="If its a 3d sound, where can it spawn?" )
-      box.prop( obj.cv_data, "bp1", text="Only in water" )
-      box.prop( obj.cv_data, "bp2", text="Only on grass" )
-      box.prop( obj.cv_data, "bp3", text="Only on wood" )
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, [1,1,1,1] )
+      
+      # Tries to simulate how we do it in the game
+      #
+      stack = [None]*64
+      stack_i = [0]*64
+      stack[0] = obj.cv_data.target
+      si = 1
+      loop_complete = False
 
-      box = layout.box()
-      box.prop( obj.cv_data, "bp0", text="allow transitions" )
+      while si > 0:
+      #{
+         if stack_i[si-1] == 2:
+         #{
+            si -= 1
+            continue
 
-      row = box.row()
-      if not obj.cv_data.bp0:
-         row.enabled=False
-      row.prop( obj.cv_data, "fltp", text="transition duration" )
-   #}
-#}
+            if si == 0: # Loop failed to complete
+               break
+         #}
 
-class classtype_logic_chances(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("targets",c_uint32*2),
-               ("p",c_float)]
+         node = stack[si-1]
 
-   function_enum = [('0',"pass along",""),
-                    ('1',"set ratio",""),
-                    ('2',"",""),
-                    ('3',"","")]
+         targets = [None,None]
+         targets[0] = node.cv_data.target
 
-   def encode_obj(_,node,node_def):
-   #{
-      node.classtype = 107
+         if node.cv_data.classtype == 'classtype_route_node':
+         #{
+            targets[1] = node.cv_data.target1
+         #}
+         
+         nextnode = targets[stack_i[si-1]]
+         stack_i[si-1] += 1
 
-      obj = node_def['obj']
+         if nextnode != None: # branch
+         #{
+            if nextnode == stack[0]: # Loop completed
+            #{
+               loop_complete = True
+               break
+            #}
 
-      if obj.cv_data.target:  _.targets[0] = obj.cv_data.target.cv_data.uid
-      if obj.cv_data.target1: _.targets[1] = obj.cv_data.target1.cv_data.uid
-      
-      _.p = obj.cv_data.fltp
-   #}
+            valid=True
+            for sj in range(si):
+            #{
+               if stack[sj] == nextnode: # invalidated path
+               #{
+                  valid=False
+                  break
+               #}
+            #}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      box = layout.box()
-      box.prop( obj.cv_data, "target", text="red" )
-      box.prop( obj.cv_data, "target1", text="black" )
-      box.prop( obj.cv_data, "fltp", text="p(red)" )
+            if valid:
+            #{
+               stack_i[si] = 0
+               stack[si] = nextnode
+               si += 1
+               continue
+            #}
+         #}
+      #}
+
+      if loop_complete:
+      #{
+         cc = Vector((obj.cv_data.colour[0],\
+                      obj.cv_data.colour[1],\
+                      obj.cv_data.colour[2],\
+                      1.0))
+
+         for sj in range(si):
+         #{
+            sk = (sj+1)%si
+
+            if stack[sj].cv_data.classtype == 'classtype_gate' and \
+               stack[sk].cv_data.classtype == 'classtype_gate':
+            #{
+               dist = (stack[sj].location-stack[sk].location).magnitude
+               cv_draw_sbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
+            #}
+            else:
+               cv_draw_bpath( stack[sj], stack[sk], cc, cc )
+         #}
+
+         cv_view_course_i += 1
+      #}
    #}
 
    @staticmethod
-   def draw_scene_helpers( obj ):
+   def editor_interface( layout, obj ):
    #{
-      global cv_view_verts, cv_view_colours
-
-      red = (1,0,0,1)
-      black = (0,0,0,1)
-
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, red, 0.7 )
-      if obj.cv_data.target1:
-         cv_draw_arrow( obj.location, obj.cv_data.target1.location, black, 0.7 )
+      layout.prop( obj.cv_data, "target", text="'Start' from" )
+      layout.prop( obj.cv_data, "colour" )
+      layout.prop( obj.cv_data, "strp", text="Name" )
    #}
 #}
 
-# Classtype 102 [ DEPRECATED ]
-#
-#  Purpose: sends a signal to another entity
+#  Purpose: Defines a route node and links to up to two more nodes
 #
-class classtype_logic_relay(Structure):
+class classtype_route_node(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("targets",c_uint32*4)]
+   _fields_ = [("target",c_uint32),
+               ("target1",c_uint32)]
 
-   def encode_obj(_, node,node_def ):
+   def encode_obj(_, node,node_def):
    #{
-      node.classtype = 102
+      node.classtype = 401
       obj = node_def['obj']
-      if obj.cv_data.target:
-         _.targets[0] = obj.cv_data.target.cv_data.uid
-      if obj.cv_data.target1:
-         _.targets[1] = obj.cv_data.target1.cv_data.uid
-      if obj.cv_data.target2:
-         _.targets[2] = obj.cv_data.target2.cv_data.uid
-      if obj.cv_data.target3:
-         _.targets[3] = obj.cv_data.target3.cv_data.uid
+
+      if obj.cv_data.target != None:
+         _.target = obj.cv_data.target.cv_data.uid
+      if obj.cv_data.target1 != None: 
+         _.target1 = obj.cv_data.target1.cv_data.uid
    #}
 
    @staticmethod
@@ -1061,40 +662,45 @@ class classtype_logic_relay(Structure):
    #{
       global cv_view_verts, cv_view_colours
 
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, [1,1,1,1] )
-      if obj.cv_data.target1:
-         cv_draw_arrow( obj.location, obj.cv_data.target1.location, [1,1,1,1] )
-      if obj.cv_data.target2:
-         cv_draw_arrow( obj.location, obj.cv_data.target2.location, [1,1,1,1] )
-      if obj.cv_data.target3:
-         cv_draw_arrow( obj.location, obj.cv_data.target3.location, [1,1,1,1] )
+      sw = Vector((0.4,0.4,0.4,0.2))
+      sw2 = Vector((1.5,0.2,0.2,0.0))
+      if obj.cv_data.target != None:
+         cv_draw_bpath( obj, obj.cv_data.target, sw, sw )
+      if obj.cv_data.target1 != None:
+         cv_draw_bpath( obj, obj.cv_data.target1, sw, sw )
+
+      cv_draw_bhandle( obj,  1.0, (0.8,0.8,0.8,1.0) )
+      cv_draw_bhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
+
+      p1 = obj.location+ \
+            obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
+      cv_draw_arrow( obj.location, p1, sw )
    #}
 
    @staticmethod
    def editor_interface( layout, obj ):
    #{
-      layout.prop( obj.cv_data, "target", text="Triggers" )
-      layout.prop( obj.cv_data, "target1", text="Triggers" )
-      layout.prop( obj.cv_data, "target2", text="Triggers" )
-      layout.prop( obj.cv_data, "target3", text="Triggers" )
+      layout.prop( obj.cv_data, "target", text="Left" )
+      layout.prop( obj.cv_data, "target1", text="Right" )
    #}
 #}
 
-# Classtype 14
-#
-#  Purpose: Plays some audio (44100hz .ogg vorbis only)
-#           NOTE: There is a 32mb limit on the audio buffer, world audio is
-#                 decompressed and stored in signed 16 bit integers (2 bytes)
-#                 per sample.
-#
-#   volume: not used if has 3D flag
+
+# 500: Audio
+# ---------------------------------------------------------------------------- #
+
+AUDIO_SPRITE_CATEGORIES_ENUM = [
+      ('0', "Bird", ""),
+      ('1', "Nocturnal Bird", ""),
+      ('2', "Grass", ""),
+      ('3', "Wave",""),
+      ('4', "Wind",""),
+      ('5', "Wood Creaks", ""),
+   ]
+
 #    flags: 
 #           AUDIO_FLAG_LOOP        0x1
 #           AUDIO_FLAG_SPACIAL_3D  0x4  (Probably what you want)
-#           AUDIO_FLAG_AUTO_START  0x8  (Play when the world starts)
-#           ......
-#           the rest are just internal flags, only use the above 3.
 #
 class classtype_audio(Structure):
 #{
@@ -1103,9 +709,16 @@ class classtype_audio(Structure):
                ("flags",c_uint32),
                ("volume",c_float)]
 
+   dynamic_enum = [
+      ('0', "mono", ""),
+      ('1', "stereo", ""),
+      ('2', "remain compressed", ""),
+      ('3', "synthetic bird",""),
+   ]
+
    def encode_obj(_, node,node_def ):
    #{
-      node.classtype = 14
+      node.classtype = 500
 
       obj = node_def['obj']
 
@@ -1116,12 +729,9 @@ class classtype_audio(Structure):
       if obj.cv_data.bp1: flags |= 0x4
       if obj.cv_data.bp2: flags |= 0x8
 
-      if obj.cv_data.audio_format == 'stereo':
-         flags |= 0x200
-      if obj.cv_data.audio_format == 'remain compressed':
-         flags |= 0x400
-      if obj.cv_data.audio_format == 'synthetic bird':
-         flags |= 0x1000
+      if obj.cv_data.dynamic_enum == '1': flags |= 0x200
+      if obj.cv_data.dynamic_enum == '2': flags |= 0x400
+      if obj.cv_data.dynamic_enum == '3': flags |= 0x1000
 
       _.flags = flags
       _.volume = obj.cv_data.fltp
@@ -1130,12 +740,12 @@ class classtype_audio(Structure):
    @staticmethod
    def editor_interface( layout, obj ):
    #{
-      layout.prop( obj.cv_data, "strp", text = "File (.ogg)" )
+      layout.prop( obj.cv_data, "strp", text = "File (.ogg/DATA)" )
 
       layout.prop( obj.cv_data, "bp0", text = "Looping" )
       layout.prop( obj.cv_data, "bp1", text = "3D Audio" )
-      layout.prop( obj.cv_data, "bp2", text = "Auto Start" )
-      layout.prop( obj.cv_data, "audio_format" )
+      layout.prop( obj.cv_data, "bp2", text = "Play here" )
+      layout.prop( obj.cv_data, "dynamic_enum" )
 
       layout.prop( obj.cv_data, "fltp", text = "Volume (0-1)" )
    #}
@@ -1145,127 +755,101 @@ class classtype_audio(Structure):
    #{
       global cv_view_verts, cv_view_colours
 
-      cv_draw_sphere( obj.location, obj.scale[0], [1,1,0,1] )
+      if bpy.context.active_object == obj:
+         cv_draw_sphere( obj.location, obj.scale[0], [1,1,0,1] )
    #}
 #}
 
-
-# Classtype 200
-# 
-#  Purpose: world light
-#
-class classtype_world_light( Structure ):
+class classtype_audio_sprite(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("type",c_uint32),
-               ("colour",c_float*4),
-               ("angle",c_float),
-               ("range",c_float)]
+   _fields_ = [("audio",c_uint32),
+               ("category",c_uint32),
+               ("probability",c_float)]
 
-   def encode_obj(_, node, node_def):
-   #{
-      node.classtype = 200
+   dynamic_enum = AUDIO_SPRITE_CATEGORIES_ENUM
 
-      obj  = node_def['obj']
-      data = obj.data
-      _.colour[0] = data.color[0]
-      _.colour[1] = data.color[1]
-      _.colour[2] = data.color[2]
-      _.colour[3] = data.energy
-      _.range = data.cutoff_distance # this has to be manually set
-                                     # TODO: At some point, automate a min
-                                     #       threshold value
+   def encode_obj(_, node,node_def ):
+   #{
+      node.classtype = 501
+      obj = node_def['obj']
 
-      if obj.data.type == 'POINT':
-      #{
-         _.type = 0
-         _.angle = 0.0
-      #}
-      elif obj.data.type == 'SPOT':
-      #{
-         _.type = 1
-         _.angle = data.spot_size*0.5
-      #}
+      _.category = int( obj.cv_data.dynamic_enum )
+      _.probability = obj.cv_data.fltp
 
-      if data.cv_data.bp0:
-         _.type += 2
+      if obj.cv_data.target:
+         _.audio = obj.cv_data.target.cv_data.uid
    #}
 
    @staticmethod
    def editor_interface( layout, obj ):
    #{
-      pass
+      layout.prop( obj.cv_data, "dynamic_enum", text="Category" )
+      layout.prop( obj.cv_data, "target", text="Sound" )
+      layout.prop( obj.cv_data, "fltp", text="Probability" )
+   #}
+
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+
+      purple = (0.5,0.2,1,1)
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, purple, 0.1 )
    #}
 #}
 
-# Classtype 201
-# 
-#  Purpose: lighting settings for world
-#
-class classtype_lighting_info(Structure):
+
+# 600: Volumes
+# ---------------------------------------------------------------------------- #
+
+class classtype_volume_audio(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("colours",(c_float*3)*3),
-               ("directions",(c_float*2)*3),
-               ("states",c_uint32*3),
-               ("shadow_spread",c_float),
-               ("shadow_length",c_float),
-               ("ambient",c_float*3)]
+   _fields_ = [("category",c_uint32)]
 
-   def encode_obj(_, node, node_def):
+   dynamic_enum = AUDIO_SPRITE_CATEGORIES_ENUM
+
+   def encode_obj(_, node,node_def ):
    #{
-      node.classtype = 201
+      node.classtype = 600
+
+      obj = node_def['obj']
+      _.category = int(obj.cv_data.dynamic_enum)
+   #}
 
-      # TODO
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+      cv_draw_ucube( obj.matrix_world, [1,0.8,0,1] )
    #}
 
    @staticmethod
    def editor_interface( layout, obj ):
    #{
-      pass
+      layout.prop( obj.cv_data, "dynamic_enum", text="Category" )
    #}
 #}
 
-class classtype_spawn_link(Structure):
+class classtype_volume_event(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("connections",c_uint32*4)]
+   _fields_ = [("event",c_uint32)]
 
    def encode_obj(_, node,node_def ):
    #{
-      node.classtype = 0
-   #}
-
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      pass
+      node.classtype = 601
+      obj = node_def['obj']
+      _.event = encoder_process_pstr( obj.cv_data.strp )
    #}
 
    @staticmethod
    def draw_scene_helpers( obj ):
    #{
       global cv_view_verts, cv_view_colours
-
-      count = 0
-
-      for obj1 in bpy.context.collection.objects:
-      #{
-         if (obj1.cv_data.classtype != 'classtype_spawn_link') and \
-            (obj1.cv_data.classtype != 'classtype_spawn') :
-            continue
-
-         if (obj1.location - obj.location).length < 40.0:
-         #{
-            cv_draw_line( obj.location, obj1.location, [1,1,1,1] )
-            count +=1
-         #}
-
-         if count == 4:
-            break
-      #}
-      
-      cv_draw_sphere( obj.location, 20.0, [0.5,0,0.2,0.4] )
+      cv_draw_ucube( obj.matrix_world, [0.0,1.0,0,1] )
    #}
 #}
 
@@ -3020,6 +2604,33 @@ class CV_LIGHT_PANEL(bpy.types.Panel):
    #}
 #}
 
+def cv_variable_enum( scene, context ):
+#{
+   obj = context.object
+   invalid = [('0',"",""),
+              ('1',"",""),
+              ('2',"",""),
+              ('3',"",""),
+              ('4',"",""),
+              ('5',"",""),
+              ('6',"",""),
+              ('7',"",""),
+              ('8',"",""),
+              ('9',"","")]
+
+   classtype = obj.cv_data.classtype
+
+   if classtype in globals():
+   #{
+      cl = globals()[ classtype ]
+      if getattr( cl, "dynamic_enum", None ):
+         if len(cl.dynamic_enum)>0:
+            return cl.dynamic_enum
+   #}
+
+   return invalid
+#}
+
 class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
 #{
    uid: bpy.props.IntProperty( name="" )
@@ -3045,40 +2656,28 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
    colour: bpy.props.FloatVectorProperty( name="colour",subtype='COLOR',\
                                           min=0.0,max=1.0)
 
-   function: bpy.props.EnumProperty(
-      name="Function",
-      items= classtype_logic_wire.get_targeted_methods
+   dynamic_enum: bpy.props.EnumProperty(
+      name="",
+      items = cv_variable_enum
    )
 
    classtype: bpy.props.EnumProperty(
-      name="Format", 
-      items = [
-      ('classtype_none', "classtype_none", "", 0),
-      ('classtype_gate', "classtype_gate", "", 1),
-      ('classtype_spawn', "classtype_spawn", "", 3),
-      ('classtype_water', "classtype_water", "", 4),
-      ('classtype_route_node', "classtype_route_node", "", 8 ),
-      ('classtype_route', "classtype_route", "", 9 ),
-      ('classtype_audio',"classtype_audio","",14),
-      ('classtype_trigger',"classtype_trigger","",100),
-      ('classtype_logic_achievement',"classtype_logic_achievement","",101),
-      ('classtype_logic_relay',"classtype_logic_relay","",102),
-      ('classtype_logic_wire',"classtype_logic_wire","",105),
-      ('classtype_soundscape',"classtype_soundscape","",106),
-      ('classtype_logic_chances',"classtype_logic_chances","",107),
-      ('classtype_particle_box',"classtype_particle_box","",108),
-      ('classtype_signal_splitter',"classtype_signal_splitter","",109),
-      ('classtype_spawn_link',"classtype_spawn_link","",150),
-      ('classtype_nonlocal_gate', "classtype_nonlocal_gate", "", 300)
-      ])
-
-   audio_format: bpy.props.EnumProperty(
-      name="Loaded format",
+      name="Class", 
       items = [
-         ('mono', "mono", "", 0),
-         ('stereo', "stereo", "", 1),
-         ('remain compressed', "remain compressed", "", 2),
-         ('synthetic bird',"synthetic bird","",3)
+      ('classtype_none',            "None",             ""), #000
+      ('classtype_gate',            "Gate",             ""), #100
+      ('classtype_nonlocal_gate',   "Gate:NonLocal",    ""), #101
+      ('classtype_spawn',           "Spawn",            ""), #200
+      ('classtype_water',           "Water Surface",    ""), #300
+      ('classtype_route',           "Route",            ""), #400
+      ('classtype_route_node',      "Route:Node",       ""), #401
+      ('classtype_audio',           "Audio:File",       ""), #500
+      ('classtype_audio_player',    "Audio:Player",     ""), #501
+      ('classtype_audio_sprite',    "Audio:Sprite",     ""), #502
+      ('classtype_volume_audio',    "Volume:Audio",     ""), #600
+      ('classtype_volume_event',    "Volume:Event",     ""), #601
+
+      ('300',                       "ERROR",            "",300)
       ])
 #}