new world menu proto
authorhgn <hgodden00@gmail.com>
Mon, 17 Feb 2025 20:03:07 +0000 (20:03 +0000)
committerhgn <hgodden00@gmail.com>
Mon, 17 Feb 2025 20:03:07 +0000 (20:03 +0000)
25 files changed:
build.c
content_skaterift/maps/mp_spawn/main.mdl
content_skaterift/models/rs_superworlds.mdl [new file with mode: 0644]
content_skaterift/models/world_error.mdl [new file with mode: 0644]
shaders/model_superworld.fs [new file with mode: 0644]
shaders/scene_override.fs
shaders/scene_preview.fs
src/addon.c
src/addon.h
src/ent_skateshop.c
src/font.h
src/input.h
src/menu.c
src/menu.h
src/player.c
src/save.c
src/skaterift.c
src/workshop.c
src/world_entity.c
src/world_gate.c
src/world_load.c
src/world_map.c
src/world_map.h
src/world_render.c
src/world_render.h

diff --git a/build.c b/build.c
index 34245b57c3128e46af832817b84bab9bad0f6e3d..cb72732f0ce15783fe370f7959b3b1c77edabd4f 100644 (file)
--- a/build.c
+++ b/build.c
@@ -122,6 +122,7 @@ void build_shaders(void){
    _S( "model_board_view",     "model.vs",         "model_character_view.fs" );
    _S( "model_entity",         "model.vs",         "model_entity.fs" );
    _S( "model_gate",           "model.vs",         "model_gate_lq.fs" );
+   _S( "model_superworld",     "model.vs",         "model_superworld.fs" );
    _S( "model_gate_unlinked",  "model.vs",         "model_gate_unlinked.fs" );
    _S( "model_font",           "model_font.vs",    "model_font.fs" );
 
index 7f454e0cb8fe6deaba9e348c4f7641e72b21a39a..eefd82e82a8c41aebc15db612eb2a89b88c75df7 100644 (file)
Binary files a/content_skaterift/maps/mp_spawn/main.mdl and b/content_skaterift/maps/mp_spawn/main.mdl differ
diff --git a/content_skaterift/models/rs_superworlds.mdl b/content_skaterift/models/rs_superworlds.mdl
new file mode 100644 (file)
index 0000000..71733c5
Binary files /dev/null and b/content_skaterift/models/rs_superworlds.mdl differ
diff --git a/content_skaterift/models/world_error.mdl b/content_skaterift/models/world_error.mdl
new file mode 100644 (file)
index 0000000..4c97e37
Binary files /dev/null and b/content_skaterift/models/world_error.mdl differ
diff --git a/shaders/model_superworld.fs b/shaders/model_superworld.fs
new file mode 100644 (file)
index 0000000..e713e21
--- /dev/null
@@ -0,0 +1,27 @@
+out vec4 FragColor;
+
+uniform float uTime;
+uniform vec3 uCam;
+uniform vec4 uColour;
+
+in vec3 aNorm;
+in vec2 aUv;
+in vec3 aCo;
+in vec3 aWorldCo;
+
+void main()
+{
+   vec2 ssuv = gl_FragCoord.xy;
+   float opacity = 1.0-smoothstep(0.0,1.0,aUv.y+uColour.a);
+   
+   vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) );
+   float dither = fract( vDither.g / 71.0 ) - 0.5;
+
+   vec3 halfdir = normalize(aWorldCo - uCam);
+   float fresnel = pow(1.0 - dot(aNorm,-halfdir), 2.0);
+
+   float fmap = fresnel+dither + (aUv.y-0.2)*0.2;
+   float f1bit = step(0.5,fmap);
+
+   FragColor = vec4( mix( vec3(0.02,0.02,0.08), vec3( 0.3, 0.6, 0.94 ), f1bit ), 1.0 );
+}
index ec2a89a3a49c19cdeba45eb0c8a7fcb69818f0f5..4561fe47653fa594f07828256feddccf76a762c9 100644 (file)
@@ -7,6 +7,7 @@ uniform vec4 uPlayerPos; /* w: distance to uSpawnPos */
 uniform vec4 uSpawnPos;  /* w: inverse distance to uPlayerPos */
 uniform bool uAlphatest;
 uniform vec4 uMapInfo; /* x: min, y: max, z: iso line amount */
+uniform vec4 uSurfaceInfo;
 
 #include "light_clearskies_stddef.glsl"
 #include "common_scene.glsl"
@@ -78,6 +79,8 @@ void main(){
       vfrag *= 1.0f+(lg*0.2*uMapInfo.z);
    }
 
+   vfrag -= (1.0-uSurfaceInfo.x)*0.1;
+
    oColour = vec4( vfrag, 1.0 );
    //oColour = vec4( vfrag, 1.0 );
 }
index 0cc5ee0b8896dc814f3d1a907b42c8523adc8297..e749699e1575476de8e57fee65483a7875ba3c5c 100644 (file)
@@ -5,6 +5,8 @@ in vec3 aWorldCo;
 
 uniform bool uAlphatest;
 uniform vec4 uMapInfo; /* x: min, y: max, z: iso line amount */
+uniform vec3 uCamera;
+uniform vec4 uSurfaceInfo; /* x: skate surface flag */
 
 layout (location = 0) out vec4 oColour;
 
@@ -24,7 +26,6 @@ void main()
 
    compute_motion_vectors();
 
-   vec3 vfrag = vec3(aUv.xy,0.0);
    vec3 qnorm = aNorm.xyz;
 
    qnorm = normalize(floor(aNorm.xyz*4.0)*0.25);
@@ -38,5 +39,22 @@ void main()
    else{
    }
 
+   if( aNorm.y < -0.2 ) discard;
+
+   vec3 halfdir = normalize(aWorldCo - uCamera);
+   float fresnel = pow(qnorm.y,2.0)*0.5;//pow(1.0-dot(-halfdir,qnorm),1.0);
+
+   vec3 grid = step(fract(aWorldCo/15.0),vec3(0.04));
+   float fgrid = max(grid.x,grid.z)*pow(aNorm.y,2.0);
+
+   vec3 vfrag = vec3(step(0.5,fresnel+dither));//vec3( step(grid,vec3(0.1)) );
+
+   float height_ramp = aWorldCo.y * 0.01;
+   float rando_uv = clamp(aUv.y,0.0,1.0);
+
+   float fmap = fresnel+dither+fgrid+(uSurfaceInfo.x-0.5)*0.7;
+   float f1bit = step(0.5,fmap);
+
+   vfrag = mix( vec3(0.1,0.1,0.2), vec3( 0.2, 0.4, 0.8 ), f1bit );
    oColour = vec4( vfrag, 1.0 );
 }
index 5cfd075e0e454e1722d5b6b966a34c804e74ee8e..ab80e9fe56bb684b4176ce2d03d1cbca76f9fe46 100644 (file)
 
 struct addon_system addon_system;
 
