too many movements to describe...
authorhgn <hgodden00@gmail.com>
Tue, 11 Mar 2025 23:36:21 +0000 (23:36 +0000)
committerhgn <hgodden00@gmail.com>
Tue, 11 Mar 2025 23:36:21 +0000 (23:36 +0000)
32 files changed:
content_skaterift/maps/dev_heaven/main.mdl
content_skaterift/maps/dev_hub/main.mdl
content_skaterift/maps/dev_tutorial/main.mdl
content_skaterift/metascenes/ch1s2.ms
content_skaterift/metascenes/ch1s5.ms
content_skaterift/metascenes/ch1s6a.ms
content_skaterift/metascenes/unlock_docks.ms [new file with mode: 0644]
skaterift_blender/sr_main.py
skaterift_blender/sr_mdl.py
src/ent_challenge.c
src/ent_challenge.h
src/ent_region.c
src/ent_script.c [new file with mode: 0644]
src/ent_script.h [new file with mode: 0644]
src/entity.c
src/entity.h
src/menu.c
src/metascene.c
src/metascene.h
src/save.c
src/scripts/heaven.c [new file with mode: 0644]
src/scripts/hub.c [new file with mode: 0644]
src/scripts/tutorial_island.c [new file with mode: 0644]
src/skaterift.c
src/skaterift_script.c
src/skaterift_script.h
src/world.c
src/world.h
src/world_entity.c
src/world_load.c
src/world_map.c
src/world_volumes.c

index f6d33871cf85c88a2e705bfac18d2d2b6dc29468..62c8374aba45440d36dc7a0aea36de3f0f26b762 100644 (file)
Binary files a/content_skaterift/maps/dev_heaven/main.mdl and b/content_skaterift/maps/dev_heaven/main.mdl differ
index 54de7f36cfe74454a5dd23e613bbb9b04b106cec..4ce03aeab17072a534b69146bff86dec44f2667d 100644 (file)
Binary files a/content_skaterift/maps/dev_hub/main.mdl and b/content_skaterift/maps/dev_hub/main.mdl differ
index 454775a172169bf1b94da6371269505d2a85c41d..f4de6ed6accafce6f9db0078a784ac2da6a894a2 100644 (file)
Binary files a/content_skaterift/maps/dev_tutorial/main.mdl and b/content_skaterift/maps/dev_tutorial/main.mdl differ
index 2b5872ad2dfa000e65cf6cceceb2cac47d55a592..dab0ebe2a4a4e476d8711309cc5cdb6fbccbf056 100644 (file)
Binary files a/content_skaterift/metascenes/ch1s2.ms and b/content_skaterift/metascenes/ch1s2.ms differ
index 74d1990a3d3e7556490de09ec6a0ffb796fe8cc5..5fa5f81b35385f0df573fc0a33905426ad6bc302 100644 (file)
Binary files a/content_skaterift/metascenes/ch1s5.ms and b/content_skaterift/metascenes/ch1s5.ms differ
index f9474b17913b84652ff4d89bd2f6452adb1ba524..cc1d3f0f6756e5b54856ce014fc331e919c51268 100644 (file)
Binary files a/content_skaterift/metascenes/ch1s6a.ms and b/content_skaterift/metascenes/ch1s6a.ms differ
diff --git a/content_skaterift/metascenes/unlock_docks.ms b/content_skaterift/metascenes/unlock_docks.ms
new file mode 100644 (file)
index 0000000..9e29743
Binary files /dev/null and b/content_skaterift/metascenes/unlock_docks.ms differ
index ca1771aba457bce69f2015e8f696d18fa433b0f3..fb3a5a259b8045ba862ae05c05e598fd46f253d8 100644 (file)
@@ -39,13 +39,14 @@ sr_entity_list = [
    ('ent_glider',       'Glider',         '', 26 ),
    ('ent_npc',          'npc',            '', 27 ),
    # reserved 28.. armature
+   ('ent_script',       'Script',         '', 29 )
 ]
 
 MDL_VERSION_NR = 109
 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', 'ent_water' ]
+                   'ent_npc', 'ent_water', 'ent_script' ]
 
 def get_entity_enum_id( alias ):
 #{
@@ -467,6 +468,14 @@ class ent_ccmd(Structure):
    _fields_ = [("pstr_command",c_uint32)]
 #}
 
+class ent_script(Structure):
+#{
+   _fields_ = [("pstr_script_name",c_uint32),
+               ("entity_list_id",c_uint32),
+               ("flags",c_uint32)]
+#}
+
+
 class ent_objective(Structure):#{
    _fields_ = [("transform",mdl_transform),
                ("submesh_start",c_uint32), ("submesh_count",c_uint32),
@@ -1978,7 +1987,7 @@ class SR_OBJECT_ENT_CHALLENGE(bpy.types.PropertyGroup):#{
       layout.prop( data[0], 'alias' )
       layout.prop( data[0], 'camera', text=("Camera" if not data[0].is_story else "Related Objective") )
       layout.prop( data[0], 'first', text=("First Objective") )
-      layout.prop( data[0], 'visible_when_unlocked' )
+      #layout.prop( data[0], 'visible_when_unlocked' )
       layout.prop( data[0], 'time_limit' )
       layout.prop( data[0], 'is_story' )
       SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target', text="On Activate" )
@@ -2033,6 +2042,15 @@ class SR_OBJECT_ENT_RELAY(bpy.types.PropertyGroup):#{
    #}
 #}
 
+class SR_OBJECT_ENT_SCRIPT(bpy.types.PropertyGroup):
+#{
+   script_alias: bpy.props.StringProperty( name="Script alias" )
+   flags: bpy.props.IntProperty( name="Flags" )
+   entity_list: bpy.props.PointerProperty( \
+            type=bpy.types.Object, name="Entity List", \
+            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_list']))
+#}
+
 class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
 #{
    ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE)
@@ -2058,6 +2076,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    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_script: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SCRIPT)
 
    ent_type: bpy.props.EnumProperty(
       name="Type",
@@ -2901,12 +2920,31 @@ def cv_draw():#{
             if data.target3:
                cv_draw_arrow( obj.location, data.target3.location, (1,1,1) )
          #}
-         elif ent_type == 'ent_list':#{
-            data = obj.SR_data.ent_list[0]
-            for subj in data.entities:
+         elif ent_type == 'ent_list':
+         #{
+            if obj.select_get():
+            #{
+               data = obj.SR_data.ent_list[0]
+               for subj in data.entities:
+               #{
+                  if subj.target:
+                     cv_draw_line_dotted( obj.location, subj.target.location, (0.9,0.0,0.7))
+               #}
+            #}
+         #}
+         elif ent_type == 'ent_script':
+         #{
+            data = obj.SR_data.ent_script[0]
+            
+            cc = (0.9,0.0,0.7)
+
+            cv_draw_ucube( obj.matrix_world, cc, Vector((0.1,0.1,0.1)) )
+            cv_draw_ucube( obj.matrix_world, cc, Vector((0.2,0.2,0.2)) )
+
+            if data.entity_list:
             #{
-               if subj:
-                  cv_draw_arrow( obj.location, subj.target.location, (0.5,0.8,0.2) )
+               cv_draw_arrow( obj.location, data.entity_list.location, cc )
+               cv_draw_arrow( data.entity_list.location, obj.location, cc )
             #}
          #}
          elif ent_type == 'ent_challenge':#{
@@ -3124,7 +3162,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
             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_ENT_GLIDER, SR_OBJECT_ENT_NPC, SR_OBJECT_ENT_SCRIPT, \
             \
             SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, 
             SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \
index 32da8a5551fc2209da52a99212ad07796c15f615..4af7a41d37e8a657e7eb1a723d33d6e80044d8f2 100644 (file)
@@ -836,6 +836,14 @@ def _mdl_compiler_compile_entities():
             ccmd.pstr_command = _af_pack_string( obj_data.command )
             sr_ent_push( ccmd )
          #}
+         elif ent_type == 'ent_script':#{
+            obj_data = obj.SR_data.ent_script[0]
+            script = ent_script()
+            script.pstr_script_name = _af_pack_string( obj_data.script_alias )
+            script.entity_list_id = sr_entity_id( obj_data.entity_list )
+            script.falgs = obj_data.flags
+            sr_ent_push( script )
+         #}
          elif ent_type == 'ent_objective':#{
             objective = ent_objective()
             obj_data = obj.SR_data.ent_objective[0]
@@ -867,7 +875,7 @@ def _mdl_compiler_compile_entities():
             challenge.first_objective_id = sr_entity_id( obj_data.first )
             challenge.camera_id = sr_entity_id( obj_data.camera )
             challenge.status = 0
-            challenge.visible_when_unlocked_id = sr_entity_id( obj_data.visible_when_unlocked )
+            #challenge.visible_when_unlocked_id = sr_entity_id( obj_data.visible_when_unlocked )
             sr_ent_push( challenge )
          #}
          elif ent_type == 'ent_list':
index 09bb5f50d6accef9180d2c7a35ade6f8b3b87cee..6313821401675fcf21d738d4da826fb42de8161a 100644 (file)
@@ -4,22 +4,6 @@
 #include "gui.h"
 #include "audio.h"
 
-static bool ent_challenge_is_acceptable( world_instance *world, u32 challenge_index )
-{
-   ent_challenge *challenge = af_arritm( &world->ent_challenge, challenge_index );
-
-   if( challenge->flags & k_ent_challenge_is_story )
-   {
-      struct script_info *script = world->events[ challenge_index ].story_script;
-      if( script->viewed_time || !script->availible )
-      {
-         return 0;
-      }
-   }
-
-   return 1;
-}
-
 entity_call_result ent_challenge_call( world_instance *world, ent_call *call )
 {
    u32 index = mdl_entity_id_id( call->id );
@@ -42,77 +26,51 @@ entity_call_result ent_challenge_call( world_instance *world, ent_call *call )
       challenge->status = 1;
       return k_entity_call_result_OK;
    }
-   else if( call->function == 1 ) /* view() */
-   {
-      if( localplayer.subsystem == k_player_subsystem_walk )
-      {
-         if( ent_challenge_is_acceptable( world, index ) )
-         {
-            if( world_set_event( k_world_event_challenge ) )
-            {
-               _world.challenge_state = k_challenge_state_none;
-               _world.active_challenge_id = call->id;
-               gui_helper_reset( 1 );
-               vg_str text;
-               if( gui_new_helper( input_button_list[k_srbind_maccept], &text ))
-                  vg_strcat( &text, (challenge->flags & k_ent_challenge_is_story)? "Play video": "View Challenge" );
-            }
-         }
-      }
 
+   if( (_world.event == k_world_event_challenge) && (_world.challenge_state == k_challenge_state_running) )
+   {
       return k_entity_call_result_OK;
    }
-   else if( call->function == -1 ) /* unview() */
+   else
    {
-      if( _world.challenge_state != k_challenge_state_running )
+      if( call->function == 1 ) /* view() */
       {
-         if( ent_challenge_is_acceptable( world, index ) )
+         if( localplayer.subsystem == k_player_subsystem_walk )
          {
-            if( world_clear_event( k_world_event_challenge ) )
+            if( !(challenge->flags & k_ent_challenge_locked) )
             {
-               _world.challenge_state = k_challenge_state_none;
-               _world.active_challenge_id = 0;
-               gui_helper_reset( k_gui_helper_mode_clear );
+               if( world_set_event( k_world_event_challenge ) )
+               {
+                  _world.challenge_state = k_challenge_state_none;
+                  _world.active_challenge_id = call->id;
+                  gui_helper_reset( 1 );
+                  vg_str text;
+                  if( gui_new_helper( input_button_list[k_srbind_maccept], &text ))
+                     vg_strcat( &text, (challenge->flags & k_ent_challenge_is_story)? "Play video": "View Challenge" );
+               }
             }
          }
-      }
-      return k_entity_call_result_OK;
-   }
-   else 
-      return k_entity_call_result_unhandled;
-}
-
-void ent_challenge_visibility( ent_challenge *challenge, bool visible )
-{
-   world_instance *world = &_world.main;
-   if( challenge->visible_when_unlocked_id )
-   {
-      ent_list *list = af_arritm( &world->ent_list, mdl_entity_id_id( challenge->visible_when_unlocked_id ) );
 
-      for( u32 i=0; i<list->entity_ref_count; i ++ )
+         return k_entity_call_result_OK;
+      }
+      else if( call->function == -1 ) /* unview() */
       {
-         u32 ref_index = list->entity_ref_start + i;
-         
-         file_entity_ref *ref = af_arritm( &world->file_entity_ref, ref_index );
-
-         u32 type = mdl_entity_id_type( ref->entity_id ),
-             index = mdl_entity_id_id( ref->entity_id );
-
-         if( type == k_ent_objective )
-         {
-            ent_objective *objective = af_arritm( &world->ent_objective, index );
-
-            if( visible ) objective->flags &= ~((u32)k_ent_objective_hidden);
-            else          objective->flags |= k_ent_objective_hidden;
-         }
-         else if( type == k_ent_prop )
+         if( _world.challenge_state != k_challenge_state_running )
          {
-            ent_prop *prop = af_arritm( &world->ent_prop, index );
-
-            if( visible ) prop->flags &= ~((u32)k_prop_flag_hidden);
-            else          prop->flags |= k_prop_flag_hidden;
+            if( !(challenge->flags & k_ent_challenge_locked) )
+            {
+               if( world_clear_event( k_world_event_challenge ) )
+               {
+                  _world.challenge_state = k_challenge_state_none;
+                  _world.active_challenge_id = 0;
+                  gui_helper_reset( k_gui_helper_mode_clear );
+               }
+            }
          }
+         return k_entity_call_result_OK;
       }
+      else
+         return k_entity_call_result_unhandled;
    }
 }
 
