From: hgn Date: Fri, 21 Jul 2023 09:02:02 +0000 (+0100) Subject: unlockables basics X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=44459e0aa734b6a090d60a309e29a9a2a232c731;p=carveJwlIkooP6JGAAIwe30JlM.git unlockables basics --- diff --git a/blender_export.py b/blender_export.py index 8eb39a3..54a02e8 100644 --- a/blender_export.py +++ b/blender_export.py @@ -36,10 +36,14 @@ sr_entity_list = [ ('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 ): #{ @@ -203,6 +207,7 @@ class ent_gate(Structure): ("submesh_start",c_uint32), # v102+ ("submesh_count",c_uint32), # v102+ (can be 0) ] + sr_functions = { 0: 'unlock' } #} class ent_route_node(Structure): @@ -456,12 +461,28 @@ class ent_challenge(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' @@ -1650,6 +1671,7 @@ def sr_compile( collection ): gate.submesh_start, gate.submesh_count, _ = \ sr_compile_mesh_internal( obj ) #} + if obj_data.locked: flags |= 0x0010 gate.flags = flags gate.dimensions[0] = mesh_data.dimensions[0] @@ -1747,7 +1769,7 @@ def sr_compile( collection ): 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) @@ -1809,6 +1831,8 @@ def sr_compile( collection ): 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 @@ -1818,6 +1842,28 @@ def sr_compile( collection ): 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 ) + #} #} #} @@ -2404,6 +2450,7 @@ class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup): 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 ): @@ -2417,6 +2464,7 @@ class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup): flags = box.box() flags.prop( data[0], 'flip' ) flags.prop( data[0], 'custom' ) + flags.prop( data[0], 'locked' ) #} #} @@ -2776,31 +2824,40 @@ class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{ 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' ) #} #} @@ -3087,11 +3144,59 @@ class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{ 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): @@ -3112,6 +3217,8 @@ 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", @@ -3417,7 +3524,7 @@ def cv_tangent_basis( n, tx, ty ): # 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 @@ -4048,6 +4155,24 @@ def cv_draw(): 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: @@ -4240,7 +4365,7 @@ 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 \ diff --git a/ent_challenge.c b/ent_challenge.c index 5f22eaa..f01d335 100644 --- a/ent_challenge.c +++ b/ent_challenge.c @@ -20,6 +20,14 @@ VG_STATIC void ent_challenge_pass( world_instance *world, vg_success( "NYU Film school graduate SUCKAH\n" ); world->challenge_target = NULL; world->challenge_timer = 0.0f; + + if( challenge->id_win ){ + ent_call call; + call.data = NULL; + call.function = challenge->win_event; + call.id = challenge->id_win; + entity_call( world, &call ); + } } } diff --git a/ent_relay.c b/ent_relay.c new file mode 100644 index 0000000..60144ab --- /dev/null +++ b/ent_relay.c @@ -0,0 +1,27 @@ +#ifndef ENT_RELAY_C +#define ENT_RELAY_C + +#include "entity.h" + +VG_STATIC void ent_relay_call( world_instance *world, ent_call *call ){ + u32 index = mdl_entity_id_id( call->id ); + ent_relay *relay = mdl_arritm( &world->ent_relay, index ); + + if( call->function == 0 ){ + for( u32 i=0; itargets); i++ ){ + if( relay->targets[i][0] ){ + ent_call call; + call.data = NULL; + call.function = relay->targets[i][1]; + call.id = relay->targets[i][0]; + entity_call( world, &call ); + } + } + } + else { + vg_print_backtrace(); + vg_error( "Unhandled function id: %u\n", call->function ); + } +} + +#endif /* ENT_RELAY_C */ diff --git a/ent_unlock.c b/ent_unlock.c new file mode 100644 index 0000000..8a266ae --- /dev/null +++ b/ent_unlock.c @@ -0,0 +1,28 @@ +#ifndef ENT_UNLOCK_C +#define ENT_UNLOCK_C + +#include "entity.h" + +VG_STATIC void ent_unlock_call( world_instance *world, ent_call *call ){ + u32 index = mdl_entity_id_id( call->id ); + ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index ); + + if( call->function == 0 ){ /* unlock() */ + if( !unlock->status ){ + vg_info( "unlock( '%s' )\n", + mdl_pstr( &world->meta, unlock->pstr_alias) ); + ent_call call; + call.data = NULL; + call.function = unlock->target_event; + call.id = unlock->target; + entity_call( world, &call ); + } + unlock->status = 1; + } + else { + vg_print_backtrace(); + vg_error( "Unhandled function id: %u\n", call->function ); + } +} + +#endif /* ENT_UNLOCK_C */ diff --git a/entity.c b/entity.c index cfc7106..33bed05 100644 --- a/entity.c +++ b/entity.c @@ -7,6 +7,8 @@ #include "ent_skateshop.c" #include "ent_challenge.c" +#include "ent_unlock.c" +#include "ent_relay.c" typedef void (*fn_entity_call_handler)( world_instance *, ent_call *); @@ -18,10 +20,13 @@ VG_STATIC void entity_call( world_instance *world, ent_call *call ){ [k_ent_audio] = ent_audio_call, [k_ent_skateshop] = ent_skateshop_call, [k_ent_challenge] = ent_challenge_call, - [k_ent_ccmd] = ent_ccmd_call + [k_ent_ccmd] = ent_ccmd_call, + [k_ent_gate] = ent_gate_call, + [k_ent_relay] = ent_relay_call, + [k_ent_unlock] = ent_unlock_call }; - if( type > vg_list_size(table) ){ + if( type >= vg_list_size(table) ){ vg_error( "call to entity type: %u is out of range\n", type ); return; } diff --git a/entity.h b/entity.h index da2341e..ae9d63b 100644 --- a/entity.h +++ b/entity.h @@ -27,6 +27,8 @@ typedef struct ent_swspreview ent_swspreview; typedef struct ent_worldinfo ent_worldinfo; typedef struct ent_ccmd ent_ccmd; typedef struct ent_challenge ent_challenge; +typedef struct ent_unlock ent_unlock; +typedef struct ent_relay ent_relay; enum entity_alias{ k_ent_none = 0, @@ -48,7 +50,8 @@ enum entity_alias{ k_ent_worldinfo = 16, k_ent_ccmd = 17, k_ent_challenge = 18, - k_ent_relay = 19 + k_ent_unlock = 19, + k_ent_relay = 20 }; static u32 mdl_entity_id_type( u32 entity_id ){ @@ -108,6 +111,7 @@ enum ent_gate_flag{ NOTE: if set, it adds the flip flag. */ k_ent_gate_flip = 0x4, /* flip direction 180* for exiting portal */ k_ent_gate_custom_mesh = 0x8, /* use a custom submesh instead of default */ + k_ent_gate_locked = 0x10,/* has to be unlocked to be useful */ }; struct ent_gate{ @@ -400,10 +404,23 @@ struct ent_challenge{ u32 submesh_start, submesh_count, id_next, - filter; + filter, + id_win, + win_event; f32 time_limit; }; +struct ent_unlock{ + u32 pstr_alias, + target, + target_event, + status; +}; + +struct ent_relay { + u32 targets[4][2]; +}; + typedef struct ent_call ent_call; struct ent_call{ u32 id, function; diff --git a/maps_src/mp_spawn/main.mdl b/maps_src/mp_spawn/main.mdl index 4467e7b..4d9629f 100644 Binary files a/maps_src/mp_spawn/main.mdl and b/maps_src/mp_spawn/main.mdl differ diff --git a/player_common.c b/player_common.c index 270f40e..3711405 100644 --- a/player_common.c +++ b/player_common.c @@ -40,8 +40,10 @@ VG_STATIC void player_camera_portal_correction( player_instance *player ) q_mulv( player->gate_waiting->q[1], (v3f){0.0f,0.0f,1.0f}, plane ); plane[3] = v3_dot( plane, player->gate_waiting->co[1] ); + f32 pol = v3_dot( player->cam.pos, plane ) - plane[3]; + /* check camera polarity */ - if( v3_dot( player->cam.pos, plane ) < plane[3] ) { + if( (pol < 0.0f) || (pol > 5.0f) ) { vg_success( "Plane cleared\n" ); player_apply_transport_to_cam( player->gate_waiting->transport ); player->gate_waiting = NULL; diff --git a/world.h b/world.h index 49c030e..e95f005 100644 --- a/world.h +++ b/world.h @@ -156,7 +156,9 @@ struct world_instance { ent_camera, ent_swspreview, ent_ccmd, - ent_challenge; + ent_challenge, + ent_unlock, + ent_relay; ent_gate *rendering_gate; diff --git a/world_gate.c b/world_gate.c index a4cf8fd..0a64551 100644 --- a/world_gate.c +++ b/world_gate.c @@ -265,6 +265,7 @@ VG_STATIC ent_gate *world_intersect_gates( world_instance *world, ent_gate *gate = mdl_arritm( &world->ent_gate, i ); if( !(gate->flags & k_ent_gate_linked) ) continue; + if( gate->flags & k_ent_gate_locked ) continue; if( gate->flags & k_ent_gate_nonlocal ){ if( world_loader.state != k_world_loader_none ){ @@ -367,4 +368,17 @@ matched:; } } +VG_STATIC void ent_gate_call( world_instance *world, ent_call *call ){ + u32 index = mdl_entity_id_id( call->id ); + ent_gate *gate = mdl_arritm( &world->ent_gate, index ); + + if( call->function == 0 ){ /* unlock() */ + gate->flags &= ~k_ent_gate_locked; + } + else { + vg_print_backtrace(); + vg_error( "Unhandled function id: %u\n", call->function ); + } +} + #endif /* WORLD_GATE_C */ diff --git a/world_gate.h b/world_gate.h index 4b3299a..89040fd 100644 --- a/world_gate.h +++ b/world_gate.h @@ -27,5 +27,6 @@ VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last ); VG_STATIC ent_gate *world_intersect_gates( world_instance *world, v3f pos, v3f last ); +VG_STATIC void ent_gate_call( world_instance *world, ent_call *call ); #endif /* WORLD_GATE_H */ diff --git a/world_load.c b/world_load.c index 01fa9cc..afd4986 100644 --- a/world_load.c +++ b/world_load.c @@ -60,6 +60,8 @@ VG_STATIC void world_load_mdl( const char *path ) mdl_load_array( meta, &world->ent_swspreview,"ent_swspreview", heap ); mdl_load_array( meta, &world->ent_ccmd, "ent_ccmd", heap ); mdl_load_array( meta, &world->ent_challenge, "ent_challenge", heap ); + mdl_load_array( meta, &world->ent_unlock, "ent_unlock", heap ); + mdl_load_array( meta, &world->ent_relay, "ent_relay", heap ); mdl_array_ptr infos; mdl_load_array( meta, &infos, "ent_worldinfo", vg_mem.scratch ); diff --git a/world_render.c b/world_render.c index a7ff25c..d59d4e1 100644 --- a/world_render.c +++ b/world_render.c @@ -535,6 +535,8 @@ VG_STATIC void render_world_gates( world_instance *world, camera *cam, world->rendering_gate = gate; if( gate ){ + if( gate->flags & k_ent_gate_locked ) return; + if( gate->flags & k_ent_gate_nonlocal ){ if( world_loader.state != k_world_loader_none ){ world->rendering_gate = NULL;