-u32 addon_count( enum addon_type type, u32 ignoreflags )
+static bool addon_filter( addon_reg *reg, u32 whitelist, u32 blacklist )
 {
-   if( ignoreflags ){
+   if( whitelist && !(reg->flags & whitelist) )
+      return 0;
+
+   if( reg->flags & blacklist )
+      return 0;
+
+   return 1;
+}
+
+u32 addon_count( enum addon_type type, u32 whitelist, u32 blacklist )
+{
+   if( whitelist || blacklist )
+   {
       u32 typecount = 0, count = 0;
-      for( u32 i=0; typecount<addon_count( type, 0 ); i++ ){
+      for( u32 i=0; typecount<addon_count( type, 0,0 ); i++ )
+      {
          addon_reg *reg = &addon_system.registry[i];
-         if( reg->alias.type == type ){
+         if( reg->alias.type == type )
+         {
             typecount ++;
 
-            if( reg->flags & ignoreflags )
-               continue;
-            
-            count ++;
+            if( addon_filter( reg, whitelist, blacklist ) )
+               count ++;
          }
       }
 
@@ -34,38 +46,44 @@ u32 addon_count( enum addon_type type, u32 ignoreflags )
 
 
 /* these kind of suck, oh well. */
-addon_reg *get_addon_from_index( enum addon_type type, u32 index, 
-                                 u32 ignoreflags )
+addon_reg *get_addon_from_index( enum addon_type type, u32 index, u32 whitelist, u32 blacklist )
 {
    u32 typecount = 0, count = 0;
-   for( u32 i=0; typecount<addon_count(type,0); i++ ){
+   for( u32 i=0; typecount<addon_count(type,0,0); i++ )
+   {
       addon_reg *reg = &addon_system.registry[i];
-      if( reg->alias.type == type ){
+      if( reg->alias.type == type )
+      {
          typecount ++;
 
-         if( reg->flags & ignoreflags )
-            continue;
-
-         if( index == count )
-            return reg;
+         if( addon_filter( reg, whitelist, blacklist ) )
+         {
+            if( index == count )
+               return reg;
 
-         count ++;
+            count ++;
+         }
       }
    }
 
    return NULL;
 }
 
-u32 get_index_from_addon( enum addon_type type, addon_reg *a )
+u32 get_index_from_addon( enum addon_type type, addon_reg *a, u32 whitelist, u32 blacklist )
 {
    u32 count = 0;
-   for( u32 i=0; count<addon_system.registry_type_counts[type]; i++ ){
+   for( u32 i=0; count<addon_system.registry_type_counts[type]; i++ )
+   {
       addon_reg *reg = &addon_system.registry[i];
-      if( reg->alias.type == type ){
-         if( reg == a )
-            return count;
+      if( reg->alias.type == type )
+      {
+         if( addon_filter( reg, whitelist, blacklist ) )
+         {
+            if( reg == a )
+               return count;
 
-         count ++;
+            count ++;
+         }
       }
    }
 
@@ -279,9 +297,10 @@ static void addon_set_foldername( addon_reg *reg, const char name[64] ){
 /*
  * Create a new registry 
  */
-static addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id,
-                                      enum addon_type type ){
-   if( addon_system.registry_count == ADDON_MOUNTED_MAX ){
+static addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id, enum addon_type type )
+{
+   if( addon_system.registry_count == ADDON_MOUNTED_MAX )
+   {
       vg_error( "You have too many addons installed!\n" );
       return NULL;
    }
@@ -294,10 +313,12 @@ static addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id,
    reg->alias.foldername[0] = '\0';
    reg->alias.type = type;
 
-   if( workshop_id ){
+   if( workshop_id )
+   {
       char foldername[64];
       snprintf( foldername, 64, PRINTF_U64, workshop_id );
       addon_set_foldername( reg, foldername );
+      reg->flags |= ADDON_REG_WORKSHOP;
    }
    return reg;
 }
@@ -352,15 +373,13 @@ static void addon_mount_finish( addon_reg *reg ){
 /*
  * Mount a fully packaged addon, one that certainly has a addon.inf
  */
-static addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id,
-                                                  vg_str folder_path )
+static addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id, vg_str folder_path )
 {
    addon_reg *reg = addon_alloc_reg( workshop_id, k_addon_type_none );
    if( !reg ) return NULL;
 
-   if( !addon_try_load_metadata( reg, folder_path ) ){
+   if( !addon_try_load_metadata( reg, folder_path ) )
       return NULL;
-   }
 
    enum addon_type type = k_addon_type_none;
    vg_msg msg;
@@ -467,11 +486,14 @@ addon_reg *addon_mount_local_addon( const char *folder,
  */
 void addon_mount_workshop_items(void)
 {
-   if( skaterift.demo_mode ){
+   if( skaterift.demo_mode )
+   {
       vg_info( "Won't load workshop items in demo mode\n" );
       return;
    }
-   if( !steam_ready ) return;
+
+   if( !steam_ready ) 
+      return;
 
    /*
     * Steam workshop scan
@@ -480,19 +502,20 @@ void addon_mount_workshop_items(void)
    PublishedFileId_t workshop_ids[ ADDON_MOUNTED_MAX ];
    u32 workshop_count = ADDON_MOUNTED_MAX;
 
-   vg_async_item *call = vg_async_alloc(
-                           sizeof(struct async_workshop_installed_files_info));
+   vg_async_item *call = vg_async_alloc( sizeof(struct async_workshop_installed_files_info));
    struct async_workshop_installed_files_info *info = call->payload;
    info->buffer = workshop_ids;
    info->len = &workshop_count;
    vg_async_dispatch( call, async_workshop_get_installed_files );
    vg_async_stall();
 
-   for( u32 j=0; j<workshop_count; j++ ){
+   for( u32 j=0; j<workshop_count; j++ )
+   {
       /* check for existance in both our caches
        * ----------------------------------------------------------*/
       PublishedFileId_t id = workshop_ids[j];
-      for( u32 i=0; i<addon_system.registry_count; i++ ){
+      for( u32 i=0; i<addon_system.registry_count; i++ )
+      {
          addon_reg *reg = &addon_system.registry[i];
 
          if( reg->alias.workshop_id == id )
@@ -617,8 +640,9 @@ u16 addon_cache_fetch( enum addon_type type, u32 reg_index )
 {
    addon_reg *reg = NULL;
 
-   if( reg_index < addon_count( type, 0 ) ){
-      reg = get_addon_from_index( type, reg_index, 0 );
+   if( reg_index < addon_count( type, 0,0 ) )
+   {
+      reg = get_addon_from_index( type, reg_index, 0,0 );
       if( reg->cache_id ) 
          return reg->cache_id;
    }
@@ -637,8 +661,8 @@ u16 addon_cache_alloc( enum addon_type type, u32 reg_index )
    struct addon_cache_entry *new_entry = vg_pool_item( &cache->pool, new_id );
 
    addon_reg *reg = NULL;
-   if( reg_index < addon_count( type, 0 ) )
-      reg = get_addon_from_index( type, reg_index, 0 );
+   if( reg_index < addon_count( type, 0,0 ) )
+      reg = get_addon_from_index( type, reg_index, 0,0 );
 
    if( new_entry ){
       if( new_entry->reg_ptr )
@@ -788,7 +812,7 @@ static void T1_addon_cache_load_loop(void *_)
             vg_info( "process cache load request (%u#%u, reg:%u)\n",
                         type, id, entry->reg_index );
 
-            if( entry->reg_index >= addon_count(type,0) )
+            if( entry->reg_index >= addon_count(type,0,0) )
             {
                /* should maybe have a different value for this case */
                entry->state = k_addon_cache_state_none;
@@ -799,7 +823,7 @@ static void T1_addon_cache_load_loop(void *_)
             SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
 
             /* continue with the request */
-            addon_reg *reg = get_addon_from_index( type, entry->reg_index, 0 );
+            addon_reg *reg = get_addon_from_index( type, entry->reg_index, 0,0 );
             entry->reg_ptr = reg;
 
             vg_str folder;
index db51af125664257d3c7d659051f0e07f0270d4a5..9cbfb6874686ddb99c4c9f02fca2482cd7350d38 100644 (file)
@@ -20,6 +20,11 @@ struct addon_alias
 #define ADDON_REG_CITY     0x4
 #define ADDON_REG_PREMIUM  0x8
 
+#define ADDON_REG_CAMPAIGN 0x10
+#define ADDON_REG_WORKSHOP 0x20
+#define ADDON_REG_VENUS    0x40
+#define ADDON_REG_INFINITE 0x80
+
 struct addon_system
 {
    struct addon_reg
@@ -67,10 +72,9 @@ struct addon_system
 extern addon_system;
 
 void addon_system_init( void );
-u32 addon_count( enum addon_type type, u32 ignoreflags );
-addon_reg *get_addon_from_index( enum addon_type type, u32 index, 
-                                 u32 ignoreflags );
-u32 get_index_from_addon( enum addon_type type, addon_reg *a );
+u32 addon_count( enum addon_type type, u32 whitelist, u32 blacklist );
+addon_reg *get_addon_from_index( enum addon_type type, u32 index, u32 whitelist, u32 blacklist );
+u32 get_index_from_addon( enum addon_type type, addon_reg *a, u32 whitelist, u32 blacklist );
 int addon_get_content_folder( addon_reg *reg, vg_str *folder, int async);
 
 /* scanning routines */
index 0de8a0395c258b6c1d13ff298c1cefe3743c044f..edbc142e35537eb10845b47124d66545ff325a17 100644 (file)
@@ -179,11 +179,13 @@ void skateshop_init(void)
    vg_async_call( skateshop_init_async, NULL, 0 );
 }
 
-static u16 skateshop_selected_cache_id(void){
-   if( addon_count(k_addon_type_board, ADDON_REG_HIDDEN) ){
+static u16 skateshop_selected_cache_id(void)
+{
+   if( addon_count(k_addon_type_board, 0,ADDON_REG_HIDDEN) )
+   {
       addon_reg *reg = get_addon_from_index( 
             k_addon_type_board, global_skateshop.selected_board_id,
-            ADDON_REG_HIDDEN );
+            0, ADDON_REG_HIDDEN );
       return reg->cache_id;
    }
    else return 0;
@@ -271,9 +273,11 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
          }
       }
 
-      u32 valid_count = addon_count( k_addon_type_board, 0 );
-      if( button_down( k_srbind_mright ) ){
-         if( global_skateshop.selected_board_id+1 < valid_count ){
+      u32 valid_count = addon_count( k_addon_type_board, 0,0 );
+      if( button_down( k_srbind_mright ) )
+      {
+         if( global_skateshop.selected_board_id+1 < valid_count )
+         {
             global_skateshop.selected_board_id ++;
          }
       }
@@ -298,11 +302,12 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
          return;
       }
    }
-   else if( shop->type == k_skateshop_type_charshop ){
+   else if( shop->type == k_skateshop_type_charshop )
+   {
       if( !vg_loader_availible() ) return;
 
       int changed = 0;
-      u32 valid_count = addon_count( k_addon_type_player, ADDON_REG_HIDDEN );
+      u32 valid_count = addon_count( k_addon_type_player, 0, ADDON_REG_HIDDEN );
 
       if( button_down( k_srbind_mleft ) ){
          if( global_skateshop.selected_player_id > 0 ){
@@ -326,13 +331,11 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
          changed = 1;
       }
 
-      if( changed ){
-         addon_reg *addon = get_addon_from_index( 
-               k_addon_type_player, global_skateshop.selected_player_id,
-               ADDON_REG_HIDDEN );
-
-         u32 real_id = get_index_from_addon( 
-               k_addon_type_player, addon );
+      if( changed )
+      {
+         addon_reg *addon = get_addon_from_index( k_addon_type_player, global_skateshop.selected_player_id,
+                                                  0, ADDON_REG_HIDDEN );
+         u32 real_id = get_index_from_addon( k_addon_type_player, addon,0,0 );
 
          player__use_model( real_id );
       }
@@ -536,7 +539,7 @@ fade_out:;
    mlocal[3][2] = -0.7f;
    m4x3_mul( mrack, mlocal, mmdl );
 
-   u32 valid_count = addon_count(k_addon_type_board,0);
+   u32 valid_count = addon_count(k_addon_type_board,0,0);
    if( valid_count ){
       char buf[16];
       vg_str str;
@@ -623,7 +626,7 @@ static void skateshop_render_worldshop( ent_skateshop *shop ){
       global_skateshop.render.world_title = "missing: workshop.title";
 
       addon_reg *reg = get_addon_from_index( k_addon_type_world,
-            global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
+                                             global_skateshop.selected_world_id, 0, ADDON_REG_HIDDEN );
 
       if( !reg ) 
          goto none;
@@ -658,7 +661,7 @@ none:;
    vg_strnull( &info, buftext, 128 );
    vg_strnull( &subtext, bufsubtext, 128 );
    
-   u32 valid_count = addon_count(k_addon_type_world,ADDON_REG_HIDDEN);
+   u32 valid_count = addon_count(k_addon_type_world,0,ADDON_REG_HIDDEN);
    if( valid_count )
    {
       vg_strcati32( &info, global_skateshop.selected_world_id+1 );
@@ -674,7 +677,7 @@ none:;
       else
       {
          addon_reg *reg = get_addon_from_index( k_addon_type_world,
-                     global_skateshop.selected_world_id, ADDON_REG_HIDDEN );
+                     global_skateshop.selected_world_id, 0, ADDON_REG_HIDDEN );
 
          if( reg->alias.workshop_id )
             vg_strcat( &subtext, "(Workshop) " );
index 3e225e814a19d833d3687663d66e029f3bd84baf..af27a4cdde61d8aaa42b247a525060bb02eeb9f5 100644 (file)
@@ -16,6 +16,7 @@ enum efont_SRglyph{
    k_SRglyph_ctrl_center   = 0x03, /* useful when text is scaled down */
    k_SRglyph_ctrl_baseline = 0x04, /* . */
    k_SRglyph_ctrl_top      = 0x05, /* . */
+   k_SRglyph_ctrl_glow     = 0x06, /* glow text */
    k_SRglyph_mod_circle    = 0x1e, /* surround and center next charater */
    k_SRglyph_mod_square    = 0x1f, /* surround and center next character */
    k_SRglyph_ascii_min     = 0x20, /* standard ascii */
index 337782420c932ff4d2b9e0729709b73b7e86b402..ff912c10e7a1f8b81032d74e89afa34a9cb51192 100644 (file)
@@ -25,6 +25,7 @@ enum sr_bind
    k_srbind_mback, 
    k_srbind_maccept,
    k_srbind_mopen,
+   k_srbind_mclose,
    k_srbind_mhub,
    k_srbind_replay_play,
    k_srbind_replay_freecam,
@@ -92,6 +93,7 @@ static vg_input_op *input_button_list[] = {
 [k_srbind_mdown] = INPUT_BASIC( SDLK_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN ),
 [k_srbind_mback] = INPUT_BASIC( SDLK_ESCAPE, SDL_CONTROLLER_BUTTON_B ),
 [k_srbind_mopen] = INPUT_BASIC( SDLK_ESCAPE, SDL_CONTROLLER_BUTTON_START ),
+[k_srbind_mclose]= (vg_input_op[]){ vg_joy_button, SDL_CONTROLLER_BUTTON_START, vg_end },
 [k_srbind_mhub]  = INPUT_BASIC( SDLK_h, SDL_CONTROLLER_BUTTON_Y ),
 [k_srbind_maccept] = (vg_input_op[]){
    vg_keyboard, SDLK_e, vg_gui_visible, 0,
index b6fa1977078e4cb44f359387c557145e14db516d..b4abd2b635795aada419b75800291ed8f2c30273 100644 (file)
@@ -25,11 +25,52 @@ void menu_at_begin(void)
 
 void menu_init(void)
 {
-   vg_console_reg_var( "skip_starter_menu", &menu.skip_starter,
-                       k_var_dtype_i32, VG_VAR_PERSISTENT );
+   vg_console_reg_var( "skip_starter_menu", &menu.skip_starter, k_var_dtype_i32, VG_VAR_PERSISTENT );
    vg_tex2d_load_qoi_async_file( "textures/prem.qoi", 
-         VG_TEX2D_CLAMP|VG_TEX2D_NOMIP|VG_TEX2D_NEAREST,
-         &menu.prem_tex );
+         VG_TEX2D_CLAMP|VG_TEX2D_NOMIP|VG_TEX2D_NEAREST, &menu.prem_tex );
+}
+
+static void menu_update_world_filter(void)
+{
+   menu.world_list_whitelist = 0;
+   menu.world_list_blacklist = 0;//ADDON_REG_HIDDEN;
+
+   if( world_map.superworld_list_selected == k_superworld_campaign )
+      menu.world_list_whitelist = ADDON_REG_CAMPAIGN;
+   else if( world_map.superworld_list_selected == k_superworld_infinite )
+      menu.world_list_whitelist = ADDON_REG_INFINITE;
+   else if( world_map.superworld_list_selected == k_superworld_steam_workshop )
+      menu.world_list_whitelist = ADDON_REG_WORKSHOP;
+   else if( world_map.superworld_list_selected == k_superworld_venus_moon )
+      menu.world_list_whitelist = ADDON_REG_VENUS;
+}
+
+void menu_figure_out_where_to_start_world_select( addon_reg *addon_reg )
+{
+   if( addon_reg == NULL )
+      return;
+   
+   i32 super_id = 0;
+   if( addon_reg->flags & ADDON_REG_CAMPAIGN ) super_id = k_superworld_campaign;
+   if( addon_reg->flags & ADDON_REG_INFINITE ) super_id = k_superworld_infinite;
+   if( addon_reg->flags & ADDON_REG_WORKSHOP ) super_id = k_superworld_steam_workshop;
+   if( addon_reg->flags & ADDON_REG_VENUS    ) super_id = k_superworld_venus_moon;
+
+   world_map.superworld_actual_world = super_id;
+   world_map.superworld_list_selected = super_id;
+
+   menu_update_world_filter();
+   u32 index = get_index_from_addon( k_addon_type_world, addon_reg, 
+                                     menu.world_list_whitelist, menu.world_list_blacklist );
+   menu.world_list_selected_index[ super_id ] = (index == 0xffffffff)? 0: index;
+   menu.clicked_world_reg = addon_reg;
+
+   vg_msg msg;
+   vg_msg_init( &msg, addon_reg->metadata, addon_reg->metadata_len );
+   const char *name = vg_msg_getkvstr( &msg, "location" );
+   if( !name )
+      name = addon_reg->alias.foldername;
+   menu.clicked_world_name = name;
 }
 
 void menu_open( enum menu_page page )
@@ -62,8 +103,7 @@ static void menu_decor_select( ui_context *ctx, ui_rect rect )
 static void menu_standard_widget( ui_context *ctx,
                                   ui_rect inout_panel, ui_rect rect, ui_px s )
 {
-   ui_split( inout_panel, k_ui_axis_h, ctx->font->sy*s*2, 
-             8, rect, inout_panel );
+   ui_split( inout_panel, k_ui_axis_h, ctx->font->sy*s*2, 8, rect, inout_panel );
 }
 
 static bool menu_slider( ui_context *ctx,
@@ -309,20 +349,28 @@ static void menu_link_modal( const char *url )
       menu.web_choice = 0;
 }
 
-static void menu_update_world_list(void)
+void menu_update_world_list(void)
 {
-   menu.world_list_total_count = addon_count( k_addon_type_world, ADDON_REG_HIDDEN );
-
-   if( menu.world_list_selected_index >= menu.world_list_total_count )
-      vg_fatal_error( "World list index out of range! (%d >= %d)\n",
-            menu.world_list_selected_index, menu.world_list_total_count );
+   menu_update_world_filter();
+   menu.world_list_total_count = addon_count( k_addon_type_world, 
+                                              menu.world_list_whitelist, menu.world_list_blacklist );
+   menu.world_list_display_count = 0;
 
-   u32 selected_world_index = menu.world_list_selected_index,
-       page_base = (selected_world_index / MENU_WORLD_COUNT) * MENU_WORLD_COUNT;
+   i32 *selected_world_index = &menu.world_list_selected_index[ world_map.superworld_list_selected ];
+   if( menu.world_list_total_count )
+   {
+      if( *selected_world_index >= menu.world_list_total_count )
+      {
+         vg_fatal_error( "World list index out of range! (%d >= %d)\n",
+               *selected_world_index, menu.world_list_total_count );
+      }
+   }
 
-   menu.world_list_display_count = 0;
+   i32 max_display_count = menu.world_list_nominal_display_count;
+   if( max_display_count <= 0 ) max_display_count = 1;
 
-   for( u32 i=0; i<MENU_WORLD_COUNT; i ++ )
+   u32 page_base = (*selected_world_index / max_display_count) * max_display_count;
+   for( u32 i=0; i<max_display_count; i ++ )
    {
       u32 world_index = page_base + i;
 
@@ -330,7 +378,8 @@ static void menu_update_world_list(void)
          menu.world_list_entries[i] = NULL;
       else
       {
-         addon_reg *reg = get_addon_from_index( k_addon_type_world, world_index, ADDON_REG_HIDDEN );
+         addon_reg *reg = get_addon_from_index( k_addon_type_world, world_index, 
+                                                menu.world_list_whitelist, menu.world_list_blacklist );
          menu.world_list_entries[ menu.world_list_display_count ] = reg;
 
          vg_msg msg;
@@ -349,9 +398,9 @@ static void menu_update_world_list(void)
 
 void menu_gui( ui_context *ctx )
 {
-   if( button_down( k_srbind_mopen ) )
+   if( skaterift.activity == k_skaterift_default )
    {
-      if( skaterift.activity == k_skaterift_default )
+      if( button_down( k_srbind_mopen ) )
       {
          menu_open( k_menu_page_main );
          return;
@@ -361,6 +410,16 @@ void menu_gui( ui_context *ctx )
    if( skaterift.activity != k_skaterift_menu ) 
       return;
 
+   if( button_down( k_srbind_mclose ) )
+   {
+      audio_lock();
+      audio_oneshot( &audio_ui[3], 1.0f, 0.0f );
+      audio_unlock();
+      skaterift.activity = k_skaterift_default;
+      srinput.state = k_input_state_resume;
+      return;
+   }
+
    menu.bg_blur = 1;
    menu.bg_cam = NULL;
 
@@ -377,13 +436,9 @@ void menu_gui( ui_context *ctx )
    /* TAB CONTROL */
    u8 lb_down = 0, rb_down = 0;
    vg_exec_input_program( k_vg_input_type_button_u8, 
-         (vg_input_op[]){
-            vg_joy_button, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, vg_end 
-         }, &rb_down );
+         (vg_input_op[]){ vg_joy_button, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, vg_end }, &rb_down );
    vg_exec_input_program( k_vg_input_type_button_u8, 
-         (vg_input_op[]){
-            vg_joy_button, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, vg_end 
-         }, &lb_down );
+         (vg_input_op[]){ vg_joy_button, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, vg_end }, &lb_down );
 
    int mtab = (i32)rb_down - (i32)lb_down;
 
@@ -405,9 +460,7 @@ void menu_gui( ui_context *ctx )
       ui_capture_mouse(ctx, 1);
    }
 
-   if( skaterift.activity != k_skaterift_menu ) return;
-
-
+/* PAGE web link modal */
    if( menu.web_link )
    {
       //menu_try_find_cam( 3 );
@@ -489,6 +542,7 @@ void menu_gui( ui_context *ctx )
       return;
    }
 
+/* PAGE credits */
    if( menu.page == k_menu_page_credits )
    {
       ui_rect panel = { 0,0, 600, 400 },
@@ -535,6 +589,7 @@ void menu_gui( ui_context *ctx )
 
       goto menu_draw;
    }
+/* PAGE starter */
    else if( menu.page == k_menu_page_starter )
    {
       i32 R = menu_nav( &menu.intro_row, mv, 3 );
@@ -572,6 +627,7 @@ void menu_gui( ui_context *ctx )
       //menu_try_find_cam( 3 );
       goto menu_draw;
    }
+/* PAGE premium */
    else if( menu.page == k_menu_page_premium )
    {
       i32 R = menu_nav( &menu.prem_row, mh, 1 );
@@ -615,60 +671,25 @@ void menu_gui( ui_context *ctx )
 
       goto menu_draw;
    }
+#if 0
    else if( menu.page == k_menu_page_world_select )
    {
-      ctx->font = &vgf_default_large;
-
-      ui_rect left_world_list = { 0, 0, 300, vg.window_y };
-      ui_fill( ctx, left_world_list, ui_opacity( GUI_COL_DARK, 0.35f ) );
-
-      ui_rect title;
-      ui_split( left_world_list, k_ui_axis_h, 28, 0, title, left_world_list );
-      ui_text( ctx, title, "Locations", 1, k_ui_align_middle_center, 0 );
-
-      i32 selected_world_index = menu.world_list_selected_index,
-          page = selected_world_index / MENU_WORLD_COUNT,
-          page_base = page * MENU_WORLD_COUNT,
-          max_page = (menu.world_list_total_count-1) / MENU_WORLD_COUNT;
-
-      i32 R = menu_nav( &menu.world_list_selected_index, mv, menu.world_list_total_count-1 );
-
-
-
-      if( menu_button( ctx, left_world_list, R == -999, page>0, "\x94" ) )
-         menu.world_list_selected_index = (page-1)*MENU_WORLD_COUNT;
-
-      for( u32 i=0; i<menu.world_list_display_count; i ++ )
-      {
-         addon_reg *reg = menu.world_list_entries[i];
-         if( menu_button( ctx, left_world_list, R == (page_base + i), 1, menu.world_list_names[i] ) )
-         {
-         }
-      }
-      
-      if( menu_button( ctx, left_world_list, R == -999, page<max_page, "\x96" ) )
-         menu.world_list_selected_index = (page+1)*MENU_WORLD_COUNT;
-
-      if( menu.world_list_selected_index < page_base || 
-          menu.world_list_selected_index >= (page_base+MENU_WORLD_COUNT) )
-      {
-         menu_update_world_list();
-      }
-
-
-
 
       if( /*menu_button( ctx, end, 1, 1, "Back" ) ||*/ button_down( k_srbind_mback ) )
       {
          menu.page = k_menu_page_main;
       }
 
+      menu.bg_blur = 0;
       goto menu_draw;
    }
+#endif
 
    /*                              TOP BAR 
     * -------------------------------------------------------------------*/
 
+   VG_ASSERT( menu.page == k_menu_page_main );
+
    ctx->font = &vgf_default_title;
    ui_px height = ctx->font->ch + 16;
    ui_rect topbar = { 0, 0, vg.window_x, height };
@@ -736,8 +757,7 @@ void menu_gui( ui_context *ctx )
                ui_outline( ctx, box, 1, GUI_COL_ACTIVE, 0 );
             }
             else 
-               ui_fill( ctx, box, 
-                        i==menu.main_index? GUI_COL_ACTIVE: GUI_COL_NORM );
+               ui_fill( ctx, box, i==menu.main_index? GUI_COL_ACTIVE: GUI_COL_NORM );
 
             ui_text( ctx, box, opts[i], 1, k_ui_align_middle_center, 0 );
          }
@@ -757,176 +777,443 @@ void menu_gui( ui_context *ctx )
       }
 
       if( draw )
-         ui_fill( ctx,
-                  (ui_rect){ x+8,0, vg.window_x-(x+8),height }, GUI_COL_NORM );
+         ui_fill( ctx, (ui_rect){ x+8,0, vg.window_x-(x+8),height }, GUI_COL_NORM );
       else
       {
          x = vg.window_x/2 - x/2;
-         ui_fill( ctx,
-                  (ui_rect){ 0, 0, x-8, height }, GUI_COL_NORM );
+         ui_fill( ctx, (ui_rect){ 0, 0, x-8, height }, GUI_COL_NORM );
       }
    }
 
+   bool menu_backable_to_exit = 1;
+
+/* PAGE map */
    if( menu.main_index == k_menu_main_map )
    {
+      menu_backable_to_exit = (vg_input.display_input_method == k_input_method_kbm);
       menu.bg_blur = 0;
-      ctx->font = &vgf_default_large;
-      ui_rect title = { vg.window_x/2- 512/2, height+8, 512, 64 };
 
-      ui_px x = 8,
-            y = height+8;
+      ui_rect title_box = { vg.window_x/2 - 300/2, height + 8, 300, 100 };
 
-      struct ui_vert *vs = ui_fill( ctx, (ui_rect){ x,y, 0,height }, GUI_COL_DARK );
+      ui_rect upper_title, lower_title;
+      ui_split( title_box, k_ui_axis_h, title_box[3]-24, 0, upper_title, lower_title );
 
-      char buf[64];
-      vg_str str;
-      vg_strnull( &str, buf, sizeof(buf) );
+      const char *title_text = NULL,
+                 *subtitle_text = NULL;
 
-      world_instance *world = &_world.main;
-      const char *world_name = af_str( &world->meta.af, world->info.pstr_name );
-      vg_strnull( &str, buf, sizeof(buf) );
-      vg_strcat( &str, world_name );
-      
-      ui_px w = ui_text( ctx, (ui_rect){ x+8, y, 1000, height }, buf, 1,
-                         k_ui_align_middle_left, 0 );
+      if( world_map.activity == k_map_activity_local_world )
+      {
+         world_instance *world = &_world.main; 
+         title_text = af_str( &world->meta.af, world->info.pstr_name );
+         subtitle_text = "Current Location";
 
-      w *= ctx->font->sx;
-      x += w + 16;
+         ctx->font = &vgf_default_title;
+         upper_title[2] = ui_text_line_width(ctx,title_text) + 24;
+         upper_title[0] = (vg.window_x - upper_title[2])/2;
 
-      vs[1].co[0] = x + 8;
-      vs[2].co[0] = x;
 
-      x = 8;
-      y += 8 + height;
+         /* main title  (world location, world status)
+          * ---------------------------------------------------------------------
+          */
+         bool go_to_super = 0;
 
-      ui_rect stat_panel = { x,y, 256,vg.window_y-y };
-      u32 c0 = ui_opacity( GUI_COL_DARK, 0.36f );
-      vs = ui_fill( ctx, stat_panel, c0 );
+         ui_px back_box_size = 48;
 
-      ui_rect_pad( stat_panel, (ui_px[2]){8,0} );
+         ctx->font = &vgf_default_title;
+         ui_rect back_box = { upper_title[0] - (back_box_size + 8), title_box[1] + ((title_box[3]-back_box_size)/2), 
+                              back_box_size, back_box_size };
+         if( ui_button_text( ctx, back_box, "<", 1 ) == k_ui_button_click )
+         {
+            go_to_super = 1;
+         }
+
+         if( vg_input.display_input_method == k_input_method_controller )
+         {
+            char buf[32];
+            vg_str helper_text;
+            vg_strnull( &helper_text, buf, 32 );
+            vg_input_string( &helper_text, input_button_list[k_srbind_mback], 1 );
+            
+            ui_rect helper_label_box = { back_box[0] + (back_box[2]-32)/2, back_box[1] + back_box[3]-4, 32, 32 };
+            ui_fill( ctx, helper_label_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+            ui_text( ctx, helper_label_box, buf, 1, k_ui_align_middle_center, 0 );
+
+            if( button_down( k_srbind_mback ) )
+            {
+               audio_lock();
+               audio_oneshot( &audio_ui[3], 1.0f, 0.0f );
+               audio_unlock();
+               go_to_super = 1;
+            }
+         }
+
+         ctx->font = &vgf_default_title;
+         ui_rect super_location_box = { back_box[0] - (120+8), back_box[1], 120, 32 };
+         ui_text( ctx, super_location_box, "Earth", 1, k_ui_align_middle_center, ui_colour( ctx, k_ui_gray ) );
 
-      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;
+         if( go_to_super )
+         {
+            world_map.activity = k_map_activity_galaxy;
+            menu_update_world_list();
+         }
 
-         const char *title = af_str( &world->meta.af, region->pstr_title );
+#if 0
          ctx->font = &vgf_default_large;
+         ui_rect title = { vg.window_x/2- 512/2, height+8, 512, 64 };
+
+         ui_px x = 8,
+               y = height+8;
+
+         struct ui_vert *vs = ui_fill( ctx, (ui_rect){ x,y, 0,height }, GUI_COL_DARK );
+
+         char buf[64];
+         vg_str str;
+         vg_strnull( &str, buf, sizeof(buf) );
 
-         ui_rect title_box;
-         menu_standard_widget( ctx, stat_panel, title_box, 1 );
+         world_instance *world = &_world.main;
+         const char *world_name = af_str( &world->meta.af, world->info.pstr_name );
+         vg_strnull( &str, buf, sizeof(buf) );
+         vg_strcat( &str, world_name );
          
-         stat_panel[0] += 16;
-         stat_panel[2] -= 16;
-         ctx->font = &vgf_default_small;
+         ui_px w = ui_text( ctx, (ui_rect){ x+8, y, 1000, height }, buf, 1,
+                            k_ui_align_middle_left, 0 );
 
-         ent_volume *volume = af_arritm(&world->ent_volume,
-               mdl_entity_id_id(region->zone_volume));
+         w *= ctx->font->sx;
+         x += w + 16;
 
-         u32 combined = k_ent_route_flag_achieve_gold | 
-                        k_ent_route_flag_achieve_silver;
+         vs[1].co[0] = x + 8;
+         vs[2].co[0] = x;
 
-         char buf[128];
-         vg_str str;
+         x = 8;
+         y += 8 + height;
+
+         ui_rect stat_panel = { x,y, 256,vg.window_y-y };
+         u32 c0 = ui_opacity( GUI_COL_DARK, 0.36f );
+         vs = ui_fill( ctx, stat_panel, c0 );
+
+         ui_rect_pad( stat_panel, (ui_px[2]){8,0} );
 
-         for( u32 j=0; j<af_arrcount(&world->ent_route); j ++ )
+         for( u32 i=0; i<af_arrcount( &world->ent_region ); i ++ )
          {
-            ent_route *route = af_arritm(&world->ent_route, j );
+            ent_region *region = af_arritm( &world->ent_region, i );
 
-            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)) )
-            {
+            if( !region->zone_volume )
                continue;
+
+            const char *title = af_str( &world->meta.af, region->pstr_title );
+            ctx->font = &vgf_default_large;
+
+            ui_rect title_box;
+            menu_standard_widget( ctx, stat_panel, title_box, 1 );
+            
+            stat_panel[0] += 16;
+            stat_panel[2] -= 16;
+            ctx->font = &vgf_default_small;
+
+            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;
+
+            char buf[128];
+            vg_str str;
+
+            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)) )
+               {
+                  continue;
+               }
+
+               combined &= route->flags;
+
+               vg_strnull( &str, buf, sizeof(buf) );
+               vg_strcat( &str, "(Race) " );
+               vg_strcat( &str, af_str( &world->meta.af, route->pstr_name ));
+
+               if( route->flags & k_ent_route_flag_achieve_silver )
+                  vg_strcat( &str, " \xb3");
+               if( route->flags & k_ent_route_flag_achieve_gold )
+                  vg_strcat( &str, "\xb3");
+
+               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, route->flags ) );
+            }
+
+            for( u32 j=0; j<af_arrcount(&world->ent_challenge); j ++ )
+            {
+               ent_challenge *challenge = af_arritm( &world->ent_challenge, j );
+
+               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)) )
+               {
+                  continue;
+               }
+
+               vg_strnull( &str, buf, sizeof(buf) );
+               vg_strcat( &str, af_str( &world->meta.af,challenge->pstr_alias));
+
+               u32 flags = 0x00;
+               if( challenge->status )
+               {
+                  flags |= k_ent_route_flag_achieve_gold;
+                  flags |= k_ent_route_flag_achieve_silver;
+                  vg_strcat( &str, " \xb3\xb3" );
+               }
+
+               combined &= flags;
+
+               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 ) );
             }
 
-            combined &= route->flags;
+            stat_panel[0] -= 16;
+            stat_panel[2] += 16;
+
+            u32 title_col = 0;
+            if( combined & k_ent_route_flag_achieve_gold ) 
+               title_col = ui_colour( ctx, k_ui_yellow );
+            else if( combined & k_ent_route_flag_achieve_silver )
+               title_col = ui_colour( ctx, k_ui_fg );
+
+            menu_heading( ctx, title_box, title, title_col );
+         }
+
+         vs[2].co[1] = stat_panel[1];
+         vs[3].co[1] = stat_panel[1];
 
-            vg_strnull( &str, buf, sizeof(buf) );
-            vg_strcat( &str, "(Race) " );
-            vg_strcat( &str, af_str( &world->meta.af, route->pstr_name ));
+         ui_rect back_button = {0,0,300,32};
+         ui_rect_center( (ui_rect){0,0,vg.window_x,vg.window_y}, back_button );
+         if( ui_button_text( ctx, back_button, "up", 1 ) == k_ui_button_click )
+         {
+            world_map.activity = k_map_activity_
+         }
+#endif
+      }
+      else if( world_map.activity >= k_map_activity_galaxy )
+      {
+         ui_rect panel = { 24, height+24, WORKSHOP_PREVIEW_WIDTH+8, vg.window_y-(height+24+24) };
+         ui_fill( ctx, panel, ui_opacity( GUI_COL_DARK, 0.35f ) );
 
-            if( route->flags & k_ent_route_flag_achieve_silver )
-               vg_strcat( &str, " \xb3");
-            if( route->flags & k_ent_route_flag_achieve_gold )
-               vg_strcat( &str, "\xb3");
 
-            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, route->flags ) );
+         title_text = menu.clicked_world_name? menu.clicked_world_name: "...";
+         subtitle_text = (menu.clicked_world_reg == _world.main.addon)? "Current Location": NULL;
+
+         ctx->font = &vgf_default_title;
+         upper_title[2] = ui_text_line_width(ctx,title_text) + 24;
+         upper_title[0] = panel[0]+panel[2] + ((vg.window_x - (panel[0]+panel[2])) - upper_title[2])/2;
+         lower_title[0] = panel[0]+panel[2] + 24;
+         lower_title[2] = (vg.window_x - (lower_title[0]+24));
+
+
+         /* planet box
+          * -------------------------------------------------------------------------------------------------------- */
+         ui_rect planet_box;
+         ui_split( panel, k_ui_axis_h, WORKSHOP_PREVIEW_HEIGHT, 8, planet_box, panel );
+
+         planet_box[0] += 4;
+         planet_box[2] = WORKSHOP_PREVIEW_WIDTH;
+         planet_box[3] = WORKSHOP_PREVIEW_HEIGHT;
+         ui_image( ctx, planet_box, &g_render.fb_workshop_preview->attachments[0].id );
+
+         ui_rect planet_box_title;
+         ui_split( planet_box, k_ui_axis_h, 28, 0, planet_box_title, planet_box );
+         ui_fill( ctx, planet_box_title, ui_opacity( GUI_COL_DARK, 0.2f ) );
+         ctx->font = &vgf_default_large;
+         ui_text( ctx, planet_box_title, _superworld_names[world_map.superworld_list_selected], 1, 
+                  k_ui_align_middle_center, ui_colour(ctx,k_ui_yellow) );
+
+         ui_px change_w = 48,
+               change_h = 160,
+               change_y = planet_box[1] + (planet_box[3]-80)/2;
+
+         /* << */
+         bool superworld_changed = 0;
+         ui_rect change_box_l = { panel[0], change_y, change_w, change_h };
+         if( world_map.superworld_actual_world < world_map.superworld_list_selected )
+         {
+            ui_rect helper_box = { change_box_l[0], planet_box_title[1]+planet_box_title[3], 300, 24 };
+            ctx->font = &vgf_default_small;
+            ui_text( ctx, helper_box, "<< Current location", 1, k_ui_align_middle_left, ui_colour(ctx,k_ui_yellow) );
+         }  
+         ctx->font = &vgf_default_title;
+         if( menu_button( ctx, change_box_l, 0, world_map.superworld_list_selected > 0, "<" ) )
+         {
+            world_map.superworld_list_selected --;
+            superworld_changed = 1;
+         }
+
+         /* >> */
+         ui_rect change_box_r = { (panel[0]+panel[2]) - change_w, change_y, change_w, change_h };
+         if( world_map.superworld_actual_world > world_map.superworld_list_selected )
+         {
+            ui_rect helper_box = { change_box_r[0] - 300, planet_box_title[1]+planet_box_title[3], 300, 24 };
+            ctx->font = &vgf_default_small;
+            ui_text( ctx, helper_box, "Current location >>", 1, k_ui_align_middle_right, ui_colour(ctx,k_ui_yellow) );
+         }  
+         ctx->font = &vgf_default_title;
+         if( menu_button( ctx, change_box_r, 0, world_map.superworld_list_selected < k_superworld_max-1, ">" ) )
+         {
+            world_map.superworld_list_selected ++;
+            superworld_changed = 1;
          }
 
-         for( u32 j=0; j<af_arrcount(&world->ent_challenge); j ++ )
+         /* controller input */
+         if( mh ) 
          {
-            ent_challenge *challenge = af_arritm( &world->ent_challenge, j );
+            i32 s = world_map.superworld_list_selected - mh;
+            if( s < 0 ) s = 0;
+            if( s > k_superworld_max -1 ) s = k_superworld_max -1;
 
-            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)) )
+            if( s != world_map.superworld_list_selected )
             {
-               continue;
+               world_map.superworld_list_selected = s;
+               superworld_changed = 1;
             }
+         }
 
-            vg_strnull( &str, buf, sizeof(buf) );
-            vg_strcat( &str, af_str( &world->meta.af,challenge->pstr_alias));
+         /* World list
+          * -------------------------------------------------------------------------------------------- */
+         ui_rect title;
+         ui_split( panel, k_ui_axis_h, 28, 0, title, panel );
+         ctx->font = &vgf_default_large;
+         ui_text( ctx, title, "Locations", 1, k_ui_align_middle_center, 0 );
+
+         i32 nominal_count = ((panel[3]+8) / (ctx->font->sy*2 + 8)) - 2;
+         if( nominal_count > MENU_WORLD_MAX_COUNT )
+            nominal_count = MENU_WORLD_MAX_COUNT;
+         if( nominal_count <= 0 )
+            nominal_count = 1;
+
+         if( superworld_changed || (nominal_count != menu.world_list_nominal_display_count) )
+         {
+            menu.world_list_nominal_display_count = nominal_count;
+            menu_update_world_list();
+         }
+
+         i32 *selected_world_index = &menu.world_list_selected_index[ world_map.superworld_list_selected ],
+             page = (*selected_world_index) / menu.world_list_nominal_display_count,
+             page_base = page * menu.world_list_nominal_display_count,
+             max_page = (menu.world_list_total_count-1) / menu.world_list_nominal_display_count;
+
+         i32 R = menu_nav( selected_world_index, mv, menu.world_list_total_count-1 );
+
+
+         if( menu_button( ctx, panel, R == -999, page>0, "\x94" ) )
+            *selected_world_index = (page-1)*menu.world_list_nominal_display_count;
+
+         for( u32 i=0; i<menu.world_list_display_count; i ++ )
+         {
+            addon_reg *reg = menu.world_list_entries[i];
+
+            bool selected = reg == menu.clicked_world_reg;
+            
+            if( selected )
+            {
+               ui_rect sel_box, _null;
+               ui_split( panel, k_ui_axis_h, ctx->font->sy*2, 8, sel_box, _null );
+               ui_outline( ctx, sel_box, 2, ui_colour(ctx,k_ui_fg), 0 );
+            }
 
-            u32 flags = 0x00;
-            if( challenge->status )
+            if( menu_button( ctx, panel, R == (page_base + i), !selected, menu.world_list_names[i] ) )
             {
-               flags |= k_ent_route_flag_achieve_gold;
-               flags |= k_ent_route_flag_achieve_silver;
-               vg_strcat( &str, " \xb3\xb3" );
+               menu.clicked_world_reg = reg;
+               menu.clicked_world_name = menu.world_list_names[i];
             }
+         }
+         
+         if( menu_button( ctx, panel, R == -999, page<max_page, "\x96" ) )
+            *selected_world_index = (page+1)*menu.world_list_nominal_display_count;
 
-            combined &= flags;
+         if( (*selected_world_index < page_base) || 
+             (*selected_world_index >= (page_base+menu.world_list_nominal_display_count)) )
+         {
+            menu_update_world_list();
+         }
 
-            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 ) );
+         if( (_world.loader_state == k_world_loader_done) && (menu.clicked_world_reg) )
+         {
+            bool do_load = 0;
+            
+            if( _world.preview_instance.complete ) 
+            {
+               if( _world.preview_instance.addon != menu.clicked_world_reg )
+                  do_load = 1;
+            }
+            else
+               do_load = 1;
+
+            if( do_load )
+               skaterift_load_world_start( menu.clicked_world_reg, 1 );
          }
 
-         stat_panel[0] -= 16;
-         stat_panel[2] += 16;
+         ctx->font = &vgf_default_title;
+         ui_px remainder = vg.window_x - (panel[0] + panel[2]),
+               width     = 400,
+               height    = ctx->font->sy*2;
+
+         ui_rect enter_world_box = { panel[0] + panel[2] + (remainder-width)/2, vg.window_y - (height + 24),
+                                     width, height };
+
+         char buf[64];
+         vg_str enter_helper;
+         vg_strnull( &enter_helper, buf, sizeof(buf) );
+         vg_strcat( &enter_helper, "Enter location " );
+         if( vg_input.display_input_method == k_input_method_controller )
+            vg_input_string( &enter_helper, input_button_list[k_srbind_mhub], 1 );
 
-         u32 title_col = 0;
-         if( combined & k_ent_route_flag_achieve_gold ) 
-            title_col = ui_colour( ctx, k_ui_yellow );
-         else if( combined & k_ent_route_flag_achieve_silver )
-            title_col = ui_colour( ctx, k_ui_fg );
+         bool enter_location = 0;
+         if( menu_button( ctx, enter_world_box, 0, menu.clicked_world_reg?1:0, buf ) )
+            enter_location = 1;
 
-         menu_heading( ctx, title_box, title, title_col );
+         if( button_down( k_srbind_mhub ) )
+            enter_location = 1;
+
+         if( enter_location && menu.clicked_world_reg )
+         {
+            if( menu.clicked_world_reg != _world.main.addon )
+               skaterift_load_world_start( menu.clicked_world_reg, 0 );
+
+            world_map.activity = k_map_activity_local_world;
+            world_map.superworld_actual_world = world_map.superworld_list_selected;
+         }
       }
 
-      vs[2].co[1] = stat_panel[1];
-      vs[3].co[1] = stat_panel[1];
-   }
-   else
-   {
-      if( button_down( k_srbind_mback ) )
+      /* Render title & subtitle 
+       * ---------------------------------------------------------------------------------------------------
+       */
+
+      ctx->font = &vgf_default_title;
+      ui_text( ctx, upper_title, title_text, 1, k_ui_align_middle_center, ui_colour( ctx, k_ui_yellow ) );
+
+      if( subtitle_text )
       {
-         audio_lock();
-         audio_oneshot( &audio_ui[3], 1.0f, 0.0f );
-         audio_unlock();
-         skaterift.activity = k_skaterift_default;
-         return;
+         ctx->font = &vgf_default_large;
+         ui_text( ctx, lower_title, subtitle_text, 1, k_ui_align_middle_center, 0 );
       }
-
-      ui_rect list0 = { vg.window_x/2 - 512/2, height+32, 
-                        512, vg.window_y-height-64 }, list;
+   }
+   else /* menu.main_index != k_menu_main_map) */
+   {
+      ui_rect list0 = { vg.window_x/2 - 512/2, height+32, 512, vg.window_y-height-64 }, list;
       rect_copy( list0, list );
       ui_rect_pad( list, (ui_px[2]){8,8} );
 
-      /*                              MAIN / MAIN
-       * -------------------------------------------------------------------*/
-
+/* PAGE main */
       if( menu.main_index == k_menu_main_main )
       {
          i32 R = menu_nav( &menu.main_row, mv, 2 );
@@ -937,23 +1224,18 @@ void menu_gui( ui_context *ctx )
             return;
          }
 
-         if( menu_button( ctx, list, R == 1, 1, "Change World" ) )
-         {
-            menu.page = k_menu_page_world_select;
-            menu_update_world_list();
-         }
-
-         if( menu_button( ctx, list, R == 2, 1, "Credits" ) )
+         if( menu_button( ctx, list, R == 1, 1, "Credits" ) )
          {
             menu.page = k_menu_page_credits;
          }
 
          ui_rect end = { list[0], list[1]+list[3]-64, list[2], 72 };
-         if( menu_button( ctx, end, R == 3, 1, "Quit Game" ) )
+         if( menu_button( ctx, end, R == 2, 1, "Quit Game" ) )
          {
             vg.window_should_close = 1;
          }
       }
+/* PAGE settings */
       else if( menu.main_index == k_menu_main_settings )
       {
          ui_fill( ctx, list0, ui_opacity( GUI_COL_DARK, 0.36f ) );
@@ -992,6 +1274,7 @@ void menu_gui( ui_context *ctx )
             vg_settings_open();
          }
       }
+/* PAGE guide */
       else if( menu.main_index == k_menu_main_guide )
       {
          list0[0] = 8;
@@ -1114,6 +1397,15 @@ void menu_gui( ui_context *ctx )
          }
       }
    }
+   
+   if( button_down( k_srbind_mback ) && menu_backable_to_exit )
+   {
+      audio_lock();
+      audio_oneshot( &audio_ui[3], 1.0f, 0.0f );
+      audio_unlock();
+      srinput.state = k_input_state_resume;
+      skaterift.activity = k_skaterift_default;
+   }
 
 menu_draw:
 
index be5cbbe6daeeef8f71b3a7a7839e1b7b5eb753a8..05f59287df7ecc13d3ab131dadaedf5c0c2c6840 100644 (file)
@@ -1,10 +1,11 @@
 #pragma once
 
 #define MENU_STACK_SIZE 8
-#define MENU_WORLD_COUNT 4
+#define MENU_WORLD_MAX_COUNT 10
 
 #include "vg/vg_engine.h"
 #include "entity.h"
+#include "world_map.h"
 
 enum menu_page
 {
@@ -14,7 +15,6 @@ enum menu_page
    k_menu_page_main,
    k_menu_page_credits,
    k_menu_page_help,
-   k_menu_page_world_select
 };
 
 enum menu_main_subpage
@@ -49,12 +49,18 @@ struct global_menu
 
    GLuint prem_tex;
 
-   addon_reg *world_list_entries[ MENU_WORLD_COUNT ];
-   const char *world_list_names[ MENU_WORLD_COUNT ];
+   addon_reg *world_list_entries[ MENU_WORLD_MAX_COUNT ];
+   const char *world_list_names[ MENU_WORLD_MAX_COUNT ];
 
    i32 world_list_display_count, 
        world_list_total_count,
-       world_list_selected_index;
+       world_list_selected_index[ k_superworld_max ],
+       world_list_nominal_display_count;
+   u32 world_list_whitelist,
+       world_list_blacklist;
+
+   addon_reg *clicked_world_reg;
+   const char *clicked_world_name;
 }
 extern menu;
 
@@ -63,3 +69,6 @@ void menu_at_begin(void);
 void menu_gui( ui_context *ctx );
 void menu_open( enum menu_page page );
 bool menu_viewing_map(void);
+bool menu_viewing_preview(void);
+void menu_update_world_list(void);
+void menu_figure_out_where_to_start_world_select( addon_reg *addon_reg );
index 473c14715c47221dfe5d1950f396c33b65662014..9e3f0c98b1e5238fa25da647059395d5a5e11167 100644 (file)
@@ -209,7 +209,7 @@ void player__pass_gate( u32 id )
                   _world.nonlocal_destination_key, 32, 
                   k_strncpy_overflow_fatal );
 
-      addon_reg *reg = get_addon_from_index( k_addon_type_world, gate->addon_reg, 0 );
+      addon_reg *reg = get_addon_from_index( k_addon_type_world, gate->addon_reg, 0,0 );
       skaterift_load_world_start( reg, 0 );
       return;
    }
index 432f3418d42a530b27895d8d16c8a52c9e334018..4b4f125938312e3368ab89c770e7773ba610b13b 100644 (file)
@@ -212,8 +212,8 @@ void skaterift_load_mainsave(void)
    vg_msg_getkvintg( &kvsav, "ach", k_vg_msg_u32, &ach, NULL );
    skaterift.achievements |= ach;
 
-   u32 board_reg_id = time(NULL) % addon_count( k_addon_type_board, 0 ),
-       player_reg_id = (time(NULL)+44) % addon_count( k_addon_type_player, 0 );
+   u32 board_reg_id = time(NULL) % addon_count( k_addon_type_board, 0,0 ),
+       player_reg_id = (time(NULL)+44) % addon_count( k_addon_type_player, 0,0 );
 
    vg_msg_cursor orig = kvsav.cur;
    if( vg_msg_seekframe( &kvsav, "player" ) )
@@ -244,7 +244,7 @@ void skaterift_load_mainsave(void)
          reg_id = addon_match( &q );
          if( reg_id != 0xffffffff )
          {
-            _world.loader_reg = get_addon_from_index( k_addon_type_world, reg_id, 0 );
+            _world.loader_reg = get_addon_from_index( k_addon_type_world, reg_id, 0,0 );
          }
          else
          {
index 590282bcd61c0ae9cdc219c5672ef70bc0e1efea..e4cb625ba5be8bbdc6b266721b8952073db7401b 100644 (file)
@@ -77,12 +77,12 @@ static void skaterift_load_world_content(void)
 {
    /* hub world */
    _world.default_hub_addon = skaterift_mount_world_unloadable( "maps/dev_hub", 0 );
-   skaterift_mount_world_unloadable( "maps/dev_heaven", ADDON_REG_HIDDEN );
-   skaterift_mount_world_unloadable( "maps/mp_spawn", ADDON_REG_CITY|ADDON_REG_PREMIUM );
-   skaterift_mount_world_unloadable( "maps/mp_mtzero", ADDON_REG_MTZERO|ADDON_REG_PREMIUM );
-   skaterift_mount_world_unloadable( "maps/dev_tutorial", 0 );
+   skaterift_mount_world_unloadable( "maps/dev_heaven",  ADDON_REG_CAMPAIGN | ADDON_REG_HIDDEN );
+   skaterift_mount_world_unloadable( "maps/mp_spawn",    ADDON_REG_CAMPAIGN | ADDON_REG_CITY|ADDON_REG_PREMIUM );
+   skaterift_mount_world_unloadable( "maps/mp_mtzero",   ADDON_REG_CAMPAIGN | ADDON_REG_MTZERO|ADDON_REG_PREMIUM );
+   skaterift_mount_world_unloadable( "maps/dev_tutorial",ADDON_REG_CAMPAIGN );
    skaterift_mount_world_unloadable( "maps/dev_flatworld", ADDON_REG_HIDDEN );
-   skaterift_mount_world_unloadable( "maps/mp_line1", ADDON_REG_PREMIUM );
+   skaterift_mount_world_unloadable( "maps/mp_line1",    ADDON_REG_CAMPAIGN | ADDON_REG_PREMIUM );
 }
 
 static void skaterift_load_player_content(void)
@@ -119,6 +119,7 @@ void game_load(void)
    vg_loader_step( addon_system_init, NULL );
    vg_loader_step( workshop_init, NULL );
    vg_loader_step( skateshop_init, NULL );
+   vg_loader_step( world_map_init, NULL );
    vg_loader_step( ent_tornado_init, NULL );
    vg_loader_step( skaterift_replay_init, NULL );
    vg_loader_step( skaterift_load_player_content, NULL );
@@ -152,8 +153,10 @@ void game_load(void)
       vg_warn( "Falling back to default hub world...\n" );
       _world.loader_reg = _world.default_hub_addon;
    }
+   menu_figure_out_where_to_start_world_select( _world.loader_reg );
    world_switcher_thread( NULL );
 
+
    /* add autosave function to exit list */
    vg_loader_step( NULL, skaterift_autosave_synchronous );
 }
@@ -292,7 +295,7 @@ static void render_player_transparent(void)
    small_cam.farz  = 60.0f;
 
    vg_camera_update_view( &small_cam );
-   vg_camera_update_projection( &small_cam );
+   vg_camera_update_projection( &small_cam, vg.window_x, vg.window_y );
    vg_camera_finalize( &small_cam );
 
    /* Draw player to window buffer and blend background ontop */
@@ -304,59 +307,6 @@ static void render_player_transparent(void)
 
 static void render_scene(void)
 {
-   /* Draw world */
-   glEnable( GL_DEPTH_TEST );
-
-   world_prerender( &_world.main );
-
-   if( menu_viewing_map() )
-   {
-      world_instance *world = &_world.main;
-      glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } );
-      
-      v3f bg;
-      v3_muls( world->ub_lighting.g_daysky_colour,
-                  world->ub_lighting.g_day_phase - 
-                  world->ub_lighting.g_sunset_phase*0.1f, bg );
-
-      v3_muladds( bg, world->ub_lighting.g_sunset_colour,
-                  (1.0f-0.5f)*world->ub_lighting.g_sunset_phase, bg );
-
-      v3_muladds( bg, world->ub_lighting.g_nightsky_colour,
-                  (1.0f-world->ub_lighting.g_day_phase), bg );
-
-      glClearColor( bg[0], bg[1], bg[2], 0.0f );
-      glClear( GL_COLOR_BUFFER_BIT );
-      glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, 
-                                    GL_COLOR_ATTACHMENT1 } );
-
-      m4x3f identity;
-      m4x3_identity( identity );
-      render_world_override( world, world, identity, &g_render.cam, 
-                             world_map.close_spawn, 
-                             (v4f){world->tar_min, world->tar_max, 1.0f, 0.0f});
-      render_world_routes( world, world, identity, &g_render.cam, 0, 1 );
-      return;
-   }
-   
-   if( _world.preview_instance.complete )
-   {
-      render_world_preview();
-   }
-   else
-      render_world( &_world.main, &g_render.cam, 0, 0, 1, 1 );
-
-   particle_system_update( &particles_grind, vg.time_delta );
-   //particle_system_debug( &particles_grind );
-   particle_system_prerender( &particles_grind );
-   particle_system_render( &particles_grind, &g_render.cam );
-   
-   ent_tornado_pre_update();
-   particle_system_update( &particles_env, vg.time_delta );
-   particle_system_prerender( &particles_env );
-   particle_system_render( &particles_env, &g_render.cam );
-
-   player_glide_render_effects( &g_render.cam );
 }
 
 m4x3f *_TEMP_VAR = NULL;