@@ -220,6 +178,7 @@ void ent_challenge_update(void)
          {
             if( challenge->on_activate_id )
             {
+               srinput.state = k_input_state_resume;
                gui_helper_reset( k_gui_helper_mode_clear );
                ent_call call;
                call.data = NULL;
@@ -240,6 +199,7 @@ void ent_challenge_update(void)
       {
          if( button_down( k_srbind_maccept ) )
          {
+            srinput.state = k_input_state_resume;
             gui_helper_reset( k_gui_helper_mode_clear );
             vg_str text;
             if( gui_new_helper( input_button_list[k_srbind_maccept], &text ))
index 00146706693b7c98000fe007525283cc667cbf8d..73641bd4d3f4f98ac45ec528384c38923abd8931 100644 (file)
@@ -2,4 +2,3 @@
 #include "entity.h"
 
 entity_call_result ent_challenge_call( world_instance *world, ent_call *call );
-void ent_challenge_visibility( ent_challenge *challenge, bool visible );
index 4a64020a59591fb752752f31f02d4b08b84a673a..197e84e17cc604f6fbe8234a91aa5d1656a2bdec 100644 (file)
@@ -77,48 +77,53 @@ entity_call_result ent_region_call( world_instance *world, ent_call *call )
  */
 void ent_region_re_eval( world_instance *world )
 {
-   u32 world_total = k_ent_route_flag_achieve_gold | 
-                     k_ent_route_flag_achieve_silver;
+   u32 world_total = k_ent_route_flag_achieve_gold | k_ent_route_flag_achieve_silver;
 
-   for( u32 i=0; i<af_arrcount(&world->ent_region); i ++ ){
+   for( u32 i=0; i<af_arrcount(&world->ent_region); i ++ )
+   {
       ent_region *region = af_arritm(&world->ent_region, i);
 
       if( !region->zone_volume )
          continue;
 
-      ent_volume *volume = af_arritm(&world->ent_volume,
-            mdl_entity_id_id(region->zone_volume));
-
-      u32 combined = k_ent_route_flag_achieve_gold | 
-                     k_ent_route_flag_achieve_silver;
+      ent_volume *volume = af_arritm(&world->ent_volume,mdl_entity_id_id(region->zone_volume));
+      u32 combined = k_ent_route_flag_achieve_gold | k_ent_route_flag_achieve_silver;
 
-      for( u32 j=0; j<af_arrcount(&world->ent_route); j ++ ){
+      for( u32 j=0; j<af_arrcount(&world->ent_route); j ++ )
+      {
          ent_route *route = af_arritm(&world->ent_route, j );
 
          v3f local;
          m4x3_mulv( volume->to_local, route->board_transform[3], local );
          if( !((fabsf(local[0]) <= 1.0f) &&
                (fabsf(local[1]) <= 1.0f) &&
-               (fabsf(local[2]) <= 1.0f)) ){
+               (fabsf(local[2]) <= 1.0f)) )
+         {
             continue;
          }
 
          combined &= route->flags;
       }
 
-      for( u32 j=0; j<af_arrcount(&world->ent_challenge); j ++ ){
+      for( u32 j=0; j<af_arrcount(&world->ent_challenge); j ++ )
+      {
          ent_challenge *challenge = af_arritm( &world->ent_challenge, j );
 
+         if( challenge->flags & k_ent_challenge_is_story )
+            continue;
+
          v3f local;
          m4x3_mulv( volume->to_local, challenge->transform.co, local );
          if( !((fabsf(local[0]) <= 1.0f) &&
                (fabsf(local[1]) <= 1.0f) &&
-               (fabsf(local[2]) <= 1.0f)) ){
+               (fabsf(local[2]) <= 1.0f)) )
+         {
             continue;
          }
 
          u32 flags = 0x00;
-         if( challenge->status ){
+         if( challenge->status )
+         {
             flags |= k_ent_route_flag_achieve_gold;
             flags |= k_ent_route_flag_achieve_silver;
          }
@@ -130,10 +135,13 @@ void ent_region_re_eval( world_instance *world )
       world_total &= combined;
 
       /* run unlock triggers. v105+ */
-      if( world->meta.version >= 105 ){
-         if( region->flags & (k_ent_route_flag_achieve_gold|
-                              k_ent_route_flag_achieve_silver) ){
-            if( region->target0[0] ){
+      if( world->meta.version >= 105 )
+      {
+         if( region->flags & (k_ent_route_flag_achieve_gold|k_ent_route_flag_achieve_silver) )
+         {
+            if( region->target0[0] )
+            {
+               vg_info( "Trigger region unlock -> %u\n", region->target0[0] );
                ent_call call;
                call.data = NULL;
                call.id = region->target0[0];
diff --git a/src/ent_script.c b/src/ent_script.c
new file mode 100644 (file)
index 0000000..b63bad4
--- /dev/null
@@ -0,0 +1,118 @@
+#include "ent_script.h"
+
+struct
+{
+   void **userdata_array;
+}
+_ent_script;
+
+static void ent_script_event_init( ent_script_event *event, world_instance *world, u32 script_index )
+{
+   event->userdata = _ent_script.userdata_array[ script_index ];
+   event->world = world;
+
+   ent_script *script = af_arritm( &world->ent_script, script_index );
+   if( script->entity_list_id )
+      event->entity_list = af_arritm( &world->ent_list, mdl_entity_id_id( script->entity_list_id ) );
+   else
+      event->entity_list = NULL;
+
+   event->script_alias = _ent_script_table[ script->script_id ].alias;
+}
+
+void ent_script_alloc( world_instance *world, void *heap )
+{
+   _ent_script.userdata_array = vg_linear_alloc( heap, af_arrcount( &world->ent_script ) * sizeof(void*) );
+
+   struct script_event_allocate info;
+   info.heap = heap;
+
+   struct ent_script_event event;
+   event.type = k_escript_event_allocate;
+   event.info = &info;
+
+   for( u32 i=0; i<af_arrcount( &world->ent_script ); i ++ )
+   {
+      ent_script *script = af_arritm( &world->ent_script, i );
+      const char *script_name = af_str( &world->meta.af, script->pstr_script_name );
+
+      for( u32 j=0; 1; j ++ )
+      {
+         struct ent_script_table_entry *table_entry = &_ent_script_table[ j ];
+
+         if( table_entry->alias == NULL )
+            break;
+
+         if( vg_str_eq( script_name, table_entry->alias ) )
+         {
+            script->script_id = j;
+            script->flags |= k_ent_script_flag_linked;
+            break;
+         }
+      }
+
+      info.userdata = NULL;
+      if( script->flags & k_ent_script_flag_linked )
+      {
+         ent_script_event_init( &event, world, i );
+         _ent_script_table[ script->script_id ].jump( &event );
+      }
+      else
+         vg_error( "Unable to link script file '%s', entity id: %u\n", script_name, mdl_entity_id( k_ent_script, i ) );
+      _ent_script.userdata_array[i] = info.userdata;
+   }
+}
+
+void ent_script_propogate_event( world_instance *world, struct ent_script_event *event )
+{
+   for( u32 i=0; i<af_arrcount( &world->ent_script ); i ++ )
+   {
+      ent_script *script = af_arritm( &world->ent_script, i );
+
+      if( script->flags & k_ent_script_flag_linked )
+      {
+         ent_script_event_init( event, world, i );
+         _ent_script_table[ script->script_id ].jump( event );
+      }
+   }
+}
+
+void ent_script_update( world_instance *world )
+{
+   struct ent_script_event event;
+   event.type = k_escript_event_update;
+   event.info = NULL;
+   ent_script_propogate_event( world, &event );
+}
+
+void ent_script_start( world_instance *world )
+{
+   struct ent_script_event event;
+   event.type = k_escript_event_world_start;
+   event.info = NULL;
+   ent_script_propogate_event( world, &event );
+}
+
+entity_call_result ent_script_call( world_instance *world, ent_call *call )
+{
+   u32 index = mdl_entity_id_id( call->id );
+   ent_script *script = af_arritm( &world->ent_script, index );
+
+   if( script->flags & k_ent_script_flag_linked )
+   {
+      struct script_event_call info;
+      info.function_id = call->function;
+
+      struct ent_script_event event;
+      ent_script_event_init( &event, world, index );
+      event.type = k_escript_event_call;
+      event.info = &info;
+      _ent_script_table[ script->script_id ].jump( &event );
+      return k_entity_call_result_OK;
+   }
+   else
+   {
+      vg_error( "Call to script '%u', function '%d', but the script is not linked.\n", index, call->function );
+      return k_entity_call_result_OK;
+   }
+}
diff --git a/src/ent_script.h b/src/ent_script.h
new file mode 100644 (file)
index 0000000..a1f5521
--- /dev/null
@@ -0,0 +1,56 @@
+#pragma once
+
+struct script_event_allocate
+{
+   void *heap;
+   void *userdata;
+};
+
+struct script_event_call
+{
+   i32 function_id;
+};
+
+struct script_event_nugget_changed
+{
+   const char *alias;
+   u64 value;
+};
+
+struct script_event_region_completion_changed
+{
+   ent_region *region;
+};
+
+typedef struct ent_script_event ent_script_event;
+struct ent_script_event
+{
+   enum escript_event
+   {
+      k_escript_event_call = 0,
+      k_escript_event_allocate,
+      k_escript_event_update,
+      k_escript_event_world_start,
+      k_escript_event_nugget_changed,
+   }
+   type;
+   void *info;
+   
+   void *userdata;
+   ent_list *entity_list;
+   world_instance *world;
+   const char *script_alias;
+};
+
+struct ent_script_table_entry
+{
+   const char *alias;
+   bool( *jump )( struct ent_script_event *event );
+}
+extern _ent_script_table[];
+
+void ent_script_propogate_event( world_instance *world, struct ent_script_event *event );
+void ent_script_start( world_instance *world );
+void ent_script_update( world_instance *world );
+void ent_script_alloc( world_instance *world, void *heap );
+entity_call_result ent_script_call( world_instance *world, ent_call *call );
index 14d5665a2e177e11f15aa055d723f2490a26cc5b..449f0507f0ee15a86005391c6fc17a33bab85786 100644 (file)
@@ -37,6 +37,7 @@ void entity_call( world_instance *world, ent_call *call )
       [k_ent_region]    = ent_region_call,
       [k_ent_glider]    = ent_glider_call,
       [k_ent_water]     = ent_water_call,
+      [k_ent_script]    = ent_script_call,
    };
 
    if( type >= VG_ARRAY_LEN(table) ){
index 28c0441eb2b63e47570b8eb72850f115b0b73e44..84bb832b91efee26ba83d8218bee80c255168a3d 100644 (file)
@@ -38,6 +38,7 @@ typedef struct ent_list ent_list;
 typedef struct file_entity_ref file_entity_ref;
 typedef struct ent_glider ent_glider;
 typedef struct ent_npc ent_npc;
+typedef struct ent_script ent_script;
 
 enum entity_alias{
    k_ent_none        = 0,
@@ -69,6 +70,7 @@ enum entity_alias{
    k_ent_glider      = 26,
    k_ent_npc         = 27,
    k_ent_armature    = 28,
+   k_ent_script      = 29,
    k_ent_max
 };
 
@@ -101,7 +103,8 @@ const char *_entity_alias_str[] =
    [k_ent_region] = "ent_region",
    [k_ent_glider] = "ent_glider",
    [k_ent_npc] = "ent_npc",
-   [k_ent_armature] = "mdl_armature"
+   [k_ent_armature] = "mdl_armature",
+   [k_ent_script] = "ent_script"
 };
 
 typedef struct ent_call ent_call;
@@ -564,6 +567,23 @@ struct ent_ccmd{
    u32 pstr_command;
 };
 
+enum ent_script_flag
+{
+   k_ent_script_flag_linked = 0x1
+};
+
+struct ent_script
+{
+   union
+   {
+      u32 pstr_script_name,  /* When its in the file */
+          script_id;         /* When its runtime */
+   };
+
+   u32 entity_list_id;
+   u32 flags;
+};
+
 enum ent_objective_filter{
    k_ent_objective_filter_none            = 0x00000000,
    k_ent_objective_filter_trick_shuvit    = 0x00000001,
index 94deb160e304b0e1d45a57668c8302ffcd735000..ffba128efb9f6cbe058ee6f7edbd437c0b81e00e 100644 (file)
@@ -379,7 +379,7 @@ void menu_update_world_list(void)
    if( world_map.superworld_list_selected == k_superworld_infinite ) world_map.selected_superworld_locked = 1;
    if( world_map.superworld_list_selected == k_superworld_venus_moon )
    {
-      world_map.selected_superworld_locked = !_skaterift_script_check_unlocked( k_escript_script_id_ch4s3 );
+      world_map.selected_superworld_locked = !_skaterift_script_nugget_status( "ch4s3" );
    }
 
    if( world_map.selected_superworld_locked )
@@ -418,11 +418,11 @@ void menu_update_world_list(void)
          bool unlocked = 1;
 
          if( reg->flags & ADDON_REG_MTZERO )
-            unlocked = _skaterift_script_check_unlocked( k_escript_script_id_ch2s1 );
+            unlocked = _skaterift_script_nugget_status( "ch2s1" )?1:0;
          if( reg->flags & ADDON_REG_CITY )
-            unlocked = _skaterift_script_check_unlocked( k_escript_script_id_ch3s1 );
+            unlocked = _skaterift_script_nugget_status( "ch3s1" )?1:0;
          if( reg->flags & ADDON_REG_VALLEY )
-            unlocked = _skaterift_script_check_unlocked( k_escript_script_id_ch4s1a );
+            unlocked = _skaterift_script_nugget_status( "ch4s1a" )?1:0;
 
          menu.world_list_entries[ menu.world_list_display_count ] = unlocked? reg: NULL;
 
index ca8f2f8330b04560caa4517cb88d5d0ce163d521..1812db44a155c486e9e8001cc348b46f891da93a 100644 (file)
@@ -70,9 +70,10 @@ void _cutscene_play(void)
    vg_audio_unlock();
 }
 
-void _cutscene_subtitle( const char *text )
+void _cutscene_set_subtitle_list( const cs_subtitle *subtitles )
 {
-   _cutscene.subtitle = text;
+   _cutscene.subtitle_list = subtitles;
+   _cutscene.subtitle_index = 0;
 }
 
 void _cutscene_unload(void)
@@ -89,7 +90,9 @@ void _cutscene_unload(void)
    _cutscene.active_samplers = 0;
    _cutscene.state = k_cutscene_state_unloading;
    _cutscene.player_binding = NULL;
-   _cutscene_subtitle( NULL );
+   _cutscene.subtitle = NULL;
+   _cutscene.subtitle_list = NULL;
+   _cutscene.subtitle_index = 0;
 }
 
 /*
@@ -500,12 +503,15 @@ void cutscene_update( f32 delta )
       vg_allocator_free( _cutscene.arena );
       _cutscene.arena = NULL;
       _cutscene.state = k_cutscene_state_none;
+      _cutscene.marker_this_frame = NULL;
+      _cutscene.subtitle = NULL;
       return;
    }
 
    if( _cutscene.state != k_cutscene_state_playing )
       return; 
 
+   _cutscene.marker_this_frame = NULL;
    _cutscene.time += delta;
    u32 frame = _cutscene.time * _cutscene.meta.info.framerate;
 
@@ -567,8 +573,32 @@ void cutscene_update( f32 delta )
          //   _cutscene.active_camera = NULL;
 
          const char *marker = af_str( &_cutscene.meta.af, strip->pstr_name );
-         _skaterift_script_marker( marker );
          vg_info( "Cutscene marker: %s\n", marker );
+
+         bool absorbed = 0;
+
+         if( _cutscene.subtitle_list )
+         {
+            const cs_subtitle *next = &_cutscene.subtitle_list[ _cutscene.subtitle_index ];
+
+            if( next->key )
+            {
+               if( vg_str_eq( marker, next->key ) )
+               {
+                  _cutscene.subtitle = next->value;
+                  _cutscene.subtitle_index ++;
+                  absorbed = 1;
+               }
+               else if( vg_str_eq( marker, "$clear_subtitles" ) )
+               {
+                  _cutscene.subtitle = NULL;
+                  absorbed = 1;
+               }
+            }
+         }
+
+         if( !absorbed )
+            _cutscene.marker_this_frame = marker;
       }
       else
       {
index fedc1529ec692441443809cfa42833dbe6b28e99..87d45fc098c73aad2ff9c2a48f0114a475998dea 100644 (file)
@@ -13,6 +13,7 @@ typedef struct ms_strip ms_strip;
 typedef struct ms_track ms_track;
 typedef struct ms_curve_keyframe ms_curve_keyframe;
 typedef struct mdl_transform ms_keyframe;
+typedef struct cs_subtitle cs_subtitle;
 
 struct ms_scene_info
 {
@@ -157,8 +158,14 @@ struct _cutscene
    u32 strip;
    f32 time;
 
-   char subtitle_buf[128];
+   const char *marker_this_frame;
    const char *subtitle;
+   struct cs_subtitle
+   {
+      const char *key, *value;
+   }
+   const *subtitle_list;
+   u32 subtitle_index;
 }
 extern _cutscene;
 
@@ -170,5 +177,6 @@ void _cutscene_unload(void);
 void cutscene_update( f32 delta );
 ent_camera *_cutscene_active_camera(void);
 void _cutscene_gui( ui_context *ctx );
+void _cutscene_set_subtitle_list( const cs_subtitle *subtitles );
 
 struct cs_instance *_cutscene_get_first_model_instance( const char *mdl_name );
index 4b4f125938312e3368ab89c770e7773ba610b13b..d1cc3a8678f675f5ef5db018015c0a5ee62a17f9 100644 (file)
@@ -116,7 +116,7 @@ static void skaterift_populate_main_savedata( savedata_file *file )
    /* script / story information */
    vg_msg_frame( &sav, "story" );
    {
-      skaterift_script_write_savedata( &sav );
+      _skaterift_script_write_savedata( &sav );
    }
    vg_msg_end_frame( &sav );
 
@@ -256,21 +256,18 @@ void skaterift_load_mainsave(void)
       }
    }
 
-   localplayer.board_view_slot = 
-      addon_cache_create_viewer( k_addon_type_board, board_reg_id );
-   localplayer.playermodel_view_slot = 
-      addon_cache_create_viewer( k_addon_type_player, player_reg_id );
+   localplayer.board_view_slot = addon_cache_create_viewer( k_addon_type_board, board_reg_id );
+   localplayer.playermodel_view_slot = addon_cache_create_viewer( k_addon_type_player, player_reg_id );
 
    kvsav.cur = orig;
 
    if( vg_msg_seekframe( &kvsav, "story" ) )
    {
-      skaterift_script_load_savedata( &kvsav );
+      _skaterift_script_load_savedata( &kvsav );
    }
    else
    {
       vg_info( "Starting new story!\n" );
       _world.loader_reg = addon_mount_local_addon( "maps/dev_heaven", k_addon_type_world, ".mdl" );
-      _skaterift_script_hook( 2, (const char *[]){ "unlock", "intro" } );
    }
 }
diff --git a/src/scripts/heaven.c b/src/scripts/heaven.c
new file mode 100644 (file)
index 0000000..ced45be
--- /dev/null
@@ -0,0 +1,29 @@
+static bool _skaterift_script_intro( ent_script_event *event )
+{
+   static const cs_subtitle EN[] = {
+   { "a1", KCOL_JESUS "Aww hello" },
+   { "a2", KCOL_JESUS "welcome to heaven." },
+   { "a3", KCOL_JESUS "Not quite your time yet," },
+   { "a4", KCOL_JESUS "but if you wanna take a quick look around" },
+   { "a5", KCOL_JESUS "then feel free to do so." },
+   { "a6", KCOL_JESUS "We've got a great grift shop on the way out!" },
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/intro.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   enum generic_cutscene_event cs_event = optional_video_wrapper( &cutscene, "intro_view", event );
+
+   u64 viewed;
+   if( on_nugget_changed( event, "intro_view", &viewed ) )
+   {
+      ent_gate *gate = af_arritm( &event->world->ent_gate, 0 );
+      if( viewed ) gate->flags &= ~((u32)k_ent_gate_locked);
+      else         gate->flags |=   (u32)k_ent_gate_locked;
+   }
+
+   return 1;
+}
diff --git a/src/scripts/hub.c b/src/scripts/hub.c
new file mode 100644 (file)
index 0000000..3ff8965
--- /dev/null
@@ -0,0 +1,78 @@
+static bool _skaterift_script_hub( ent_script_event *event )
+{
+   if( on_function_trigger( event, 0 ) )
+   {
+      if( on_nugget_once( event, "hub_info_view" ) )
+      {
+         menu_open( k_menu_page_impromptu_guide );
+      }
+   }
+   else if( event->type == k_escript_event_nugget_changed )
+   {
+      world_instance *world = &_world.main;
+
+      bool unlock_mtzero = _skaterift_script_nugget_status( "unlock_mtzero" ),
+           unlock_city   = _skaterift_script_nugget_status( "unlock_city" ),
+           unlock_valley = _skaterift_script_nugget_status( "unlock_valley" );
+
+      for( u32 i=0; i<af_arrcount( &world->ent_prop ); i ++ )
+      {
+         ent_prop *prop = af_arritm( &world->ent_prop, i );
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "MARC" ) )
+            if( skaterift.achievements & 0x1 )
+               prop->flags &= ~0x1;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "ALBERT" ) )
+            if( skaterift.achievements & 0x2 )
+               prop->flags &= ~0x1;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "JANET" ) )
+            if( skaterift.achievements & 0x4 )
+               prop->flags &= ~0x1;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "BERNADETTA" ) )
+            if( skaterift.achievements & 0x8 )
+               prop->flags &= ~0x1;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "main_island_block" ) )
+            if( unlock_mtzero )
+               prop->flags |= k_prop_flag_hidden;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "mtzero_block" ) )
+            if( unlock_mtzero )
+               prop->flags |= k_prop_flag_hidden;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "city_block" ) )
+            if( unlock_city )
+               prop->flags |= k_prop_flag_hidden;
+
+         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "valley_block" ) )
+            if( unlock_valley )
+               prop->flags |= k_prop_flag_hidden;
+      }
+
+      u32 spawns_allowed = 0;
+      if( unlock_mtzero ) spawns_allowed |= k_ent_spawn_flag_group_1;
+      if( unlock_city ) spawns_allowed |= k_ent_spawn_flag_group_2;
+      if( unlock_valley ) spawns_allowed |= k_ent_spawn_flag_group_3;
+
+      for( u32 i=0; i<af_arrcount(&world->ent_spawn); i++ )
+      {
+         ent_spawn *spawn = af_arritm( &world->ent_spawn, i );
+
+         bool allow = 0;
+
+         if( spawn->flags == 0 ) allow = 1;
+         if( spawn->flags & spawns_allowed ) allow = 1;
+
+         if( allow ) spawn->flags &= ~(u32)k_ent_spawn_flag_locked;
+         else        spawn->flags |= k_ent_spawn_flag_locked;
+      }
+
+      return 1;
+   }
+
+   return 0;
+}
+
diff --git a/src/scripts/tutorial_island.c b/src/scripts/tutorial_island.c
new file mode 100644 (file)
index 0000000..788000c
--- /dev/null
@@ -0,0 +1,200 @@
+static bool _skaterift_script_tutorial_island( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "john_1", KCOL_JOHN "Well, here we are. Home." },
+   { "john_2", KCOL_JOHN "I mean, halfway home." },
+   { "john_3", KCOL_JOHN "Obviously you've forgotten quite a bit, so we'll stop off here" },
+   { "john_4", KCOL_JOHN "and I'll teach you some stuff again" },
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s2.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   generic_cutscene_wrapper( &cutscene, event );
+
+   if( on_nugget_once( event, "ch1s2_view" ) )
+   {
+      play_generic_cutscene( event );
+   }
+
+   return 1;
+}
+
+static bool _skaterift_script_unlock_docks( ent_script_event *event )
+{
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/unlock_docks.ms",
+      .freeze_player = 1,
+   };
+   generic_cutscene_wrapper( &cutscene, event );
+
+   if( on_function_trigger( event, 1 ) )
+   {
+      vg_error( "Show required objectives here\n" );
+   }
+
+   if( on_function_trigger( event, 2 ) )
+   {
+      if( on_nugget_once( event, "unlock_docks_view" ) )
+      {
+         play_generic_cutscene( event );
+      }
+      else
+      {
+         ent_list_set_visible( event->world, event->entity_list, 0 );
+      }
+   }
+
+   if( on_cutscene_marker( event, "$break" ) )
+   {
+      ent_list_set_visible( event->world, event->entity_list, 0 );
+   }
+
+   return 1;
+}
+
+static bool _skaterift_script_ch1s3( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "john_1", KCOL_JOHN "Alright so, fliptricks." },
+   { "john_2", KCOL_JOHN "We spent ages practicing these before" },
+   { "john_3", KCOL_JOHN "Shouldn't take you long to get it again" },
+   { "john_4", KCOL_JOHN "see if you can get a tre-flip down there" },
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s3.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   challenge_video_wrapper( &cutscene, "ch1s3_view", event );
+   return 1;
+}
+
+static bool _skaterift_script_ch1s3b( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "john_1", KCOL_JOHN "That is it mate!" },
+   { "john_2", KCOL_JOHN "You have still got it!" },
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s3b.ms",
+      .freeze_player = 0,
+      .subtitles = EN,
+   };
+   challenge_video_wrapper( &cutscene, "ch1s3b_view", event );
+
+   u64 viewed;
+   if( on_nugget_changed( event, "ch1s3b_view", &viewed ) )
+      ent_list_set_visible( event->world, event->entity_list, !viewed );
+
+   return 1;
+}
+
+static bool _skaterift_script_ch1s4( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "john_1", KCOL_JOHN "Remember these courses we were setting up?" },
+   { "john_2", KCOL_JOHN "Nah?" },
+   { "john_3", KCOL_JOHN "Alright well uh, to jog your memory.." },
+   { "john_4", KCOL_JOHN "Get yourself down through the gates as quick as possible" },
+   { "john_5", KCOL_JOHN "Thats it." },
+   { "john_6", KCOL_JOHN "Give it a shot mate" },
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s4.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   optional_video_wrapper( &cutscene, "ch1s4_view", event );
+   return 1;
+}
+
+static bool _skaterift_script_ch1s5( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "j1", KCOL_JOHN "Alright, well then" },
+   { "j2", KCOL_JOHN "You're gonna need to play close attention to this part" },
+   { "j3", KCOL_JOHN "because its difficult.." },
+   { "j4", KCOL_JOHN "It's gonna take some practice until it clicks" },
+   { "j5", KCOL_JOHN "Right as you like, go across the transition of the ramp," },
+   { "j6", KCOL_JOHN "Right here," },
+   { "j7", KCOL_JOHN "you need to pump to gain some momentum." },
+
+   { "j8", KCOL_JOHN "What I mean right, watch" },
+   { "j9", KCOL_JOHN "just as I'm going into the base of the ramp" },
+   { "j10",KCOL_JOHN "I'm storing up some energy here by crouching down" },
+   { "j11",KCOL_JOHN "Right as I go across this point" },
+   { "j12",KCOL_JOHN "I'm almost jumping back up, adding some uwpwards momentum" },
+
+   { "j13",KCOL_JOHN "Then as the board comes up to this angle.." },
+   { "j14",KCOL_JOHN "that upwards momentum is transferred into my speed" },
+
+   { "j15",KCOL_JOHN "Same principle works, same way in the other direction" },
+
+   { "j16",KCOL_JOHN "Now, like I'm saying" },
+   { "j17",KCOL_JOHN "this might take you a little bit until it clicks" },
+
+   { "j18",KCOL_JOHN "But once it does you'll feel it. You'll know!" },
+
+   { "j19",KCOL_JOHN "And I uhh, set a target for you" },
+   { "j20",KCOL_JOHN "right up there.." },
+   { "j21",KCOL_JOHN "Thats how we'll know you're back on form." },
+
+   { "j22",KCOL_JOHN "Come see me at the docks once you've got it." },
+
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s5.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   challenge_video_wrapper( &cutscene, "ch1s5_view", event );
+   return 1;
+}
+
+static bool _skaterift_script_ch1s6a( ent_script_event *event )
+{
+   static const struct cs_subtitle EN[] = {
+   { "j1", KCOL_JOHN "Eyyy! Looks like you're ready again.." },
+   { "j2", KCOL_JOHN "Didn't take long" },
+   { "j3", KCOL_JOHN "Just a short ferry ride back over to Mt.Zero now." },
+   { "j4", KCOL_JOHN "Oh right, Mt.Zero.. You really don't remember anything do you?" },
+   { "j5", KCOL_JOHN "Its where we live." },
+   { "j7", KCOL_JOHN "Yknow it's where the woodshop is," },
+   { "j8", KCOL_JOHN "It's where we skate, where Mike is," },
+   { "j9", KCOL_JOHN "yknow, its our home!" },
+
+   { NULL, NULL },
+   };
+   static const struct generic_cutscene cutscene = 
+   {
+      .metascene_path = "metascenes/ch1s6a.ms",
+      .freeze_player = 1,
+      .subtitles = EN,
+   };
+   enum generic_cutscene_event cs_event = optional_video_wrapper( &cutscene, "ch1s6a_view", event );
+
+   if( cs_event == k_generic_cutscene_event_start )
+   {
+      _skaterift_script_nugget_set( "unlock_mtzero", 1 );
+   }
+
+   if( cs_event == k_generic_cutscene_event_end )
+   {
+      /* move level to the hub */
+   }
+
+   return 1;
+}
index 6c16d86d752c675f94186721f0eee864586f1cf7..abe5cd060f854b85c4b39f4b27cf6f847a40f374 100644 (file)
@@ -52,6 +52,7 @@
 #include "client.h"
 #include "skaterift_script.h"
 #include "ent_challenge.h"
