World map shows some detailed info for stuff
authorhgn <hgodden00@gmail.com>
Mon, 21 Apr 2025 10:28:19 +0000 (11:28 +0100)
committerhgn <hgodden00@gmail.com>
Mon, 21 Apr 2025 10:28:19 +0000 (11:28 +0100)
src/ent_route.c
src/ent_route.h
src/menu.c
src/menu.h
src/skaterift.c
src/world_map.c
src/world_map.h

index 48f4bd7881df5df1e4c6337dea701df6e0918a1a..7d032260eb25c7ea3e70d6c4c3a3313b8d3aed8d 100644 (file)
@@ -161,3 +161,149 @@ E0:
       }
    }
 }
+
+void ent_route_leaderboard_ui( ui_context *ctx, ui_rect ref_box, u32 route_index )
+{
+   if( _ent_route.viewing_route_id != route_index )
+   {
+      _ent_route.viewing_route_id = route_index;
+      _ent_route.cursor[0] = 0;
+      _ent_route.cursor[1] = 0;
+   }
+
+   ui_rect leaderboard_box = { ref_box[0], ref_box[1] + ref_box[3] + 16, ref_box[2], 24 };
+   
+   if( !network_connected() )
+   {
+      ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_text( ctx, leaderboard_box, "Offline - No Leaderboards", 1, k_ui_align_middle_center, 0 );
+      return;
+   }
+
+   world_instance *world = &_world.main;
+   if( route_index >= af_arrcount( &world->ent_route ) )
+   {
+      ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_text( ctx, leaderboard_box, "Error - Out of range", 1, k_ui_align_middle_center, 0 );
+      return;
+   }
+
+   struct leaderboard_cache *board = &world->leaderboard_cache[ route_index ];
+
+   if( board->status == k_request_status_not_found )
+   {
+      ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_text( ctx, leaderboard_box, "Leaderboard not found", 1, k_ui_align_middle_center, 0 );
+      return;
+   }
+
+   if( board->status != k_request_status_ok )
+   {
+      ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_text( ctx, leaderboard_box, "Leaderboard error", 1, k_ui_align_middle_center, 0 );
+      return;
+   }
+
+   vg_msg body;
+   vg_msg_init( &body, board->data, board->data_len );
+
+   if( !vg_msg_seekframe( &body, "rows" ) )
+   {
+      ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_text( ctx, leaderboard_box, "No Records", 1, k_ui_align_middle_center, 0 );
+      return;
+   }
+
+   ui_fill( ctx, leaderboard_box, ui_opacity( GUI_COL_DARK, 0.36f ) );
+   ui_text( ctx, leaderboard_box, "Online Leaderboard", 1, k_ui_align_middle_center, 0 );
+   leaderboard_box[1] += leaderboard_box[3]+4;
+
+   int ml = button_press( k_srbind_mleft ),
+       mr = button_press( k_srbind_mright ),
+       mu = button_press( k_srbind_mup ),
+       md = button_press( k_srbind_mdown ),
+       mh = ml-mr,
+       mv = mu-md;
+
+   if( vg_input.display_input_method == k_input_method_controller )
+   {
+      menu_nav( &_ent_route.cursor[1], mv, _ent_route.entries_max-1 );
+      menu_nav( &_ent_route.cursor[0], mh, 1 );
+   }
+
+   i32 count = 0;
+   while( vg_msg_seekframe( &body, NULL ) )
+   {
+      ui_rect bl, br, brr;
+      ui_split( leaderboard_box, k_ui_axis_v, 48, 4, bl, br );
+      ui_split( br, k_ui_axis_v, -150, 4, br, brr );
+      leaderboard_box[1] += leaderboard_box[3]+4;
+
+      ui_fill( ctx, bl, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_fill( ctx, br, ui_opacity( GUI_COL_DARK, 0.36f ) );
+      ui_fill( ctx, brr, ui_opacity( GUI_COL_DARK, 0.36f ) );
+
+      char buf[100];
+      vg_str str;
+      vg_strnull( &str, buf, 100 );
+      vg_strcat( &str, "#" );
+      vg_strcati32( &str, count+1 );
+      ui_text( ctx, bl, buf, 1, k_ui_align_middle_center, 0 );
+
+      /* name */
+      const char *username = vg_msg_getkvstr( &body, "username" );
+      if( !username )
+         username = "????";
+      ui_text( ctx, br, username, 1, k_ui_align_middle_left, 0 );
+
+      /* time */
+      vg_strnull( &str, buf, 100 );
+      u32 centiseconds;
+      vg_msg_getkvintg( &body, "time", k_vg_msg_u32, &centiseconds, NULL );
+
+      i32 seconds = centiseconds / 100,
+          minutes = seconds / 60;
+
+      centiseconds %= 100;
+      seconds     %= 60;
+      minutes     %= 60;
+      if( minutes > 9 ) vg_strcat( &str, "?" );
+      else vg_strcati32( &str, minutes );
+      vg_strcat( &str, ":" );
+      vg_strcati32r( &str, seconds, 2, '0' );
+      vg_strcat( &str, "." );
+      vg_strcati32r( &str, centiseconds, 2, '0' );
+      ui_text( ctx, brr, buf, 1, k_ui_align_middle_center, 0 );
+
+      if( vg_input.display_input_method == k_input_method_controller )
+      {
+         if( _ent_route.cursor[1] == count )
+         {
+            if( _ent_route.cursor[0] == 0 )
+               ui_outline( ctx, br, -1, ui_colour(ctx,k_ui_fg), 0 );
+            else if( _ent_route.cursor[0] == 1 )
+               ui_outline( ctx, brr, -1, ui_colour(ctx,k_ui_fg), 0 );
+         }
+      }
+      else
+      {
+         enum ui_button_state state = ui_button_base( ctx, br );
+         if( state == k_ui_button_hover )
+            ui_outline( ctx, br, -1, ui_colour(ctx,k_ui_fg), 0 );
+         if( state == k_ui_button_click )
+         {
+         }
+
+         state = ui_button_base( ctx, brr );
+         if( state == k_ui_button_hover )
+            ui_outline( ctx, brr, -1, ui_colour(ctx,k_ui_fg), 0 );
+         if( state == k_ui_button_click )
+         {
+         }
+      }
+
+      count ++;
+      vg_msg_skip_frame( &body );
+   }
+   _ent_route.entries_max = count;
+}
index 7b5af1065ae38185b92080f99adc136538b5e94f..7bf882086d8fe40ce7792ef48e1d534c645ecf77 100644 (file)
@@ -7,8 +7,12 @@ struct _ent_route
 
    u32 viewing_route_id;
    u32 run_selection;
+
+   v2i cursor;
+   i32 entries_max;
 }
 extern _ent_route;
 
 entity_call_result ent_route_call( world_instance *world, ent_call *call );
 void ent_route_preupdate(void);