@@ -400,17 +350,6 @@ static void skaterift_composite_maincamera(void)
       g_render.cam.farz = 3100.0f;
    }
 
-   if( global_miniworld.transition ){
-      f32 dt = vg.time_frame_delta / 2.0f,
-          s  = vg_signf( global_miniworld.transition );
-      global_miniworld.t += s * dt;
-
-      if( (global_miniworld.t > 1.0f) || (global_miniworld.t < 0.0f) ){
-         global_miniworld.t = vg_clampf( global_miniworld.t, 0.0f, 1.0f );
-         global_miniworld.transition = 0;
-      }
-   }
-
    ent_camera *cs_cam = _cutscene_active_camera();
    if( cs_cam )
       ent_camera_unpack( cs_cam, &g_render.cam );
@@ -423,7 +362,7 @@ static void skaterift_composite_maincamera(void)
       _TEMP_VAR = NULL;
    }
    vg_camera_update_view( &g_render.cam );
-   vg_camera_update_projection( &g_render.cam );
+   vg_camera_update_projection( &g_render.cam, vg.window_x, vg.window_y );
    vg_camera_finalize( &g_render.cam );
 }
 
@@ -454,8 +393,9 @@ static void render_main_game(void)
 
    skaterift_composite_maincamera();
 
-   /* --------------------------------------------------------------------- */
-   if( !menu_viewing_map() )
+   bool render_actual_game = !menu_viewing_map();
+
+   if( render_actual_game )
    {
       world_instance *world = &_world.main;
       render_world_cubemaps( world );
@@ -466,33 +406,68 @@ static void render_main_game(void)
    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );
 