+#include "ent_script.h"
 
 struct skaterift_globals skaterift = 
 { 
@@ -169,7 +170,7 @@ void vg_pre_update(void)
       return;
 
    //draw_origin_axis();
-   _skaterift_script_update();
+   //_skaterift_script_update();
    addon_system_pre_update();
    skateshop_world_preview_preupdate();
    network_update();
@@ -562,6 +563,7 @@ void vg_framebuffer_resize( int w, int h )
 #include "ent_tornado.c"
 #include "ent_traffic.c"
 #include "ent_prop.c"
+#include "ent_script.c"
 #include "freecam.c"
 #include "menu.c"
 #include "network.c"
index 920989d88efb61720e03910578ace8c63252999c..603953c02503f1dda4f82f40d6866751a68777b7 100644 (file)
-/* 
- * One day in the future, all will be well with the abstraction gods, and these
- * scripts will run in an interpreter just like a real scripting language. That
- * of course assumes I believe in the abstraction gods, but I am not a religious
- * man.
- */
-
 #define KCOL_JOHN KRED
 #define KCOL_MIKE KBLU
 #define KCOL_PRES KYEL
 #define KCOL_FBI  KGRN
 #define KCOL_JESUS KMAG
 
-#include "entity.h"
-#include "ent_challenge.h"
-
-/* you can add anything you want to this. */
-enum escript_state
-{
-   k_escript_state_none,
-   k_escript_state_loading,
-   k_escript_state_initializing,
-   k_escript_state_playing,
-   k_escript_state_end
-};
-
-extern m4x3f *_TEMP_VAR;
-
-static bool _skaterift_script_bind_player(void)
-{
-   _cutscene.player_binding = 
-      _cutscene_get_first_model_instance( "models/ch_none" );
-
-   if( !_cutscene.player_binding )
-   {
-      vg_error( "Failed to find models/ch_none in scene!" );
-      return 0;
-   }
-
-   _cutscene.player_binding->disable_render = 1;
-   return 1;
-}
-
-struct sr_subtitle
-{
-   const char *k, *v;
-};
-
-static void _skaterift_dialogue( struct sr_subtitle *subtitles, u32 *index, const char *inf )
-{
-   u32 i = *index;
-
-   if( subtitles[i].k && vg_str_eq( inf, subtitles[ i ].k ) )
-      _cutscene_subtitle( subtitles[ i ++ ].v );
-
-   else if( vg_str_eq( inf, "clear_subs" ) )
-      _cutscene_subtitle( NULL );
-
-   *index = i;
-}
-
-static bool _skaterift_generic_script_template( 
-      enum escript_event ev,
-      const char *inf,
-
-      const char *metascene_path,
-      struct sr_subtitle subtitles[],
-      bool freeze_player )
-{
-   static u32 state, subtitle_id;
-
-   if( ev == k_escript_event_call )
-   {
-      state = k_escript_state_loading;
-      subtitle_id = 0;
-      vg_info( "generic_template:state = loading\n" );
-      
-      if( freeze_player )
-         localplayer.immobile = 1;
-   }
-
-   if( ev == k_escript_event_cutscene_marker )
-   {
-      _skaterift_dialogue( subtitles, &subtitle_id, inf );
-      return 0;
-   }
-
-   /* scene 
-    * ---------------------------------------------------------------  */
-
-   if( state == k_escript_state_loading )
-   {
-      if( cmd_cutscene_load( 1, (const char *[]){ metascene_path } ) )
-      {
-         state = k_escript_state_initializing;
-         vg_info( "generic_template:state = initializing\n" );
-      }
-   }
-
-   if( state == k_escript_state_initializing )
-   {
-      if( _cutscene.state == k_cutscene_state_ready )
-      {
-         _skaterift_script_bind_player();
-         state = k_escript_state_playing;
-         vg_info( "generic_template:state = playing\n" );
-         _cutscene_play();
-      }
-   }
-
-   if( state == k_escript_state_playing )
-   {
-      if( _cutscene.state == k_cutscene_state_done )
-      {
-         state = k_escript_state_end;
-         vg_info( "generic_template:state = end\n" );
-         _cutscene_unload();
-
-         if( freeze_player )
-            localplayer.immobile = 0;
-
-         return 1;
-      }
-   }
-
-   return 0;
-}
-
-static bool _skaterift_script_intro( enum escript_event ev, const char *inf )
-{
-   struct sr_subtitle EN[] = {
-   { "a1", KCOL_JESUS "Aww hello" },
-   { "a2", KCOL_JESUS "welcome to heaven." },
-   { "a3", KCOL_JESUS "Not quite your time yet," },
-   { "a4", KCOL_JESUS "but if you wanna take a quick look around" },
-   { "a5", KCOL_JESUS "then feel free to do so." },
-   { "a6", KCOL_JESUS "We've got a great grift shop on the way out!" },
-   { NULL, NULL },
-   };
-
-   return _skaterift_generic_script_template( ev, inf, "metascenes/intro.ms", EN, 1 );
-}
-
-
-static bool _skaterift_script_ch1s2( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-   {
-      if( _skaterift_script_already_viewed( k_escript_script_id_ch1s2 ) )
-         return 1;
-   }
-
-   struct sr_subtitle EN[] = {
-   { "john_1", KCOL_JOHN "Well, here we are. Home." },
-   { "john_2", KCOL_JOHN "I mean, halfway home." },
-   { "john_3", KCOL_JOHN "Obviously you've forgotten quite a bit, so we'll stop off here" },
-   { "john_4", KCOL_JOHN "and I'll teach you some stuff again" },
-   { NULL, NULL },
-   };
-   return _skaterift_generic_script_template( ev, inf, "metascenes/ch1s2.ms", EN, 1 );
-}
-
-static bool _skaterift_script_ch1s3( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-      if( _skaterift_script_already_viewed( k_escript_script_id_ch1s3 ) )
-         return 1;
-
-   struct sr_subtitle EN[] = {
-   { "john_1", KCOL_JOHN "Alright so, fliptricks." },
-   { "john_2", KCOL_JOHN "We spent ages practicing these before" },
-   { "john_3", KCOL_JOHN "Shouldn't take you long to get it again" },
-   { "john_4", KCOL_JOHN "see if you can get a tre-flip down there" },
-   { NULL, NULL },
-   };
-   return _skaterift_generic_script_template( ev, inf, "metascenes/ch1s3.ms", EN, 1 );
-}
-
-static bool _skaterift_script_ch1s3b( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-      if( _skaterift_script_already_viewed( k_escript_script_id_ch1s3b ) )
-         return 1;
-
-   struct sr_subtitle EN[] = {
-   { "john_1", KCOL_JOHN "That is it mate!" },
-   { "john_2", KCOL_JOHN "You have still got it!" },
-   { NULL, NULL },
-   };
-   return _skaterift_generic_script_template( ev, inf, "metascenes/ch1s3b.ms", EN, 0 );
-}
-
-static bool _skaterift_script_ch1s4( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-      if( _skaterift_script_already_viewed( k_escript_script_id_ch1s4 ) )
-         return 1;
-
-   struct sr_subtitle EN[] = {
-   { "john_1", KCOL_JOHN "Remember these courses we were setting up?" },
-   { "john_2", KCOL_JOHN "Nah?" },
-   { "john_3", KCOL_JOHN "Alright well uh, to jog your memory.." },
-   { "john_4", KCOL_JOHN "Get yourself down through the gates as quick as possible" },
-   { "john_5", KCOL_JOHN "Thats it." },
-   { "john_6", KCOL_JOHN "Give it a shot mate" },
-   { NULL, NULL },
-   };
-   return _skaterift_generic_script_template( ev, inf, "metascenes/ch1s4.ms", EN, 1 );
-}
-
-static bool _skaterift_script_ch1s5( enum escript_event ev, const char *inf )
-{
-   static u32 state, subtitle_id;
-   static struct cs_instance *override_inst;
-
-   if( ev == k_escript_event_call )
-   {
-      state = k_escript_state_loading;
-      override_inst = NULL;
-      subtitle_id = 0;
-      vg_info( "test:state = loading\n" );
-   }
-
-   if( ev == k_escript_event_cutscene_marker )
-   {
-      struct sr_subtitle EN[] = {
-      { "j1", KCOL_JOHN "Alright, well then" },
-      { "j2", KCOL_JOHN "You're gonna need to play close attention to this part" },
-      { "j3", KCOL_JOHN "because its difficult.." },
-      { "j4", KCOL_JOHN "It's gonna take some practice until it clicks" },
-      { "j5", KCOL_JOHN "Right as you like, go across the transition of the ramp," },
-      { "j6", KCOL_JOHN "Right here," },
-      { "j7", KCOL_JOHN "you need to pump to gain some momentum." },
-
-      { "j8", KCOL_JOHN "What I mean right, watch" },
-      { "j9", KCOL_JOHN "just as I'm going into the base of the ramp" },
-      { "j10",KCOL_JOHN "I'm storing up some energy here by crouching down" },
-      { "j11",KCOL_JOHN "Right as I go across this point" },
-      { "j12",KCOL_JOHN "I'm almost jumping back up, adding some uwpwards momentum" },
-
-      { "j13",KCOL_JOHN "Then as the board comes up to this angle.." },
-      { "j14",KCOL_JOHN "that upwards momentum is transferred into my speed" },
-
-      { "j15",KCOL_JOHN "Same principle works, same way in the other direction" },
-
-      { "j16",KCOL_JOHN "Now, like I'm saying" },
-      { "j17",KCOL_JOHN "this might take you a little bit until it clicks" },
+#include "ent_script.h"
 
-      { "j18",KCOL_JOHN "But once it does you'll feel it. You'll know!" },
 
-      { "j19",KCOL_JOHN "And I uhh, set a target for you" },
-      { "j20",KCOL_JOHN "right up there.." },
-      { "j21",KCOL_JOHN "Thats how we'll know you're back on form." },
 
-      { "j22",KCOL_JOHN "Come see me at the docks once you've got it." },
+#if 0
 
-      { NULL, NULL },
-      };
-      _skaterift_dialogue( EN, &subtitle_id, inf );
-
-      return 0;
-   }
-
-   /* scene 
-    * ---------------------------------------------------------------  */
-
-   if( state == k_escript_state_loading )
-   {
-      if( cmd_cutscene_load( 1, (const char *[]){ "metascenes/ch1s5.ms" } ) )
-      {
-         state = k_escript_state_initializing;
-         vg_info( "test:state = initializing\n" );
-      }
-   }
-
-   if( state == k_escript_state_initializing )
-   {
-      if( _cutscene.state == k_cutscene_state_ready )
-      {
-         _skaterift_script_bind_player();
-         state = k_escript_state_playing;
-         vg_info( "test:state = playing\n" );
-         _cutscene_play();
-      }
-   }
-
-   if( state == k_escript_state_playing )
-   {
-      if( _cutscene.state == k_cutscene_state_done )
-      {
-         state = k_escript_state_end;
-         vg_info( "test:state = end\n" );
-         _cutscene_unload();
-         return 1;
-      }
-   }
+#include "entity.h"
+#include "ent_challenge.h"
 
-   return 0;
-}
 
-static bool _skaterift_script_ch1s6a( enum escript_event ev, const char *inf )
+static bool _skaterift_script_tutorial_setup( enum escript_event ev, const char *inf )
 {
-   static u32 state, subtitle_id;
-   static struct cs_instance *override_inst;
-
    if( ev == k_escript_event_call )
    {
-      state = k_escript_state_loading;
-      override_inst = NULL;
-      subtitle_id = 0;
-      vg_info( "test:state = loading\n" );
-   }
-
-   if( ev == k_escript_event_cutscene_marker )
-   {
-      struct sr_subtitle EN[] = {
-      { "j1", KCOL_JOHN "Eyyy! Looks like you're ready again.." },
-      { "j2", KCOL_JOHN "Didn't take long" },
-      { "j3", KCOL_JOHN "Just a short ferry ride back over to Mt.Zero now." },
-      { "j4", KCOL_JOHN "Oh right, Mt.Zero.. You really don't remember anything do you?" },
-      { "j5", KCOL_JOHN "Its where we live." },
-      { "j7", KCOL_JOHN "Yknow it's where the woodshop is," },
-      { "j8", KCOL_JOHN "It's where we skate, where Mike is," },
-      { "j9", KCOL_JOHN "yknow, its our home!" },
-
-      { NULL, NULL },
-      };
-      _skaterift_dialogue( EN, &subtitle_id, inf );
-
-      return 0;
-   }
-
-   /* scene 
-    * ---------------------------------------------------------------  */
-
-   if( state == k_escript_state_loading )
-   {
-      if( cmd_cutscene_load( 1, (const char *[]){ "metascenes/ch1s6a.ms" } ) )
-      {
-         state = k_escript_state_initializing;
-         vg_info( "test:state = initializing\n" );
-      }
-   }
+      /* TODO: Attach event handler things
+       *       update: we should switch away from the 'play' thing. And just call it 'call'
+       *               then each individial script can decide if he wants to attach himself to the frame update
+       *               or cutscene updates etc...
+       */
 
-   if( state == k_escript_state_initializing )
-   {
-      if( _cutscene.state == k_cutscene_state_ready )
+      if( !_skaterift_script_already_viewed( k_escript_script_id_ch1s2 ) )
       {
-         _skaterift_script_bind_player();
-         state = k_escript_state_playing;
-         vg_info( "test:state = playing\n" );
-         _cutscene_play();
+         _skaterift_script_hook_apply_action( k_escript_script_id_ch1s2, k_script_action_play );
+         return 0;
       }
-   }
-
-   if( state == k_escript_state_playing )
-   {
-      if( _cutscene.state == k_cutscene_state_done )
+      else
       {
-         state = k_escript_state_end;
-         vg_info( "test:state = end\n" );
-         _cutscene_unload();
          return 1;
       }
    }
@@ -389,7 +60,7 @@ static bool _skaterift_script_ch2s1( enum escript_event ev, const char *inf )
       { "j4", KCOL_JOHN "There's Mike over there" },
       { "j5", KCOL_JOHN "HELLO MIKE!" },
       { "j7", KCOL_JOHN "Yeah listen.." },
     { "j8", KCOL_JOHN "I know things are a bit weird at the moment" },
+ { "j8", KCOL_JOHN "I know things are a bit weird at the moment" },
       { "j9", KCOL_JOHN "but it'l make sense soon" },
       { "j10", KCOL_JOHN "I garuntee it" },
       { "j11", KCOL_JOHN "50%% probably garuntee it" },
@@ -1556,94 +1227,10 @@ static bool _skaterift_script_test( enum escript_event ev, const char *inf )
    return 0;
 }
 
-static bool _skaterift_script_hub_setup( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-   {
-      world_instance *world = &_world.main;
-
-      bool unlock_mtzero = _skaterift_script_check_unlocked( k_escript_script_id_ch2s1 ),
-           unlock_city   = _skaterift_script_check_unlocked( k_escript_script_id_ch3s1 ),
-           unlock_valley = _skaterift_script_check_unlocked( k_escript_script_id_ch4s1a );
-
-      for( u32 i=0; i<af_arrcount( &world->ent_prop ); i ++ )
-      {
-         ent_prop *prop = af_arritm( &world->ent_prop, i );
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "MARC" ) )
-            if( skaterift.achievements & 0x1 )
-               prop->flags &= ~0x1;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "ALBERT" ) )
-            if( skaterift.achievements & 0x2 )
-               prop->flags &= ~0x1;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "JANET" ) )
-            if( skaterift.achievements & 0x4 )
-               prop->flags &= ~0x1;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "BERNADETTA" ) )
-            if( skaterift.achievements & 0x8 )
-               prop->flags &= ~0x1;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "main_island_block" ) )
-            if( unlock_mtzero )
-               prop->flags |= k_prop_flag_hidden;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "mtzero_block" ) )
-            if( unlock_mtzero )
-               prop->flags |= k_prop_flag_hidden;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "city_block" ) )
-            if( unlock_city )
-               prop->flags |= k_prop_flag_hidden;
-
-         if( AF_STR_EQ( &world->meta.af, prop->pstr_alias, "valley_block" ) )
-            if( unlock_valley )
-               prop->flags |= k_prop_flag_hidden;
-      }
-
-      u32 spawns_allowed = 0;
-      if( unlock_mtzero ) spawns_allowed |= k_ent_spawn_flag_group_1;
-      if( unlock_city ) spawns_allowed |= k_ent_spawn_flag_group_2;
-      if( unlock_valley ) spawns_allowed |= k_ent_spawn_flag_group_3;
-
-      for( u32 i=0; i<af_arrcount(&world->ent_spawn); i++ )
-      {
-         ent_spawn *spawn = af_arritm( &world->ent_spawn, i );
-
-         bool allow = 0;
-
-         if( spawn->flags == 0 ) allow = 1;
-         if( spawn->flags & spawns_allowed ) allow = 1;
-
-         if( allow ) spawn->flags &= ~(u32)k_ent_spawn_flag_locked;
-         else        spawn->flags |= k_ent_spawn_flag_locked;
-      }
-
-      return 1;
-   }
-
-   return 0;
-}
-
-static bool _skaterift_script_hub_guide( enum escript_event ev, const char *inf )
-{
-   if( ev == k_escript_event_call )
-   {
-      if( _skaterift_script_already_viewed( k_escript_script_id_hub_guide ) )
-         return 1;
-
-      menu_open( k_menu_page_impromptu_guide );
-      return 1;
-   }
-
-   return 0;
-}
-
 struct
 {
-   enum escript_script_id script_id;
+   enum escript_script_id active_scripts[ MAX_ACTIVE_SCRIPTS ];
+   u32 active_script_count;
 }
 static _script = { .script_id = k_escript_script_id_max };
 static struct script_info _script_infos[] =