+void ent_route_leaderboard_ui( ui_context *ctx, ui_rect ref_box, u32 route_index );
index 5abf2e6a34db894984ff3597cf63f24d578e35f9..b98dddb82536308ef620ada4b7d1f41ff2018f2a 100644 (file)
@@ -354,7 +354,7 @@ static u32 medal_colour( ui_context *ctx, u32 flags )
    else return 0;
 }
 
-static i32 menu_nav( i32 *p_row, int mv, i32 max )
+i32 menu_nav( i32 *p_row, int mv, i32 max )
 {
    i32 row_prev = *p_row;
 
index 40f24ddb732523e30207bab28e877658b0b7d46a..fddd0cf97b68996de838bbd171df720a5d4d560c 100644 (file)
@@ -74,3 +74,5 @@ bool menu_viewing_preview(void);
 void menu_update_world_list(void);
 void menu_on_world_change( addon_id addon );
 bool menu_button_rect( ui_context *ctx, ui_rect rect, bool select, bool clickable, const char *text );
+//util
+i32 menu_nav( i32 *p_row, int mv, i32 max );
index 2e16be10ebdda4c78ecc604b4ef6eb0df72be395..4ed8d9c5f62a1979dfb4d152e1b885cd8918900e 100644 (file)
@@ -371,10 +371,7 @@ static void skaterift_composite_maincamera(void)
    if( cs_cam )
       ent_camera_unpack( cs_cam, &g_render.cam );
 
-   if( world_map.sel_spawn && (world_map.spawn_timer > 0.0f) )
-   {
-      vg_camera_copy( &world_map.final_cam, &g_render.cam );
-   }
+   world_map_get_transition_cam( &g_render.cam );
 
    vg_camera_update_transform( &g_render.cam );
 
@@ -434,7 +431,7 @@ static void render_main_game(void)
    {
       render_world_map();
 
-      if( world_map.sel_spawn && (world_map.spawn_timer > 0.0f) )
+      if( world_map_get_transition_cam(NULL) )
       {
          glEnable( GL_STENCIL_TEST );
          glDisable( GL_DEPTH_TEST );
index bdd61d48a32d0d67ea80d3c993faa588d02ee1dd..6c1120d3f49398dcc7528d29beeac4a852ce3076 100644 (file)
@@ -60,8 +60,8 @@ void world_map_initialize_view(void)
    respawn_world_to_plane_pos( localplayer.rb.co, world_map.plane_pos );
    world_map.boom_dist = 400.0f;
    world_map.view_ready = 1;
-   world_map.sel_spawn = NULL;
-   world_map.close_spawn = NULL;
+   world_map.selected_entity_id = 0;
+   world_map.closest_entity_id = 0;
    world_map.smooth_warm = 0;
 }
 
@@ -78,7 +78,7 @@ static void world_map_update_final_camera( vg_camera *cam )
    world_map.final_cam.farz = 5000.0f;
    world_map.final_cam.nearz = 10.0f;
 
-   if( world_map.sel_spawn && (world_map.spawn_timer > 0.0f) )
+   if( world_map_get_transition_cam(NULL) )
    {
       f32 t = vg_smoothstepf( world_map.spawn_timer );
       vg_camera_lerp( &localplayer.cam, &world_map.smoothed_cam, t, &world_map.final_cam );
@@ -95,6 +95,24 @@ static void world_map_update_final_camera( vg_camera *cam )
    vg_camera_finalize( &world_map.final_cam );
 }
 
+static bool _map_closer( v3f co, f32 *closest2, f32 *d2 )
+{
+   v4f v;
+   v3_copy( co, v );
+   v[3] = 1.0f;
+   m4x4_mulv( world_map.final_cam.mtx.pv, v, v );
+   v2_divs( v, v[3], v );
+
+   *d2 = v2_dist2(v, world_map.view_centroid);
+   if( *d2 < *closest2 )
+   {
+      *closest2 = *d2;
+      return 1;
+   }
+
+   return 0;
+}
+
 void render_world_map(void)
 {
    glEnable( GL_DEPTH_TEST );
@@ -122,55 +140,57 @@ void render_world_map(void)
 
       world_map_update_final_camera( &cam );
 
-      /* update closest spawn based on camera
+      /* update closest entity based on camera
        * ------------------------------------------------------------------------------------------- */
-      f32 closest2 = INFINITY;
+      f32 closest2 = INFINITY, d2;
+
       for( u32 i=0; i<af_arrcount(&world->ent_spawn); i++ )
       {
          ent_spawn *spawn = af_arritm(&world->ent_spawn,i);
-
          if( spawn->flags & k_ent_spawn_flag_locked )
             continue;
 
-         if( world_map.sel_spawn )
+         u32 spawn_ent_id = mdl_entity_id( k_ent_spawn, i );
+         if( world_map.selected_entity_id )
          {
-            spawn->transform.s[0] = ( spawn == world_map.sel_spawn )? 0.0f: 10000.0f;
+            spawn->transform.s[0] = ( spawn_ent_id == world_map.selected_entity_id )? 0.0f: 10000.0f;
          }
          else
          {
-            v4f v;
-            v3_copy( spawn->transform.co, v );
-            v[3] = 1.0f;
-            m4x4_mulv( world_map.final_cam.mtx.pv, v, v );
-            v2_divs( v, v[3], v );
-
-            f32 d2 = v2_dist2(v, world_map.view_centroid);
-            if( d2 < closest2 )
-            {
-               world_map.close_spawn = spawn;
-               closest2 = d2;
-            }
+            if( _map_closer( spawn->transform.co, &closest2, &d2 ) )
+               world_map.closest_entity_id = spawn_ent_id;
             spawn->transform.s[0] = d2;
          }
       }
+      for( u32 i=0; i<af_arrcount(&world->ent_challenge); i++ )
+      {
+         ent_challenge *challenge = af_arritm( &world->ent_challenge, i );
+         if( challenge->flags & k_ent_challenge_locked ) 
+            continue;
+         u32 challenge_ent_id = mdl_entity_id( k_ent_challenge, i );
+         if( _map_closer( challenge->transform.co, &closest2, &d2 ) )
+            world_map.closest_entity_id = challenge_ent_id;
+      }
+      for( u32 i=0; i<af_arrcount(&world->ent_route); i++ )
+      {
+         ent_route *route = af_arritm( &world->ent_route, i );
+         u32 route_ent_id = mdl_entity_id( k_ent_route, i );
+         if( _map_closer( route->board_transform[3], &closest2, &d2 ) )
+            world_map.closest_entity_id = route_ent_id;
+      }
 
       /* Do the rendering of the world in override shader
        * ------------------------------------------------------------------------------------------------- */
 
       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 );
+                  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 );
+      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 );
@@ -178,8 +198,21 @@ void render_world_map(void)
 
       m4x3f identity;
       m4x3_identity( identity );