-   render_scene();
+   if( render_actual_game )
+   {
+      /* Draw world */
+      glEnable( GL_DEPTH_TEST );
+      world_prerender( &_world.main );
+
+#if 0
+      if( menu_viewing_preview() )
+      {
+         glClearColor( 0.1f, 0.1f, 0.2f, 0.0f );
+         glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+         if( _world.preview_instance.complete )
+            render_world_preview();
+
+         return;
+      }
+#endif
+
+      render_world( &_world.main, &g_render.cam, 0, 0, 1, 1 );
+
+      particle_system_update( &particles_grind, vg.time_delta );
+      //particle_system_debug( &particles_grind );
+      particle_system_prerender( &particles_grind );
+      particle_system_render( &particles_grind, &g_render.cam );
+      
+      ent_tornado_pre_update();
+      particle_system_update( &particles_env, vg.time_delta );
+      particle_system_prerender( &particles_env );
+      particle_system_render( &particles_env, &g_render.cam );
+
+      player_glide_render_effects( &g_render.cam );
+   }
+   else
+   {
+      render_world_map();
+   }
+
    glEnable( GL_DEPTH_TEST );
 
    /* full res target */
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    glViewport( 0,0, vg.window_x, vg.window_y );
 
-   render_player_transparent(); /* needs to read the depth buffer before we fuck
-                                   it up with the oblique rendering inside the
-                                   portals */
-
-   /* continue with variable rate */
-   if( !global_miniworld.transition && !menu_viewing_map() )
+   if( render_actual_game )
    {
+      render_player_transparent(); /* needs to read the depth buffer before we fuck
+                                      it up with the oblique rendering inside the
+                                      portals */
+
+      /* continue with variable rate */
       vg_framebuffer_bind( g_render.fb_main, k_render_scale );
       render_world_gates( &_world.main, &g_render.cam );
    }
 
    /* composite */