@@ -1672,6 +1259,7 @@ static struct script_info _script_infos[] =
    [k_escript_script_id_ch4s3] = { "ch4s3", _skaterift_script_ch4s3,  SCRIPT_FLAG_STORY_EVENT },
    [k_escript_script_id_hub_setup] = { "hub_setup", _skaterift_script_hub_setup },
    [k_escript_script_id_hub_guide] = { "hub_guide", _skaterift_script_hub_guide, SCRIPT_FLAG_STORY_EVENT },
+   [k_escript_script_id_tutorial_setup ] = { "tutorial_setup", _skaterift_script_tutorial_setup },
 };
 
 bool _skaterift_script_check_unlocked( enum escript_script_id id )
@@ -1698,208 +1286,468 @@ struct script_info *skaterift_script_get_info( const char *alias )
    return NULL;
 }
 
-void skaterift_script_write_savedata( vg_msg *sav )
+void _skaterift_script_update(void)
+{
+   u32 j=0;
+   for( u32 i=0; i<_script.active_script_count; i ++ )
+   {
+      enum escript_script_id script_id = _script.active_scripts[ i ];
+      if( !_script_infos[ script_id ].jump( k_escript_event_update, NULL ) )
+      {
+         _script.active_scripts[ j ++ ] = script_id;
+      }
+   }
+   _script.active_script_count = j;
+}
+
+void _skaterift_script_marker( const char *marker )
+{
+   if( _script.script_id != k_escript_script_id_max )
+   {
+      if( _script_infos[ _script.script_id ].jump( k_escript_event_cutscene_marker, marker) )
+         _script.script_id = k_escript_script_id_max;
+   }
+}
+
+void _skaterift_script_unlink_all_challenges(void)
 {
    for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+      _script_infos[i].linked_challenge = NULL;
+}
+#endif
+
+
+extern m4x3f *_TEMP_VAR;
+
+static bool _skaterift_script_bind_player(void)
+{
+   _cutscene.player_binding = _cutscene_get_first_model_instance( "models/ch_none" );
+
+   if( !_cutscene.player_binding )
    {
-      struct script_info *info = &_script_infos[i];
+      vg_error( "Failed to find models/ch_none in scene!" );
+      return 0;
+   }
 
-      if( info->flags & SCRIPT_FLAG_STORY_EVENT )
+   _cutscene.player_binding->disable_render = 1;
+   return 1;
+}
+
+static void ent_list_set_visible( world_instance *world, ent_list *list, bool visible )
+{
+   for( u32 i=0; i<list->entity_ref_count; i ++ )
+   {
+      u32 ref_index = list->entity_ref_start + i;
+      
+      file_entity_ref *ref = af_arritm( &world->file_entity_ref, ref_index );
+
+      u32 type = mdl_entity_id_type( ref->entity_id ),
+          index = mdl_entity_id_id( ref->entity_id );
+
+      if( type == k_ent_objective )
+      {
+         ent_objective *objective = af_arritm( &world->ent_objective, index );
+
+         if( visible ) objective->flags &= ~((u32)k_ent_objective_hidden);
+         else          objective->flags |= k_ent_objective_hidden;
+      }
+      else if( type == k_ent_prop )
+      {
+         ent_prop *prop = af_arritm( &world->ent_prop, index );
+
+         if( visible ) prop->flags &= ~((u32)k_prop_flag_hidden);
+         else          prop->flags |= k_prop_flag_hidden;
+      }
+      else if( type == k_ent_challenge )
+      {
+         ent_challenge *challenge = af_arritm( &world->ent_challenge, index );
+
+         if( visible ) challenge->flags &= ~((u32)k_ent_challenge_locked);
+         else          challenge->flags |=   (u32)k_ent_challenge_locked;
+      }
+      else if( type == k_ent_volume )
+      {
+         ent_volume *volume = af_arritm( &world->ent_volume, index );
+
+         if( visible ) volume->flags &= ~((u32)k_ent_volume_flag_disabled);
+         else          volume->flags |=   (u32)k_ent_volume_flag_disabled;
+      }
+   }
+}
+
+enum generic_cutscene_event generic_cutscene_wrapper( const struct generic_cutscene *cutscene_template, 
+                                                      ent_script_event *event )
+{
+   if( event->type == k_escript_event_allocate )
+   {
+      struct script_event_allocate *event_info = event->info;
+      struct generic_cutscene *generic = vg_linear_alloc( event_info->heap, sizeof(struct generic_cutscene) );
+      memcpy( generic, cutscene_template, sizeof(struct generic_cutscene) );
+      event_info->userdata = generic;
+      return k_generic_cutscene_event_none;
+   }
+   else
+   {
+      struct generic_cutscene *generic = event->userdata;
+
+      if( event->type == k_escript_event_update )
       {
-         vg_msg_frame( sav, info->alias );
+         if( generic->state == k_generic_cutscene_state_wake )
          {
-            vg_msg_wkvnum( sav, "viewed_time", k_vg_msg_u64, 1, &info->viewed_time );
+            if( generic->freeze_player )
+               localplayer.immobile = 1;
+
+            if( cmd_cutscene_load( 1, (const char *[]){ generic->metascene_path } ) )
+            {
+               generic->state = k_generic_cutscene_state_init;
+               vg_info( "generic_cutscene:state = initializing\n" );
+            }
 
-            u8 availible = info->availible;
-            vg_msg_wkvnum( sav, "availible", k_vg_msg_u8, 1, &availible );
+            if( generic->trigger_start == 0 )
+            {
+               generic->trigger_start = 1;
+               return k_generic_cutscene_event_start;
+            }
+            else
+               return k_generic_cutscene_event_none;
          }
-         vg_msg_end_frame( sav );
+
+         if( generic->state == k_generic_cutscene_state_init )
+         {
+            if( _cutscene.state == k_cutscene_state_ready )
+            {
+               if( generic->subtitles )
+               {
+                  _cutscene_set_subtitle_list( generic->subtitles );
+               }
+
+               _skaterift_script_bind_player();
+               _cutscene_play();
+               generic->state = k_generic_cutscene_state_playing;
+               vg_info( "generic_template:state = playing\n" );
+               return k_generic_cutscene_event_start;
+            }
+         }
+
+         if( generic->state == k_generic_cutscene_state_playing )
+         {
+            if( _cutscene.state == k_cutscene_state_done )
+            {
+               generic->state = k_generic_cutscene_state_end;
+
+               vg_info( "generic_template:state = end\n" );
+               _cutscene_unload();
+
+               if( generic->freeze_player )
+                  localplayer.immobile = 0;
+
+               return k_generic_cutscene_event_end;
+            }
+         }
+
+         return k_generic_cutscene_event_none;
       }
+      else
+         return k_generic_cutscene_event_none;
    }
 }
 