-      render_world_override( world, world, identity, &world_map.final_cam, 
-                              world_map.sel_spawn? world_map.sel_spawn: world_map.close_spawn, 
+
+      u32 selected_type = mdl_entity_id_type( world_map.selected_entity_id ),
+          selected_index = mdl_entity_id_id( world_map.selected_entity_id ),
+          close_type = mdl_entity_id_type( world_map.closest_entity_id ),
+          close_index = mdl_entity_id_id( world_map.closest_entity_id );
+
+      ent_spawn *dest_spawn = NULL;
+
+      if( (selected_type == k_ent_none) && (close_type == k_ent_spawn) )
+         dest_spawn = af_arritm( &world->ent_spawn, close_index );
+
+      if( selected_type == k_ent_spawn )
+         dest_spawn = af_arritm( &world->ent_spawn, selected_index );
+
+      render_world_override( world, world, identity, &world_map.final_cam, dest_spawn,
                              (v4f){world->tar_min, world->tar_max, 1.0f, 0.0f});
       render_world_routes( world, world, identity, &world_map.final_cam, 0, 1 );
 
@@ -194,7 +227,10 @@ void render_world_map(void)
          enum gui_icon icon = k_gui_icon_exclaim_2d;
          if( challenge->status )
             icon = k_gui_icon_tick_2d;
-         respawn_map_draw_icon( &world_map.final_cam, icon, challenge->transform.co, 1.0f );
+
+         u32 challenge_id = mdl_entity_id( k_ent_challenge, i );
+         respawn_map_draw_icon( &world_map.final_cam, icon, challenge->transform.co, 
+                                 world_map.closest_entity_id == challenge_id? 1.5f: 1.0f );
       }
 
       for( u32 i=0; i<af_arrcount(&world->ent_marker); i ++ )
