-import bpy, math, gpu, os
+import bpy, blf, math, gpu, os
import cProfile
from ctypes import *
from mathutils import *
from gpu_extras.batch import batch_for_shader
from bpy_extras import mesh_utils
+from bpy_extras import view3d_utils
bl_info = {
"name":"Skaterift .mdl exporter",
('ent_menuitem', 'Menu Item', '', 15 ),
('ent_worldinfo', 'World Info', '', 16 ),
('ent_ccmd', 'CCmd', '', 17 ),
- ('ent_challenge', 'Challenge', '', 18 )
+ ('ent_challenge', 'Challenge', '', 18 ),
+ ('ent_unlock', 'Unlockable', '', 19 ),
+ ('ent_relay', 'Relay', '', 20 )
]
+MDL_VERSION_NR = 102
+SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_unlock', \
+ 'ent_relay', 'ent_skateshop', 'ent_challenge' ]
+
def get_entity_enum_id( alias ):
#{
for et in sr_entity_list:#{
class ent_gate(Structure):
#{
- _fields_ = [("type",c_uint32),
+ _fields_ = [("flags",c_uint32),
("target", c_uint32),
("key",c_uint32),
("dimensions", c_float*3),
("_anonymous_union",version_refcount_union),
("timing_time",c_double),
("routes",c_uint16*4),
- ("route_count",c_uint8)]
+ ("route_count",c_uint8),
+ ("submesh_start",c_uint32), # v102+
+ ("submesh_count",c_uint32), # v102+ (can be 0)
+ ]
+ sr_functions = { 0: 'unlock' }
#}
class ent_route_node(Structure):
("submesh_start",c_uint32), ("submesh_count",c_uint32),
("id_next",c_uint32),
("filter",c_uint32),
+ ("id_win",c_uint32),
+ ("win_event",c_uint32),
("time_limit",c_float)]
sr_functions = { 0: 'trigger',
1: 'start_challenge' }
#}
+class ent_unlock(Structure):#{
+ _fields_ = [("pstr_alias",c_uint32),
+ ("target",c_uint32),
+ ("target_event",c_uint32),
+ ("status",c_uint32)]
+ sr_functions = { 0: 'unlock' }
+#}
+
+class ent_relay(Structure):#{
+ _fields_ = [("targets",(c_uint32*2)*4),
+ ("targets_events",c_uint32*4)]
+ sr_functions = { 0: 'trigger' }
+#}
+
def obj_ent_type( obj ):
#{
if obj.type == 'ARMATURE': return 'mdl_armature'
if ent_type == 'ent_font_variant': continue
if ent_type == 'ent_menuitem': continue
if ent_type == 'ent_challenge': continue
+
+ #TODO: This is messy.
+ if ent_type == 'ent_gate':#{
+ obj_data = obj.SR_data.ent_gate[0]
+ if obj_data.custom: continue
+ #}
#--------------------------
print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
obj_data = obj.SR_data.ent_gate[0]
mesh_data = obj.data.SR_data.ent_gate[0]
+ flags = 0x0000
+
if obj_data.tipo == 'default':#{
if obj_data.target:#{
gate.target = sr_compile.entity_ids[obj_data.target.name]
- gate.type = 1
+ flags |= 0x0001
#}
#}
elif obj_data.tipo == 'nonlocal':#{
gate.target = 0
gate.key = sr_compile_string(obj_data.key)
- gate.type = 2
+ flags |= 0x0002
+ #}
+
+ if obj_data.flip: flags |= 0x0004
+ if obj_data.custom:#{
+ flags |= 0x0008
+ gate.submesh_start, gate.submesh_count, _ = \
+ sr_compile_mesh_internal( obj )
#}
- else: gate.type = 0
+ if obj_data.locked: flags |= 0x0010
+ gate.flags = flags
gate.dimensions[0] = mesh_data.dimensions[0]
gate.dimensions[1] = mesh_data.dimensions[1]
if obj_data.target:#{
volume.target = sr_entity_id( obj_data.target )
- volume._anon.trigger.event = obj_data.event
+ volume._anon.trigger.event = obj_data.target_event
#}
sr_ent_push(volume)
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 = 0
challenge.time_limit = obj_data.time_limit
sr_ent_push( challenge )
#}
+ elif ent_type == 'ent_unlock':#{
+ unlock = ent_unlock()
+ obj_data = obj.SR_data.ent_unlock[0]
+ 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_relay':#{
+ relay = ent_relay()
+ obj_data = obj.SR_data.ent_relay[0]
+ relay.targets[0][0] = sr_entity_id( obj_data.target0 )
+ relay.targets[1][0] = sr_entity_id( obj_data.target1 )
+ relay.targets[2][0] = sr_entity_id( obj_data.target2 )
+ relay.targets[3][0] = sr_entity_id( obj_data.target3 )
+ relay.targets[0][1] = obj_data.target0_event
+ relay.targets[1][1] = obj_data.target1_event
+ relay.targets[2][1] = obj_data.target2_event
+ relay.targets[3][1] = obj_data.target3_event
+ sr_ent_push( relay )
+ #}
#}
#}
os.makedirs(os.path.dirname(path),exist_ok=True)
fp = open( path, "wb" )
header = mdl_header()
- header.version = 101
+ header.version = MDL_VERSION_NR
sr_array_title( header.arrays, \
'index', len(file_array_instructions), \
sizeof(mdl_array), header_size )
active_object = context.active_object
if not active_object: return
- _.layout.operator( 'skaterift.copy_entity_data', \
- text=F'Copy entity data to {len(context.selected_objects)-1} '+\
- F'other objects' )
+ amount = max( 0, len(context.selected_objects)-1 )
+
+ row = _.layout.row()
+ row.operator( 'skaterift.copy_entity_data', \
+ text=F'Copy entity data to {amount} other objects' )
+ if amount == 0: row.enabled=False
box = _.layout.box()
row = box.row()
row.label( text=active_object.name )
row.scale_y = 1.5
- def _draw_prop_collection( data ): #{
+ def _draw_prop_collection( source, data ): #{
nonlocal box
row = box.row()
row.alignment = 'CENTER'
row.enabled = False
row.scale_y = 1.5
- row.label( text=F'{data[0]}' )
+ row.label( text=F'{source}' )
if hasattr(type(data[0]),'sr_inspector'):#{
type(data[0]).sr_inspector( box, data )
text=F'Mirror attributes to {mb.name}' )
#}
- _draw_prop_collection( [bones.active.SR_data ] )
+ _draw_prop_collection( \
+ F'bpy.types.Bone["{bones.active.name}"].SR_data',\
+ [bones.active.SR_data ] )
#}
else: #{
row = box.row()
#}
#}
elif active_object.type == 'LIGHT': #{
- _draw_prop_collection( [active_object.data.SR_data] )
+ _draw_prop_collection( \
+ F'bpy.types.Light["{active_object.data.name}"].SR_data', \
+ [active_object.data.SR_data] )
#}
elif active_object.type in ['EMPTY','CURVE','MESH']:#{
box.prop( active_object.SR_data, "ent_type" )
ent_type = active_object.SR_data.ent_type
col = getattr( active_object.SR_data, ent_type, None )
- if col != None and len(col)!=0: _draw_prop_collection( col )
+ if col != None and len(col)!=0:
+ _draw_prop_collection( \
+ F'bpy.types.Object["{active_object.name}"].SR_data.{ent_type}[0]', \
+ col )
if active_object.type == 'MESH':#{
col = getattr( active_object.data.SR_data, ent_type, None )
- if col != None and len(col)!=0: _draw_prop_collection( col )
+ if col != None and len(col)!=0:
+ _draw_prop_collection( \
+ F'bpy.types.Mesh["{active_object.data.name}"].SR_data.{ent_type}[0]', \
+ col )
#}
#}
#}
key: bpy.props.StringProperty()
tipo: bpy.props.EnumProperty(items=(('default', 'Default', ""),
- ('nonlocal', 'Non-Local', ""),))
+ ('nonlocal', 'Non-Local', "")))
+
+ flip: bpy.props.BoolProperty( name="Flip exit", default=False )
+ custom: bpy.props.BoolProperty( name="Mesh is surface", default=False )
+ locked: bpy.props.BoolProperty( name="Start Locked", default=False )
@staticmethod
def sr_inspector( layout, data ):
if data[0].tipo == 'default': box.prop( data[0], 'target' )
elif data[0].tipo == 'nonlocal': box.prop( data[0], 'key' )
+
+ flags = box.box()
+ flags.prop( data[0], 'flip' )
+ flags.prop( data[0], 'custom' )
+ flags.prop( data[0], 'locked' )
#}
#}
poll=lambda self,obj: sr_filter_ent_type(obj,\
['ent_audio','ent_skateshop','ent_ccmd',\
'ent_challenge']))
-
- event: bpy.props.IntProperty( name="Event/Method" )
+ target_event: bpy.props.IntProperty( name="Event/Method" )
@staticmethod
- def sr_inspector( layout, data ):#{
- layout.prop( data[0], 'subtype' )
- layout.prop( data[0], 'target' )
+ def inspect_target( layout, data, propname ):#{
+ box = layout.box()
+ box.prop( data[0], propname )
- row = layout.row()
- row.prop( data[0], 'event' )
+ row = box.row()
+ row.prop( data[0], propname + '_event')
- if data[0].target:#{
- tipo = data[0].target.SR_data.ent_type
+ target = getattr( data[0], propname )
+ if target:#{
+ tipo = target.SR_data.ent_type
cls = globals()[ tipo ]
table = getattr( cls, 'sr_functions', None )
if table:#{
- if data[0].event in table:#{
- row.label( text=table[data[0].event] )
- #}
- else:#{
+ index = getattr( data[0], propname+'_event')
+ if index in table:
+ row.label( text=table[index] )
+ else:
row.label( text="undefined function" )
- #}
#}
#}
+ 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' )
#}
#}
type=bpy.types.Object, name="Next", \
poll=lambda self,obj: sr_filter_ent_type(obj,['ent_challenge']))
target: bpy.props.PointerProperty( \
- type=bpy.types.Object, name="Target", \
- poll=lambda self,obj: sr_filter_ent_type(obj,\
- ['ent_audio','ent_ccmd']))
- event: bpy.props.IntProperty( name="Event/Method" )
+ type=bpy.types.Object, name="Win", \
+ poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+ target_event: bpy.props.IntProperty( name="Event/Method" )
time_limit: bpy.props.FloatProperty( name="Time Limit", default=1.0 )
+
+ @staticmethod
+ def sr_inspector( layout, data ):#{
+ layout.prop( data[0], 'proxima' )
+ layout.prop( data[0], 'time_limit' )
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target' )
+ #}
+#}
+
+class SR_OBJECT_ENT_UNLOCK(bpy.types.PropertyGroup):#{
+ alias: bpy.props.StringProperty( name="Alias" )
+ 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" )
+
+ @staticmethod
+ def sr_inspector( layout, data ):#{
+ layout.prop( data[0], 'alias' )
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target' )
+ #}
+#}
+
+class SR_OBJECT_ENT_RELAY(bpy.types.PropertyGroup):#{
+ target0: bpy.props.PointerProperty( \
+ type=bpy.types.Object, name="Target 0", \
+ poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+ target1: bpy.props.PointerProperty( \
+ type=bpy.types.Object, name="Target 1", \
+ poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+ target2: bpy.props.PointerProperty( \
+ type=bpy.types.Object, name="Target 2", \
+ poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+ target3: bpy.props.PointerProperty( \
+ type=bpy.types.Object, name="Target 3", \
+ poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+
+ target0_event: bpy.props.IntProperty( name="Event" )
+ target1_event: bpy.props.IntProperty( name="Event" )
+ target2_event: bpy.props.IntProperty( name="Event" )
+ target3_event: bpy.props.IntProperty( name="Event" )
+
+ @staticmethod
+ def sr_inspector( layout, data ):#{
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target0' )
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target1' )
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target2' )
+ SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target3' )
+ #}
#}
class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
ent_worldinfo: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORLD_INFO)
ent_ccmd: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CCMD)
ent_challenge: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CHALLENGE)
+ ent_unlock: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_UNLOCK)
+ ent_relay: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_RELAY)
ent_type: bpy.props.EnumProperty(
name="Type",
# ---------------------------------------------------------------------------- #
cv_view_draw_handler = None
+cv_view_pixel_handler = None
cv_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
cv_view_verts = []
cv_view_colours = []
# Draw coloured arrow
#
-def cv_draw_arrow( p0, p1, c0, size=0.15 ):
+def cv_draw_arrow( p0, p1, c0, size=0.25 ):
#{
global cv_view_verts, cv_view_colours
#}
#}
-def cv_draw():
-#{
+def cv_draw():#{
global cv_view_shader
global cv_view_verts
global cv_view_colours
if data.proxima:#{
cv_draw_arrow( obj.location, data.proxima.location, (0,0.2,1.0) )
#}
+ if data.target:
+ cv_draw_arrow( obj.location, data.target.location, (0,1.0,0.0) )
+ #}
+ elif ent_type == 'ent_relay':#{
+ data = obj.SR_data.ent_relay[0]
+ if data.target0:
+ cv_draw_arrow( obj.location, data.target0.location, (0,1,0) )
+ if data.target1:
+ cv_draw_arrow( obj.location, data.target1.location, (0,1,0) )
+ if data.target2:
+ cv_draw_arrow( obj.location, data.target2.location, (0,1,0) )
+ if data.target3:
+ cv_draw_arrow( obj.location, data.target3.location, (0,1,0) )
+ #}
+ elif ent_type == 'ent_unlock':#{
+ data = obj.SR_data.ent_unlock[0]
+ if data.target:
+ cv_draw_arrow( obj.location, data.target.location, (0,1.0,0.0) )
#}
elif ent_type == 'ent_audio':#{
if obj.SR_data.ent_audio[0].flag_3d:
#}
cv_draw_lines()
- return
+#}
+
+def pos3d_to_2d( pos ):#{
+ return view3d_utils.location_3d_to_region_2d( \
+ bpy.context.region, \
+ bpy.context.space_data.region_3d, pos )
+#}
+
+def cv_draw_pixel():#{
+ if not bpy.context.scene.SR_data.gizmos: return
+ blf.size(0,10)
+ blf.color(0, 1.0,1.0,1.0,0.9)
+ blf.enable(0,blf.SHADOW)
+ blf.shadow(0,3,0.0,0.0,0.0,1.0)
+ for obj in bpy.context.collection.objects:#{
+ ent_type = obj_ent_type( obj )
+
+ if ent_type != 'none':#{
+ co = pos3d_to_2d( obj.location )
+
+ if not co: continue
+ blf.position(0,co[0],co[1],0)
+ blf.draw(0,ent_type)
+ #}
+ #}
#}
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_CHALLENGE,SR_OBJECT_ENT_UNLOCK,SR_OBJECT_ENT_RELAY,\
\
SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES,
SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \
bpy.types.Material.SR_data = \
bpy.props.PointerProperty(type=SR_MATERIAL_PROPERTIES)
- global cv_view_draw_handler
+ global cv_view_draw_handler, cv_view_pixel_handler
cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
cv_draw,(),'WINDOW','POST_VIEW')
+ cv_view_pixel_handler = bpy.types.SpaceView3D.draw_handler_add(\
+ cv_draw_pixel,(),'WINDOW','POST_PIXEL')
#}
def unregister():
for c in classes:
bpy.utils.unregister_class(c)
- global cv_view_draw_handler
+ global cv_view_draw_handler, cv_view_pixel_handler
bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')
+ bpy.types.SpaceView3D.draw_handler_remove(cv_view_pixel_handler,'WINDOW')
#}
# ---------------------------------------------------------------------------- #