-void skaterift_script_load_savedata( vg_msg *sav )
+void play_generic_cutscene( ent_script_event *event )
 {
-   vg_msg_cursor orig = sav->cur;
+   struct generic_cutscene *generic = event->userdata;
+   generic->state = k_generic_cutscene_state_wake;
+}
 
-   for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+bool on_cutscene_marker( ent_script_event *event, const char *marker )
+{
+   if( event->type != k_escript_event_allocate )
    {
-      sav->cur = orig;
-      struct script_info *info = &_script_infos[i];
+      struct generic_cutscene *generic = event->userdata;
 
-      if( info->flags & SCRIPT_FLAG_STORY_EVENT )
+      if( generic->state == k_generic_cutscene_state_playing )
       {
-         if( vg_msg_seekframe( sav, info->alias ) )
+         if( _cutscene.marker_this_frame )
          {
-            vg_msg_getkvintg( sav, "viewed_time", k_vg_msg_u64, &info->viewed_time, NULL );
-
-            u8 availible;
-            vg_msg_getkvintg( sav, "availible", k_vg_msg_u8, &availible, NULL );
-            info->availible = availible? 1: 0;
+            if( vg_str_eq( marker, _cutscene.marker_this_frame ) )
+               return 1;
          }
       }
    }
+
+   return 0;
 }
 