-
    if( (skaterift.activity == k_skaterift_menu) && menu.bg_blur )
-      v2_muls( (v2f){ 0.04f, 0.001f }, 1.0f-skaterift.time_rate, 
-               g_render.blur_override );
+      v2_muls( (v2f){ 0.04f, 0.001f }, 1.0f-skaterift.time_rate, g_render.blur_override );
    else 
       v2_zero( g_render.blur_override );
-   postprocess_to_screen( g_render.fb_main );
 
+   postprocess_to_screen( g_render.fb_main );
    skaterift_replay_post_render();
    control_overlay_render();
 }
@@ -560,6 +535,10 @@ void vg_gui( ui_context *ctx )
    }
 }
 
+void vg_framebuffer_resize( int w, int h )
+{
+}
+
 #include "addon.c"
 #include "addon_types.c"
 #include "audio.c"
index 6399c469bd0c967eb7e043b8af783a0e07eddb2f..8b27a425e3e7bb9b7df62adb7d1b1821ef575aa4 100644 (file)
@@ -961,7 +961,7 @@ static void workshop_render_player_preview(void)
    
    vg_camera_update_transform( &cam );
    vg_camera_update_view( &cam );
-   vg_camera_update_projection( &cam );
+   vg_camera_update_projection( &cam, WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT );
    vg_camera_finalize( &cam );
 
    render_playermodel( &cam, &_world.main, 0, 
@@ -1035,7 +1035,7 @@ static void workshop_render_board_preview(void)
    
    vg_camera_update_transform( &cam );
    vg_camera_update_view( &cam );
-   vg_camera_update_projection( &cam );
+   vg_camera_update_projection( &cam, WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT );
    vg_camera_finalize( &cam );
 
    m4x3f mmdl, mmdl1;
index 3c5eed218639bec54eecf90fac23e6e70a32028a..7ded96b18db6a7200eee03651dc37ee426335b8f 100644 (file)
@@ -211,7 +211,7 @@ void world_gen_entities_init( world_instance *world )
          {
             if( gate->addon_reg != 0xffffffff )
             {
-               addon_reg *reg = get_addon_from_index( k_addon_type_world, gate->addon_reg, 0 );
+               addon_reg *reg = get_addon_from_index( k_addon_type_world, gate->addon_reg, 0,0 );
                
                char cubemap_path[256];
                nonlocal_gate_cubemap_path( reg, af_str( &world->meta.af, gate->key ), cubemap_path );
index f8fb4fbd1094285df5420cf1727340076a6f4d83..d7faf6fa0a774183405b65bc3e791b836728fc0a 100644 (file)
@@ -147,7 +147,7 @@ int render_gate( world_instance *world, world_instance *world_inside,
 
    m4x3_mul( gate->transport, cam->transform, world_gates.cam.transform );
    vg_camera_update_view( &world_gates.cam );
-   vg_camera_update_projection( &world_gates.cam );
+   vg_camera_update_projection( &world_gates.cam, vg.window_x, vg.window_y );
 
    /* Add special clipping plane to projection */
    v4f surface;
@@ -408,7 +408,7 @@ void world_link_gates_async( void *payload, u32 size )
                      
                      if( !(_world.copy_of_nonlocal_sender.flags & k_ent_gate_no_linkback) )
                      {
-                        gate->addon_reg = get_index_from_addon( k_addon_type_world, _world.previous_world_addon );
+                        gate->addon_reg = get_index_from_addon( k_addon_type_world, _world.previous_world_addon,0,0 );
 
                         if( gate->addon_reg != 0xffffffff )
                         {
index ba69fcce786e3200cb6f3b9bd974bb296aa83f16..bb8755112cbf29a99590b28fc3f667db07a05583 100644 (file)
@@ -46,8 +46,7 @@ static void world_instance_load_mdl( world_instance *world, const char *path, vo
 
          for( u32 i=0; i<af_arrcount(&legacy_cameras); i ++ )
          {
-            fix_ent_camera_v107( af_arritm( &legacy_cameras, i ), 
-                                 af_arritm( &world->ent_camera, i ) );
+            fix_ent_camera_v107( af_arritm( &legacy_cameras, i ), af_arritm( &world->ent_camera, i ) );
          }
       }
 #endif
@@ -57,7 +56,12 @@ static void world_instance_load_mdl( world_instance *world, const char *path, vo
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_route_node,ent_route_node, heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_path_index,ent_path_index, heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_checkpoint,ent_checkpoint, heap );
-      AF_LOAD_ARRAY_STRUCT( af, &world->ent_route,     ent_route,      heap );
+   }
+
+   AF_LOAD_ARRAY_STRUCT( af, &world->ent_route,     ent_route,      heap );
+
+   if( load_all )
+   {
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_water,     ent_water,      heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_audio_clip,ent_audio_clip, heap );
       AF_LOAD_ARRAY_STRUCT( af, &world->ent_audio,     ent_audio,      heap );
@@ -279,7 +283,8 @@ void skaterift_world_load_thread( void *_ )
    }
    else
    {
-      vg_fatal_error( "No .mdl files found in the map folder.\n" );
+      vg_error( "No .mdl files found in the map folder!\n" );
+      strcpy( mdl_path, "models/world_error.mdl" );
    }
 
    world_instance_load_mdl( _world.loader_instance, mdl_path, _world.loader_heap );
@@ -519,7 +524,8 @@ int skaterift_load_world_command( int argc, const char *argv[] )
       u32 reg_id = addon_match( &q );
       if( reg_id != 0xffffffff )
       {
-         addon_reg *reg = get_addon_from_index( k_addon_type_world, reg_id, 0 );
+         addon_reg *reg = get_addon_from_index( k_addon_type_world, reg_id, 0,0 );
+         menu_figure_out_where_to_start_world_select( reg );
          skaterift_load_world_start( reg, preview );
       }
       else 
@@ -531,9 +537,9 @@ int skaterift_load_world_command( int argc, const char *argv[] )
    {
       vg_info( "worlds availible to load:\n" );
          
-      for( int i=0; i<addon_count(k_addon_type_world,0); i ++ )
+      for( int i=0; i<addon_count(k_addon_type_world,0,0); i ++ )
       {
-         addon_reg *w = get_addon_from_index( k_addon_type_world, i, 0);
+         addon_reg *w = get_addon_from_index( k_addon_type_world, i, 0,0);
 
          char buf[ADDON_UID_MAX];
          addon_alias_uid( &w->alias, buf );
index 9752533dd6bda90c1797ce2ac052a1db28fab2f3..ced1cbaba7de7aaee4b8a2139c110a6bdfd034b9 100644 (file)
@@ -5,6 +5,7 @@
 #include "gui.h"
 #include "menu.h"
 #include "scene.h"
+#include "shaders/model_superworld.h"
 
 struct world_map world_map;
 
@@ -40,8 +41,7 @@ static void respawn_world_to_plane_pos( v3f pos, v2f plane_pos )
    plane_pos[1] = co[2];
 }
 
-static void respawn_map_draw_icon( vg_camera *cam, 
-                                   enum gui_icon icon, v3f pos, f32 size )
+static void respawn_map_draw_icon( vg_camera *cam, enum gui_icon icon, v3f pos, f32 size )
 {
    v4f v;
    v3_copy( pos, v );
@@ -54,6 +54,7 @@ static void respawn_map_draw_icon( vg_camera *cam,
 
 static void world_map_select_close(void)
 {
+#if 0
    world_map.sel_spawn = world_map.close_spawn;
    gui_helper_reset( k_gui_helper_mode_default );
 
@@ -62,15 +63,12 @@ static void world_map_select_close(void)
       vg_strcat( &text, "Spawn Here" );
    if( gui_new_helper( input_button_list[k_srbind_mback], &text ) )
       vg_strcat( &text, "Back" );
-}
-
-void world_map_click(void)
-{
-   world_map_select_close();
+#endif
 }
 
 static void world_map_help_normal(void)
 {
+#if 0
    gui_helper_reset( k_gui_helper_mode_default );
 
    vg_str text;
@@ -80,14 +78,22 @@ static void world_map_help_normal(void)
    if( gui_new_helper( input_button_list[k_srbind_maccept], &text ) )
       vg_strcat( &text, "Select" );
 
+#if 0
    if( gui_new_helper( input_button_list[k_srbind_mback], &text ) )
       vg_strcat( &text, "Exit" );
+#endif
+#endif
 }
 
 void world_map_pre_update(void)
 {
    if( menu_viewing_map() )
    {
+      if( world_map.activity == k_map_activity_galaxy )
+      {
+         return;
+      }
+
       if( !world_map.view_ready )
       {
          world_instance *world = &_world.main;
@@ -118,6 +124,7 @@ void world_map_pre_update(void)
    v3f *bbx = world->scene_geo.bbx;
    f32 *pos = world_map.plane_pos;
 
+#if 0
    v2f steer;
    joystick_state( k_srjoystick_steer, steer );
    v2_normalize_clamp( steer );
@@ -130,6 +137,7 @@ void world_map_pre_update(void)
       m2x2_mulv( rm, steer, steer );
       v2_muladds( pos, steer, vg.time_frame_delta * 200.0f, pos );
    }
+#endif
 
    f32 bd_target = 400.0f,
        interp = vg.time_frame_delta*2.0f;
@@ -168,7 +176,7 @@ void world_map_pre_update(void)
 
    vg_camera_update_transform( cam );
    vg_camera_update_view( cam );
-   vg_camera_update_projection( cam );
+   vg_camera_update_projection( cam, vg.window_x, vg.window_y );
    vg_camera_finalize( cam );
 
    /* pick spawn */
@@ -194,6 +202,7 @@ void world_map_pre_update(void)
       spawn->transform.s[0] = d2;
    }
 
+#if 0
    if( button_down( k_srbind_maccept ) )
    {
       if( world_map.sel_spawn )
@@ -216,13 +225,8 @@ void world_map_pre_update(void)
          world_map.sel_spawn = NULL;
          world_map_help_normal();
       }
-      else
-      {
-         srinput.state = k_input_state_resume;
-         skaterift.activity = k_skaterift_default;
-         return;
-      }
    }
+#endif
 
    /* icons
     * ---------------------*/
@@ -314,3 +318,155 @@ void world_map_pre_update(void)
       respawn_map_draw_icon( cam, k_gui_icon_glider, glider->transform.co, 1 );
    }
 }
+
+void render_world_map(void)
+{
+   glEnable( GL_DEPTH_TEST );
+
+   if( world_map.activity == k_map_activity_local_world )
+   {
+      world_instance *world = &_world.main;
+      world_prerender( world );
+
+      glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } );
+      
+      v3f bg;
+      v3_muls( world->ub_lighting.g_daysky_colour,
+                  world->ub_lighting.g_day_phase - 
+                  world->ub_lighting.g_sunset_phase*0.1f, bg );
+
+      v3_muladds( bg, world->ub_lighting.g_sunset_colour,
+                  (1.0f-0.5f)*world->ub_lighting.g_sunset_phase, bg );
+
+      v3_muladds( bg, world->ub_lighting.g_nightsky_colour,
+                  (1.0f-world->ub_lighting.g_day_phase), bg );
+
+      glClearColor( bg[0], bg[1], bg[2], 0.0f );
+      glClear( GL_COLOR_BUFFER_BIT );
+      glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } );
+
+      m4x3f identity;
+      m4x3_identity( identity );
+      render_world_override( world, world, identity, &g_render.cam, 
+                             world_map.close_spawn, 
+                             (v4f){world->tar_min, world->tar_max, 1.0f, 0.0f});
+      render_world_routes( world, world, identity, &g_render.cam, 0, 1 );
+   }
+   else
+   {
+      glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } );
+      glClearColor( 0.1f,0.1f,0.2f, 0.0f );
+      glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+      if( _world.preview_instance.complete )
+      {
+         world_instance *world = &_world.preview_instance;
+         vg_camera cam;
+         v3f dir;
+         dir[0] = -sqrtf(0.5f);
+         dir[2] =  sqrtf(0.5f);
+         dir[1] =  1.0f;
+         v3_normalize(dir);
+
+         v3f centroid;
+         v3_add( world->scene_geo.bbx[0], world->scene_geo.bbx[1], centroid );
+         v3_muls( centroid, 0.5f, centroid );
+         v3_muladds( centroid, dir, 900.0f, cam.pos );
+
+         f32 t = vg.time_real * 0.1;
+
+         v3f ofs = {
+            vg_perlin_fract_1d( t,       2.0f, 2, 300 ),
+            vg_perlin_fract_1d( t+43.5f, 2.0f, 2, 301 ),
+            vg_perlin_fract_1d( t+80.2f, 2.0f, 2, 302 ),
+         };
+
+         v3_muladds( cam.pos, ofs, 40.0f, cam.pos );
+
+         cam.angles[0] = 0.25f * VG_PIf + ofs[0]*0.1f;
+         cam.angles[1] = 0.25f * VG_PIf + ofs[1]*0.1f;
+         cam.angles[2] = ofs[2]*0.01f;
+         cam.farz = 5000.0f;
+         cam.nearz = 10.0f;
+         cam.fov = 40.0f;
+
+         vg_camera_update_transform( &cam );
+         vg_camera_update_view( &cam );
+         vg_camera_update_projection( &cam, vg.window_x, vg.window_y );
+         vg_camera_finalize( &cam );
+         vg_camera_finalize( &cam );
+
+         render_world_preview( &cam );
+
+         if( world->complete )
+         {
+            for( u32 i=0; i<af_arrcount(&world->ent_route); i++ )
+            {
+               ent_route *route = af_arritm( &world->ent_route, i );
+
+               v4f colour;
+               v4_copy( route->colour, colour );
+               v3_muls( colour, 1.6666f, colour );
+               gui_icon_setcolour( colour );
+               respawn_map_draw_icon( &cam, k_gui_icon_rift_run_2d, route->anon.transform.co, 0.5f );
+            }
+         }
+      }
+
+      vg_framebuffer_bind( g_render.fb_workshop_preview, 1.0f );
+      glClearColor( 0.02f,0.02f,0.08f, 0.0f );
+      glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+      vg_camera cam, *smooth_cam = &world_map.superworld_cam;
+      ent_camera *cam_temp = af_arritm( &world_map.ent_camera, world_map.superworld_list_selected );
+      ent_camera_unpack( cam_temp, &cam );
+
+      vg_camera_lerp( smooth_cam, &cam, vg.time_frame_delta * 6.0f, smooth_cam );
+      vg_camera_update_transform( smooth_cam );
+      vg_camera_update_view( smooth_cam );
+      vg_camera_update_projection( smooth_cam, WORKSHOP_PREVIEW_WIDTH, WORKSHOP_PREVIEW_HEIGHT );
+      vg_camera_finalize( smooth_cam );
+      
+      shader_model_superworld_use();
+      shader_model_superworld_uPv( smooth_cam->mtx.pv );
+      shader_model_superworld_uCam( smooth_cam->pos );
+      shader_model_superworld_uColour( (v4f){0.4f,0.8f,0.0f,0.0f} );
+      shader_model_superworld_uTime( vg.time*0.25f );
+
+      mesh_bind( &world_map.superworld_mesh );
+      mdl_context *model = &world_map.superworld_meta;
+      for( u32 i=0; i<model->mesh_count; i ++ )
+      {
+         mdl_mesh *mesh = &model->meshes[i];
+
+         q_axis_angle( mesh->transform.q, (v3f){0,1,0}, vg.time_real*0.4f );
+
+         m4x3f mmdl;
+         mdl_transform_m4x3( &mesh->transform, mmdl );
+         shader_model_superworld_uMdl( mmdl );
+
+         for( u32 j=0; j<mesh->submesh_count; j ++ )
+         {
+            mdl_submesh *submesh = &model->submeshes[ mesh->submesh_start + j ];
+            mdl_draw_submesh( submesh );
+         }
+      }
+
+      glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } );
+   }
+}
+
+void world_map_init(void)
+{
+   mdl_context *model = &world_map.superworld_meta;
+   mdl_open( model, "models/rs_superworlds.mdl", vg_mem.rtmemory );
+   mdl_load_metadata_block( model, vg_mem.rtmemory );
+   mdl_async_load_glmesh( model, &world_map.superworld_mesh, NULL );
+   AF_LOAD_ARRAY_STRUCT( &model->af, &world_map.ent_camera, ent_camera, vg_mem.rtmemory );
+   AF_LOAD_ARRAY_STRUCT( &model->af, &world_map.ent_marker, ent_marker, vg_mem.rtmemory );
+   mdl_close( model );
+
+   world_map.superworld_cam.nearz = 0.1f;
+   world_map.superworld_cam.farz = 250.0f;
+   world_map.superworld_cam.fov = 90.0f;
+}
index fc29d785e31af89d2848e8e441b7d7317ba3b21e..d78cd773aa87f44845efc50eb3358084932cf456 100644 (file)
@@ -2,6 +2,14 @@
 #include "vg/vg_platform.h"
 #include "vg/vg_camera.h"
 #include "world_entity.h"