@@ -217,9 +253,10 @@ void render_world_map(void)
          if( spawn->transform.s[0] > 0.3f )
             continue;
 
+         u32 spawn_ent_id = mdl_entity_id( k_ent_spawn, i );
          f32 s = 1.0f-(spawn->transform.s[0] / 0.3f);
          respawn_map_draw_icon( &world_map.final_cam, 
-                                 spawn==world_map.sel_spawn? k_gui_icon_spawn_select: k_gui_icon_spawn,
+                                 world_map.selected_entity_id == spawn_ent_id? k_gui_icon_spawn_select: k_gui_icon_spawn,
                                  spawn->transform.co, s );
       }
 
@@ -253,7 +290,10 @@ void render_world_map(void)
          v4_copy( route->colour, colour );
          v3_muls( colour, 1.6666f, colour );
          gui_icon_setcolour( colour );
-         respawn_map_draw_icon( &world_map.final_cam, k_gui_icon_rift_run_2d, route->board_transform[3], 1 );
+
+         u32 route_id = mdl_entity_id( k_ent_route, i );
+         respawn_map_draw_icon( &world_map.final_cam, k_gui_icon_rift_run_2d, route->board_transform[3], 
+                                 world_map.closest_entity_id == route_id? 1.5f: 1.0f );
       }
 
       for( u32 i=0; i<af_arrcount(&world->ent_glider); i ++ )
@@ -381,6 +421,22 @@ void render_world_map(void)
    }
 }
 