-enum script_action
+bool on_nugget_changed( ent_script_event *event, const char *nugget_alias, u64 *out_value )
 {
-   k_script_action_none,
-   k_script_action_unlock,
-   k_script_action_reset,
-   k_script_action_info,
-   k_script_action_play
-};
+   if( event->type == k_escript_event_nugget_changed )
+   {
+      struct script_event_nugget_changed *nugget_info = event->info;
+      if( !strcmp( nugget_alias, nugget_info->alias ) )
+      {
+         *out_value = nugget_info->value;
+         return 1;
+      }
+   }
+   else if( event->type == k_escript_event_world_start )
+   {
+      *out_value = _skaterift_script_nugget_status( nugget_alias );
+      return 1;
+   }
 
-static void _skaterift_script_hook_apply_action( u32 script_id, enum script_action action )
-{
-   struct script_info *info = &_script_infos[ script_id ];
+   return 0;
+}
 
-   if( action == k_script_action_unlock )
+bool on_function_trigger( ent_script_event *event, i32 function_id )
+{
+   if( event->type == k_escript_event_call )
    {
-      info->availible = 1;
+      struct script_event_call *call_info = event->info;
+      if( call_info->function_id == function_id )
+      {
+         return 1;
+      }
+   }
 
-      if( info->linked_challenge )
+   return 0;
+}
+
+bool on_nugget_once( ent_script_event *event, const char *nugget_alias )
+{
+   if( event->type != k_escript_event_allocate )
+   {
+      if( _skaterift_script_nugget_status( nugget_alias ) == 0 )
       {
-         info->linked_challenge->flags &= ~((u32)k_ent_challenge_locked);
-         ent_challenge_visibility( info->linked_challenge, 1 );
+         _skaterift_script_nugget_set( nugget_alias, 1 );
+         return 1;
       }
    }
 
-   if( action == k_script_action_reset )
+   return 0;
+}
+
+enum generic_cutscene_event optional_video_wrapper( const struct generic_cutscene *cutscene_template, 
+                                                    const char *nugget_alias,
+                                                    ent_script_event *event )
+{
+   enum generic_cutscene_event cs_event = generic_cutscene_wrapper( cutscene_template, event );
+
+   u64 viewed;
+   if( on_nugget_changed( event, nugget_alias, &viewed ) )
    {
-      info->availible = 0;
-      info->viewed_time = 0;
+      ent_list_set_visible( event->world, event->entity_list, !viewed );
+   }
 
-      if( info->linked_challenge )
+   if( on_function_trigger( event, 0 ) )
+   {
+      if( on_nugget_once( event, nugget_alias ) )
       {
-         info->linked_challenge->flags |= (u32)k_ent_challenge_locked;
-         ent_challenge_visibility( info->linked_challenge, 0 );
+         play_generic_cutscene( event );
       }
    }
 
-   if( action == k_script_action_play )
-   {
-      _script.script_id = script_id;
-      if( info->jump( k_escript_event_call, NULL ) )
-         _script.script_id = k_escript_script_id_max;
+   return cs_event;
+}
 
-      info->viewed_time = 9999; /* TODO */
+enum generic_cutscene_event challenge_video_wrapper( const struct generic_cutscene *cutscene_template, 
+                                                     const char *nugget_alias,
+                                                     ent_script_event *event )
+{
+   enum generic_cutscene_event cs_event = generic_cutscene_wrapper( cutscene_template, event );
 
-      if( info->linked_challenge )
-         ent_challenge_visibility( info->linked_challenge, 0 );
+   if( on_function_trigger( event, 0 ) )
+   {
+      if( on_nugget_once( event, nugget_alias ) )
+      {
+         play_generic_cutscene( event );
+      }
    }
 
-   if( action == k_script_action_info )
+   return cs_event;
+}
+
+#include "scripts/heaven.c"
+#include "scripts/hub.c"
+#include "scripts/tutorial_island.c"
+
+/* --------------------------------------------------------------------------------------------------------------------
+ *   function aliasing
+ */
+
+struct ent_script_table_entry _ent_script_table[] =
+{
+   { "intro", _skaterift_script_intro },
+   { "hub", _skaterift_script_hub },
+
+   { "tutorial_island", _skaterift_script_tutorial_island },
+   { "ch1s3", _skaterift_script_ch1s3 },
+   { "ch1s3b", _skaterift_script_ch1s3b },
+   { "ch1s4", _skaterift_script_ch1s4 },
+   { "ch1s5", _skaterift_script_ch1s5 },
+   { "ch1s6a", _skaterift_script_ch1s6a },
+   { "unlock_docks", _skaterift_script_unlock_docks },
+
+   { NULL }
+};
+
+
+/* --------------------------------------------------------------------------------------------------------------------
+ *   save data
+ */
+
+struct script_save_nugget
+{
+   const char *alias;
+   u64 status;
+}
+_skaterift_script_savedata[] = 
+{
+   { "intro_view" },
+
+   { "hub_info_view" },
+
+   { "ch1s2_view" },
+   { "ch1s3_view" },
+   { "ch1s3b_view" },
+   { "ch1s4_view" },
+   { "ch1s5_view" },
+   { "ch1s6a_view" },
+   { "unlock_docks_view" },
+
+   { "unlock_mtzero" },
+   { "unlock_city" },
+   { "unlock_valley" }
+};
+
+u64 _skaterift_script_nugget_status( const char *nugget_alias )
+{
+   for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
    {
-      vg_info( "%s | availible: %s, viewed_time: %lu\n", 
-               info->alias,
-               info->availible? "Yes": "No",
-               info->viewed_time );
+      struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+      if( !strcmp( nugget_alias, nugget->alias ) )
+         return nugget->status;
    }
+
+   vg_error( "No nugget named: '%s'\n", nugget_alias );
+   return 0;
 }
 
-static int _skaterift_script_hook( int argc, const char *argv[] )
+void _skaterift_script_nugget_set( const char *nugget_alias, u64 value )
 {
-   if( argc != 2 ) 
+   for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
    {
-      vg_error( "Usage: script <command> <name>\n" );
-      return 0;
+      struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+      if( !strcmp( nugget_alias, nugget->alias ) )
+      {
+         nugget->status = value;
+
+         struct script_event_nugget_changed info;
+         info.alias = nugget_alias;
+         info.value = value;
+
+         struct ent_script_event event;
+         event.type = k_escript_event_nugget_changed;
+         event.info = &info;
+
+         ent_script_propogate_event( &_world.main, &event );
+         return;
+      }
    }
 
-   if( _cutscene.state != k_cutscene_state_none )
+   vg_error( "No nugget named: '%s'\n", nugget_alias );
+}
+
+void _skaterift_script_write_savedata( vg_msg *sav )
+{
+   for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
    {
-      vg_error( "Cannot do anything while cutscene is in use.\n" );
-      return 0;
+      struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+      vg_msg_wkvnum( sav, nugget->alias, k_vg_msg_u64, 1, &nugget->status );
    }
+}
 
-   enum script_action action = k_script_action_none;
+void _skaterift_script_load_savedata( vg_msg *sav )
+{
+   for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
+   {
+      struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+      vg_msg_getkvintg( sav, nugget->alias, k_vg_msg_u64, &nugget->status, NULL );
+   }
+}
 
-   if     ( !strcmp( argv[0], "unlock")) action = k_script_action_unlock;
-   else if( !strcmp( argv[0], "play" ) ) action = k_script_action_play;
-   else if( !strcmp( argv[0], "info" ) ) action = k_script_action_info;
-   else if( !strcmp( argv[0], "reset" )) action = k_script_action_reset;
-   else                                   
+static int _skaterift_script_ccmd( int argc, const char *argv[] )
+{
+   if( argc < 2 ) 
    {
-      vg_error( "'%s' is not a command\n", argv[0] );
+      vg_error( "Usage: script <command> <data name> <optional set-value>\n" );
       return 0;
    }
 
-   if( !strcmp( argv[1], "all" ) )
+   if( !strcmp( argv[0], "nugget" ) ) 
    {
-      if( action == k_script_action_play )
+      bool all = 0;
+      if( !strcmp( argv[1], "all" ) ) 
+         all = 1;
+
+      for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
       {
-         vg_error( "No\n" );
-         return 0;
+         struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+         if( all || !strcmp( argv[1], nugget->alias ) )
+         {
+            if( argc==3 ) _skaterift_script_nugget_set( nugget->alias, atol(argv[2]) );
+            else vg_info( "%s: %lu\n", nugget->alias, nugget->status );
+         }
       }
 
-      for( u32 i=0; i<k_escript_script_id_max; i ++ )
-         _skaterift_script_hook_apply_action( i, action );
-
       return 1;
    }
-   else
+   else if( !strcmp( argv[0], "trigger" ) )
    {
-      struct script_info *info = skaterift_script_get_info( argv[1] );
-      if( info )
+      if( !strcmp( argv[1], "start" ) )
       {
-         _skaterift_script_hook_apply_action( info-_script_infos, action );
+         ent_script_start( &_world.main );
          return 1;
       }
       else
       {
-         vg_error( "No such script '%s'\n", argv[1] );
+         vg_error( "Dont know event '%s'.\n", argv[1] );
          return 0;
       }
    }
+   else                                   
+   {
+      vg_error( "'%s' is not a command\n", argv[0] );
+      return 0;
+   }
 }
 
-static void _skaterift_script_hook_poll( int argc, const char *argv[] )
+static void _skaterift_script_ccmd_poll( int argc, const char *argv[] )
 {
    const char *term = argv[argc-1];
 
    if( argc == 1 )
    {
-      console_suggest_score_text( "unlock", term, 0 );
-      console_suggest_score_text( "reset", term, 0 );
-      console_suggest_score_text( "play", term, 0 );
-      console_suggest_score_text( "info", term, 0 );
+      console_suggest_score_text( "nugget", term, 0 );
+      console_suggest_score_text( "trigger", term, 0 );
    }
    else if( argc == 2 )
    {
-      console_suggest_score_text( "all", term, 0 );
-      for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+      if( !strcmp( argv[0], "nugget" ) )
       {
-         struct script_info *info = &_script_infos[i];
-         console_suggest_score_text( info->alias, term, 0 );
+         console_suggest_score_text( "all", term, 0 );
+         for( u32 i=0; i<VG_ARRAY_LEN( _skaterift_script_savedata ); i ++ )
+         {
+            struct script_save_nugget *nugget = &_skaterift_script_savedata[i];
+            console_suggest_score_text( nugget->alias, term, 0 );
+         }
+      }
+      else if( !strcmp( argv[0], "trigger" ) )
+      {
+         console_suggest_score_text( "start", term, 0 );
       }
-   }
-}
-
-void _skaterift_script_update(void)
-{
-   if( _script.script_id != k_escript_script_id_max )
-   {
-      if( _script_infos[ _script.script_id ].jump( k_escript_event_update, NULL ) )
-         _script.script_id = k_escript_script_id_max;
-   }
-}
-
-void _skaterift_script_marker( const char *marker )
-{
-   if( _script.script_id != k_escript_script_id_max )
-   {
-      if( _script_infos[ _script.script_id ].jump( k_escript_event_cutscene_marker, marker) )
-         _script.script_id = k_escript_script_id_max;
    }
 }
 
 void _skaterift_script_init(void)
 {
-   vg_console_reg_cmd( "script", _skaterift_script_hook, _skaterift_script_hook_poll );
-}
-
-void _skaterift_script_unlink_all_challenges(void)
-{
-   for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
-      _script_infos[i].linked_challenge = NULL;
+   vg_console_reg_cmd( "script", _skaterift_script_ccmd, _skaterift_script_ccmd_poll );
 }
index 02a5bf7d8e75f0ea925fb4874b4f9ceb8c1c9f48..5d1d7fd6b5ea7afdce3811a6056803227d84c2d2 100644 (file)
@@ -1,63 +1,37 @@
 #pragma once
+#include "ent_script.h"
 
-#define SCRIPT_FLAG_STORY_EVENT 0x1
+void _skaterift_script_init(void);
+void _skaterift_script_write_savedata( vg_msg *sav );
+void _skaterift_script_load_savedata( vg_msg *sav );
 
-enum escript_event
-{
-   k_escript_event_call = 0,
-   k_escript_event_update = 1,
-   k_escript_event_cutscene_marker = 2
-};
+u64 _skaterift_script_nugget_status( const char *nugget_alias );
+void _skaterift_script_nugget_set( const char *nugget_alias, u64 value );
 
-struct script_info
+struct generic_cutscene
 {
-   const char *alias;
-   bool( *jump )( enum escript_event ev, const char *inf );
-   u32 flags;
-
-   bool availible;
-   u64 viewed_time;
-
-   ent_challenge *linked_challenge;
+   enum generic_cutscene_state
+   {
+      k_generic_cutscene_state_none,
+      k_generic_cutscene_state_wake,
+      k_generic_cutscene_state_init,
+      k_generic_cutscene_state_loading,
+      k_generic_cutscene_state_playing,
+      k_generic_cutscene_state_end
+   }
+   state;
+   const char *metascene_path;
+   const cs_subtitle *subtitles;
+   bool freeze_player;
+   bool trigger_start;
 };
 
-void _skaterift_script_update(void);
-void _skaterift_script_init(void);
-void _skaterift_script_marker( const char *marker );
-void skaterift_script_write_savedata( vg_msg *sav );
-void skaterift_script_load_savedata( vg_msg *sav );
-static int _skaterift_script_hook( int argc, const char *argv[] );
-struct script_info *skaterift_script_get_info( const char *alias );
-
-void _skaterift_script_unlink_all_challenges(void);
-
-enum escript_script_id
+enum generic_cutscene_event
 {
-   k_escript_script_id_test = 0,
-   k_escript_script_id_intro,
-   k_escript_script_id_ch1s2,
-   k_escript_script_id_ch1s3,
-   k_escript_script_id_ch1s3b,
-   k_escript_script_id_ch1s4,
-   k_escript_script_id_ch1s5,
-   k_escript_script_id_ch1s6a,
-   k_escript_script_id_ch2s1,
-   k_escript_script_id_ch2s2,
-   k_escript_script_id_ch2s3a,
-   k_escript_script_id_ch2s4,
-   k_escript_script_id_ch2e1,
-   k_escript_script_id_ch2s5,
-   k_escript_script_id_ch2s6,
-   k_escript_script_id_ch3s1,
-   k_escript_script_id_ch3s2,
-   k_escript_script_id_ch3s3,
-   k_escript_script_id_ch4s1a,
-   k_escript_script_id_ch4s1, /* NOTE: 1 and 2 are backwards in order (accident) */
-   k_escript_script_id_ch4s2, 
-   k_escript_script_id_ch4s3,
-   k_escript_script_id_hub_setup,
-   k_escript_script_id_hub_guide,
-   k_escript_script_id_max 
+   k_generic_cutscene_event_none,
+   k_generic_cutscene_event_end,
+   k_generic_cutscene_event_start
 };
-bool _skaterift_script_check_unlocked( enum escript_script_id id );
-bool _skaterift_script_already_viewed( enum escript_script_id id );
+
+enum generic_cutscene_event generic_cutscene_wrapper( const struct generic_cutscene *cutscene_template, ent_script_event *event );
+void play_generic_cutscene( ent_script_event *event );
index bb907d6fb560c1546b4c0f2538d9bb3bc8764e80..b06d202c1e2f86c3f2e087198a382908d8e5c2eb 100644 (file)
@@ -38,6 +38,7 @@ void skaterift_world_get_save_path( addon_reg *world_reg, char buf[128] )
 
 void world_update( world_instance *world, v3f pos )
 {
+   ent_script_update( world );
    world_routes_update_timer_texts( world );
    world_routes_update( world );
    ent_traffic_update( world, pos );
@@ -59,6 +60,7 @@ bool world_set_event( enum world_event event )
    {
       vg_info( "Switched to world event: %d\n", event );
       _world.event = event;
+      world_routes_clear( &_world.main );
       return 1;
    }
    else
index 7443e5e46b68b7e29f55bf5727c18898186f0745..5f39110cb25f32e74c8ef1f3ea5b86b319204f67 100644 (file)
@@ -177,15 +177,8 @@ struct world_instance
                  ent_region,
                  ent_glider,
                  ent_list,
-                 file_entity_ref;
-
-   /* These share the indexes of ent_challenge. 
-    * So ent_challenge[x] is related to events[x] */
-   struct event_info 
-   {
-      struct script_info *story_script;
-   }
-   * events;
+                 file_entity_ref,
+                 ent_script;
    
    GLuint *nonlocal_gates_cubemaps;
    u32 nonlocal_gate_count;
index ddf715009a0afcb293b165621428e35fa92e2a85..dcedb84cc6ab8fe4bb7034229912bb86b44eb7ef 100644 (file)
@@ -800,32 +800,9 @@ void world_entity_start( world_instance *world, vg_msg *sav )
 
       if( challenge->flags & k_ent_challenge_is_story )
       {
-         struct script_info *script = skaterift_script_get_info( alias );
-         world->events[i].story_script = script;
-
-         if( script )
-         {
-            script->linked_challenge = challenge;
-
-            bool hide_linked = 0;
-
-            if( !script->availible )
-            {
-               hide_linked = 1;
-               challenge->flags |= k_ent_challenge_locked;
-            }
-
-            if( script->viewed_time )
-               hide_linked = 1;
-
-            if( hide_linked )
-               ent_challenge_visibility( challenge, 0 );
-         }
       }
       else
       {
-         world->events[i].story_script = NULL;
-
          u32 result;
          vg_msg_getkvintg( sav, alias, k_vg_msg_u32, &result, NULL );
 
@@ -902,6 +879,7 @@ void world_entity_start( world_instance *world, vg_msg *sav )
 
    ent_region_re_eval( world );
    world_volumes_start( world );
+   ent_script_start( world );
 }
 
 void world_entity_serialize( world_instance *world, vg_msg *sav )
@@ -913,7 +891,8 @@ void world_entity_serialize( world_instance *world, vg_msg *sav )
       vg_msg_wkvnum( sav, alias, k_vg_msg_u32, 1, &challenge->status );
    }
    
-   if( af_arrcount(&world->ent_route) ){
+   if( af_arrcount(&world->ent_route) )
+   {
       vg_msg_frame( sav, "routes" );
       for( u32 i=0; i<af_arrcount(&world->ent_route); i++ ){
          ent_route *route = af_arritm( &world->ent_route, i );
@@ -921,8 +900,7 @@ void world_entity_serialize( world_instance *world, vg_msg *sav )
          vg_msg_frame( sav, af_str( &world->meta.af, route->pstr_name ) );
          {
             vg_msg_wkvnum( sav, "flags", k_vg_msg_u32, 1, &route->flags );
-            vg_msg_wkvnum( sav, "best_laptime", 
-                           k_vg_msg_f64, 1, &route->best_laptime );
+            vg_msg_wkvnum( sav, "best_laptime", k_vg_msg_f64, 1, &route->best_laptime );
 
             f32 sections[ route->checkpoints_count ];
 
@@ -933,8 +911,7 @@ void world_entity_serialize( world_instance *world, vg_msg *sav )
                sections[j] = cp->best_time;
             }
 
-            vg_msg_wkvnum( sav, "sections", k_vg_msg_f32, 
-                           route->checkpoints_count, sections );
+            vg_msg_wkvnum( sav, "sections", k_vg_msg_f32, route->checkpoints_count, sections );
          }
          vg_msg_end_frame( sav );
       }
index b67102d845aaf049ed36fc62612e109f167324a9..2874197a8cb666027f73966df438c9a33d9246e8 100644 (file)
@@ -114,6 +114,7 @@ static void world_instance_load_mdl( world_instance *world, const char *path, vo
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_glider,    ent_glider,     heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_list,      ent_list,       heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->file_entity_ref, file_entity_ref, heap );
+      AF_LOAD_ARRAY_STRUCT( af, &world->ent_script, ent_script, heap );
    }
 
    array_file_ptr infos;
@@ -144,11 +145,6 @@ static void world_instance_load_mdl( world_instance *world, const char *path, vo
       world->info.flags = 0;
    }
 
-   if( load_all )
-   {
-      world->events = vg_linear_alloc( heap, af_arrcount(&world->ent_challenge)*sizeof(struct event_info) );
-   }
-
    vg_loader_set_user_information( "Compiling world details" );
 
    time_t seconds = time(NULL) % ((u32)vg_maxf(1.0f,k_day_length)*60);
@@ -215,8 +211,9 @@ static void world_instance_load_mdl( world_instance *world, const char *path, vo
          board->data_len = 0;
       }
 
-      world->routes_ui = vg_linear_alloc( heap, 
-            sizeof(struct route_ui)*af_arrcount(&world->ent_route) );
+      world->routes_ui = vg_linear_alloc( heap, sizeof(struct route_ui)*af_arrcount(&world->ent_route) );
+
+      ent_script_alloc( world, heap );
 
       vg_loader_set_user_information( "Postprocessing world" );
       vg_async_call( async_world_postprocess, world, 0 );
@@ -427,7 +424,6 @@ void world_switcher_update(void)
       if( !vg_audio_flagged_stopped( AUDIO_FLAG_WORLD ) )
          return;
       
-      _skaterift_script_unlink_all_challenges();
       world_instance_free_graphics_data( &_world.main );
       _world.main.complete = 0;
       _world.loader_state = k_world_loader_ready;
index 76bbe5b84109d4d68c7d5a58d1e886075dbbc415..5884fbf1a92a4a7ab4501d167b3f2de876158e40 100644 (file)
@@ -228,6 +228,7 @@ void render_world_map(void)
          if( challenge->flags & k_ent_challenge_is_story )
          {
             icon = k_gui_icon_story2d;
+#if 0
             if( world->events[i].story_script )
             {
                if( world->events[i].story_script->viewed_time )
@@ -235,6 +236,7 @@ void render_world_map(void)
                   icon = k_gui_icon_story_done2d;
                }
             }
+#endif
          }
          else
          {
@@ -648,6 +650,8 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
                for( u32 j=0; j<af_arrcount(&world->ent_challenge); j ++ )
                {
                   ent_challenge *challenge = af_arritm( &world->ent_challenge, j );
+                  if( challenge->flags & k_ent_challenge_is_story )
+                     continue;
 
                   v3f local;
                   m4x3_mulv( volume->to_local, challenge->transform.co, local );
@@ -673,8 +677,7 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 
                   ui_rect r;
                   ui_standard_widget( ctx, stat_panel, r, 1 );
-                  ui_text( ctx, r, buf, 1, 
-                           k_ui_align_middle_left, medal_colour( ctx, flags ) );
+                  ui_text( ctx, r, buf, 1, k_ui_align_middle_left, medal_colour( ctx, flags ) );
                }
 
                stat_panel[0] -= 16;
index 5e8ba75ef6e9d07a06712685f4f34b3c32cdb5dd..1e6637857ad2e0c6e9490b4b7da28ef5fc358ef9 100644 (file)
@@ -77,11 +77,15 @@ void world_volumes_update( world_instance *world, v3f pos )
           type  = mdl_entity_id_type( id ),
           index = mdl_entity_id_id( id );
 
-      if( type != k_ent_volume ) continue;
+      if( type != k_ent_volume ) 
+         continue;
 
       ent_volume *volume = af_arritm( &world->ent_volume, index );
+
+      if( volume->flags & k_ent_volume_flag_disabled )
+         continue;
+
       boxf cube = {{-1.0f,-1.0f,-1.0f},{1.0f,1.0f,1.0f}};
-      
       if( volume->flags & k_ent_volume_flag_particles )
       {
          vg_line_boxf_transformed( volume->to_world, cube, 0xff00c0ff );