+enum superworld
+{
+   k_superworld_venus_moon = 0,
+   k_superworld_infinite,
+   k_superworld_campaign,
+   k_superworld_steam_workshop,
+   k_superworld_max
+};
 
 struct world_map
 {
@@ -13,6 +21,35 @@ struct world_map
    vg_camera cam;
 
    bool view_ready;
+
+   enum map_activity
+   {
+      k_map_activity_local_world,
+      k_map_activity_galaxy
+   }
+   activity;
+
+   mdl_context superworld_meta;
+   glmesh superworld_mesh;
+   array_file_ptr ent_camera,
+                  ent_marker,
+                  ent_challenge;
+
+   i32 superworld_list_selected,
+       superworld_actual_world;
+
+   vg_camera superworld_cam;
 }
 extern world_map;
 void world_map_pre_update(void);
+void render_world_map(void);
+void world_map_init(void);
+
+
+const char *_superworld_names[] =
+{
+   [k_superworld_venus_moon] = "One of Venus's moons",
+   [k_superworld_infinite] = "Infinite Dimension",
+   [k_superworld_campaign] = "Earth",
+   [k_superworld_steam_workshop] = "Steam Workshop Planet",
+};
index afcc3578caf698a246123ddb357989045c48793f..2206739af2a5835d0f6f3a2f0a2e8ad2af2b600e 100644 (file)
@@ -764,6 +764,9 @@ static void bindpoint_terrain( world_instance *world,
 
 static void bindpoint_override( world_instance *world, struct world_surface *mat )
 {
+   f32 skate_surface = mat->info.flags & k_material_flag_skate_target? 1.0f: 0.0f;
+   shader_scene_override_uSurfaceInfo( (v4f){ skate_surface, 0,0,0 } );
+   
    if( mat->info.flags & k_material_flag_collision )
    {
       shader_scene_override_uAlphatest(0);
@@ -778,6 +781,9 @@ static void bindpoint_override( world_instance *world, struct world_surface *mat
 
 static void bindpoint_world_preview( world_instance *world, struct world_surface *mat )
 {
+   f32 skate_surface = mat->info.flags & k_material_flag_skate_target? 1.0f: 0.0f;
+   shader_scene_preview_uSurfaceInfo( (v4f){ skate_surface, 0,0,0 } );
+
    if( mat->info.flags & k_material_flag_collision )
    {
    }
@@ -1289,12 +1295,13 @@ void render_world_override( world_instance *world,
    render_world_fxglow( world, world, cam, mmdl, 0, 0, 1 );
 }
 
-void render_world_preview(void)
+void render_world_preview( vg_camera *cam )
 {
    world_instance *world = &_world.preview_instance;
+
    struct world_pass pass = 
    {
-      .cam = &g_render.cam,
+      .cam = cam,
       .fn_bind = bindpoint_world_preview,
       .fn_set_mdl = shader_scene_preview_uMdl,
       .fn_set_uPvmPrev = shader_scene_preview_uPvmPrev,
@@ -1304,6 +1311,7 @@ void render_world_preview(void)
 
    shader_scene_preview_use();
    shader_scene_preview_uPv( pass.cam->mtx.pv );
+   shader_scene_preview_uCamera( pass.cam->pos );
    shader_scene_preview_uMapInfo((v4f){-1000.0f, 1000.0f, 0.0f, 0.0f});
    //shader_scene_preview_uMapInfo((v4f){world->tar_min, world->tar_max, 1.0f, 0.0f});
 
@@ -1321,10 +1329,12 @@ void render_world_preview(void)
    v3_normalize( mnormal[1] );
    v3_normalize( mnormal[2] );
 
-   glDisable( GL_CULL_FACE );
+   glEnable( GL_CULL_FACE );
    mesh_bind( &world->mesh_geo );
    pass.geo_type = k_world_geo_type_solid;
    render_world_override_pass( world, &pass, mmdl, mnormal, mpvm_prev );
+
+   glDisable( GL_CULL_FACE );
    mesh_bind( &world->mesh_no_collide );
    pass.geo_type = k_world_geo_type_nonsolid;
    render_world_override_pass( world, &pass, mmdl, mnormal, mpvm_prev );
index 6e6915b7689dcfb1fff42af9dc65c38c7a42f27f..2f0437e2ed58cf363218a38a60e94a6fe350ce28 100644 (file)
@@ -83,7 +83,7 @@ void render_world_override( world_instance *world,
                             m4x3f mmdl,
                             vg_camera *cam,
                             ent_spawn *dest_spawn, v4f map_info );
-void render_world_preview(void);
+void render_world_preview( vg_camera *cam );
 void render_world_gates( world_instance *world, vg_camera *cam );
 void imgui_world_light_edit( ui_context *ctx, world_instance *world );