+bool world_map_get_transition_cam( vg_camera *cam )
+{
+   u32 selected_type = mdl_entity_id_type( world_map.selected_entity_id ),
+       selected_index = mdl_entity_id_id( world_map.selected_entity_id );
+
+   if( (selected_type == k_ent_spawn) && (world_map.spawn_timer > 0.0f) )
+   {
+      if( cam )
+         vg_camera_copy( &world_map.final_cam, cam );
+
+      return 1;
+   }
+   
+   return 0;
+}
+
 void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *allow_back_to_exit )
 {
    ui_rect title_box = { main_area[2]/2 - 300/2, main_area[1] + 8, 300, 100 };
@@ -397,11 +453,32 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 
       world_map.boom_dist = 400.0f;
 
-      if( world_map.sel_spawn )
+      if( world_map.selected_entity_id )
       {
+         u32 selected_type = mdl_entity_id_type( world_map.selected_entity_id ),
+             selected_index = mdl_entity_id_id( world_map.selected_entity_id );
+
          /* focus camera down onto spawn */
          v2f pp;
-         respawn_world_to_plane_pos( world_map.sel_spawn->transform.co, pp );
+
+         if( selected_type == k_ent_spawn )
+         {
+            ent_spawn *spawn = af_arritm( &world->ent_spawn, selected_index );
+            respawn_world_to_plane_pos( spawn->transform.co, pp );
+         }
+         else if( selected_type == k_ent_challenge )
+         {
+            ent_challenge *challenge = af_arritm( &world->ent_challenge, selected_index );
+            respawn_world_to_plane_pos( challenge->transform.co, pp );
+         }
+         else if( selected_type == k_ent_route )
+         {
+            ent_route *route = af_arritm( &world->ent_route, selected_index );
+            respawn_world_to_plane_pos( route->board_transform[3], pp );
+         }
+         else
+            v2_zero( pp );
+
          v2_lerp( world_map.plane_pos, pp, vg.time_frame_delta*2.0f, world_map.plane_pos );
          world_map.boom_dist = 200.0f;
 
@@ -416,7 +493,7 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
             {
                skaterift.activity = k_skaterift_default;
                srinput.state = k_input_state_resume;
-               world_map.sel_spawn = NULL;
+               world_map.selected_entity_id = 0;
             }
          }
          else
@@ -430,37 +507,65 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 
             ctx->font = &vgf_default_title;
             ui_rect label_box = { spawn_box[0], spawn_box[1] + 8, spawn_box[2], 48 };
-            ui_text( ctx, label_box, "Spawnpoint", 1, k_ui_align_center, ui_colour(ctx,k_ui_yellow) );
+
+            const char *title_text = "Spawnpoint";
+            if( selected_type == k_ent_challenge )
+               title_text = "Challenge";
+            else if( selected_type == k_ent_route )
+               title_text = "Race";
+            ui_text( ctx, label_box, title_text, 1, k_ui_align_center, ui_colour(ctx,k_ui_yellow) );
 
             bool spawn_here = 0,
-                 exit_spawn = 0;
+                 exit_this = 0;
 
             ui_rect no_box = { spawn_box[0] + spawn_box[2]-32, spawn_box[1], 32, 32 };
             if( menu_button_rect( ctx, no_box, 0, 1, "X" ) )
             {
-               exit_spawn = 1;
+               exit_this = 1;
             }
 
             ctx->font = &vgf_default_large;
-            ui_rect yes_box = { spawn_box[0]+(spawn_box[2]-100)/2, spawn_box[1]+spawn_box[3]-32, 100, 32 };
-            if( menu_button_rect( ctx, yes_box, vg_input.display_input_method == k_input_method_controller, 1, "Spawn" ) )
+
+            if( selected_type == k_ent_spawn )
             {
-               spawn_here = 1;
+               ui_rect yes_box = { spawn_box[0]+(spawn_box[2]-100)/2, spawn_box[1]+spawn_box[3]-32, 100, 32 };
+               if( menu_button_rect( ctx, yes_box, vg_input.display_input_method == k_input_method_controller, 1, "Spawn" ) )
+                  spawn_here = 1;
+
+               if( button_down( k_srbind_maccept ) )
+                  spawn_here = 1;
+            }
+            else
+            {
+               ui_rect name_box = { spawn_box[0], label_box[1] + label_box[3], 
+                                    spawn_box[2], (spawn_box[1] + spawn_box[3]) - (label_box[1] + label_box[3]) };
+
+               const char *name = "???";
+               if( selected_type == k_ent_challenge )
+               {
+                  ent_challenge *challenge = af_arritm( &world->ent_challenge, selected_index );
+                  name = af_str( &world->meta.af, challenge->pstr_alias );
+               }
+               else if( selected_type == k_ent_route )
+               {
+                  ent_route *route = af_arritm( &world->ent_route, selected_index );
+                  name = af_str( &world->meta.af, route->pstr_name );
+                  ent_route_leaderboard_ui( ctx, spawn_box, selected_index );
+               }
+               ui_text( ctx, name_box, name, 1, k_ui_align_middle_center, 0 );
             }
 
-            if( button_down( k_srbind_maccept ) )
-               spawn_here = 1;
             if( button_down( k_srbind_mback ) )
-               exit_spawn = 1;
+               exit_this = 1;
 
             if( spawn_here )
             {
-               player__spawn( world_map.sel_spawn );
+               player__spawn( af_arritm( &world->ent_spawn, selected_index ) );
                world_map.spawn_timer = 1.0f;
             }
-            else if( exit_spawn )
+            else if( exit_this )
             {
-               world_map.sel_spawn = NULL;
+               world_map.selected_entity_id = 0;
             }
          }
       }
@@ -546,9 +651,10 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
          if( (world->meta.version >= 109) && af_arrcount( &world->ent_region ) )
          {
             ui_rect stat_panel = { main_area[0]+main_area[2]-(256+8), main_area[1]+8, 256, main_area[3]-16 };
+            main_area[2] = stat_panel[0] - main_area[0]; // for mouse clipping
+
             u32 c0 = ui_opacity( GUI_COL_DARK, 0.36f );
             ui_fill( ctx, stat_panel, c0 );
-
             ui_rect_pad( stat_panel, (ui_px[2]){8,0} );
 
             for( u32 i=0; i<af_arrcount( &world->ent_region ); i ++ )
@@ -584,6 +690,33 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
                   u32 type = mdl_entity_id_type( ref->entity_id ),
                       index = mdl_entity_id_id( ref->entity_id );
 
+                  if( !((type == k_ent_route) || (type == k_ent_challenge)) )
+                     continue;
+
+                  ui_rect r;
+                  ui_standard_widget( ctx, stat_panel, r, 1 );
+
+                  ui_rect b;
+                  rect_copy( r, b );
+                  b[0] -= 16;
+                  b[2] += 16;
+
+                  if( ref->entity_id == world_map.selected_entity_id )
+                  {
+                     ui_outline( ctx, b, -1, ui_colour(ctx,k_ui_blue), 0 );
+                  }
+                  else
+                  {
+                     enum ui_button_state state = ui_button_base( ctx, b );
+                     if( state == k_ui_button_hover )
+                        ui_outline( ctx, b, -2, ui_colour(ctx,k_ui_fg), 0 );
+
+                     if( state == k_ui_button_click )
+                     {
+                        world_map.selected_entity_id = ref->entity_id;
+                     }
+                  }
+
                   if( type == k_ent_route )
                   {
                      ent_route *route = af_arritm( &world->ent_route, index );
@@ -597,9 +730,6 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
                         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 ) );
                   }
                   else if( type == k_ent_challenge )
@@ -618,9 +748,6 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
                      }
 
                      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 ) );
                   }
                }
@@ -643,7 +770,7 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 
          if( ctx->focused_control_type == k_ui_control_none )
          {
-            if( world_map.close_spawn )
+            if( world_map.closest_entity_id )
             {
                if( vg_input.display_input_method == k_input_method_kbm )
                {
@@ -651,14 +778,14 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
                   {
                      if( ui_click_up(ctx, UI_MOUSE_LEFT) )
                      {
-                        world_map.sel_spawn = world_map.close_spawn;
+                        world_map.selected_entity_id = world_map.closest_entity_id;
                      }
                   }
                }
                else
                {
                   if( button_down( k_srbind_maccept ) )
-                     world_map.sel_spawn = world_map.close_spawn;
+                     world_map.selected_entity_id = world_map.closest_entity_id;
                }
             }
          }
@@ -815,7 +942,6 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 
       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;
index 2b8e7bbba2b91898cf9d370ceee049363249837b..a3a84b542cd8465bd4e094578d9bcef033db37a5 100644 (file)
@@ -16,7 +16,7 @@ struct world_map
    v2f plane_pos;
    f32 boom_dist;
 
-   ent_spawn *sel_spawn, *close_spawn;
+   u32 selected_entity_id, closest_entity_id;
    vg_camera smoothed_cam, final_cam;
 
    bool smooth_warm;
@@ -48,6 +48,7 @@ void world_map_gui( ui_context *ctx, ui_rect main_area, i32 mh, i32 mv, bool *al
 void render_world_map(void);
 void world_map_init(void);
 void world_map_initialize_view(void);
+bool world_map_get_transition_cam( vg_camera *cam );
 
 const char *_superworld_names[] =
 {