+ u32 j = (pui->fade_start + i) % k_max_ui_segments;
+ struct route_ui_segment *segment = &pui->segments[j];
+
+ fade_block_size += segment->length;
+ }
+
+ cx -= fade_block_size * fade_amt;
+
+ v4f fade_colour;
+ v4_copy( colour, fade_colour );
+ fade_colour[3] *= 1.0f-fade_amt;
+
+ /* 1 minute timer */
+ float timer_delta = (world_global.time - world_global.last_use) * (1.0/45.0),
+ timer_scale = 1.0f - vg_minf( timer_delta, 1.0f );
+
+ /*
+ * Draw fadeout bar
+ */
+
+ float height = pr->factive*k_bar_height * timer_scale,
+ base = -1.0f + (offset+0.5f)*k_bar_height * timer_scale;
+
+ shader_routeui_uColour( fade_colour );
+ for( u32 i=0; i<pui->fade_count; i++ )
+ {
+ u32 j = (pui->fade_start + i) % k_max_ui_segments;
+ struct route_ui_segment *segment = &pui->segments[j];
+
+ shader_routeui_uOffset( (v4f){ cx*k_bar_scale_x, base,
+ k_bar_scale_x, height } );
+
+ world_routes_ui_draw_segment( segment );
+ cx += segment->length;
+ }
+
+ /*
+ * Draw main bar
+ */
+ shader_routeui_uColour( colour );
+ for( u32 i=0; i<pui->segment_count; i++ )
+ {
+ u32 j = (pui->segment_start + i) % k_max_ui_segments;
+ struct route_ui_segment *segment = &pui->segments[j];
+
+ shader_routeui_uOffset( (v4f){ cx*k_bar_scale_x, base,
+ k_bar_scale_x, height } );
+
+ world_routes_ui_draw_segment( segment );
+ cx += segment->length;
+
+ main_block_size += segment->length;
+ }
+
+ pui->xpos = vg_lerpf( pui->xpos, -main_block_size * 0.5f, 0.03f );
+}
+
+VG_STATIC void world_routes_local_set_record( world_instance *world,
+ u32 route, double lap_time )
+{
+ vg_success( " NEW LAP TIME: %f\n", lap_time );
+
+ /* FIXME(10): ID mishmatch */
+ struct route *pr = &world->routes[route];
+
+ if( pr->track_id != 0xffffffff )
+ {
+ double time_centiseconds = lap_time * 100.0;
+ if( time_centiseconds > (float)0xfffe )
+ return;
+
+ highscore_record temp;
+ temp.trackid = pr->track_id;
+ temp.datetime = time(NULL);
+ temp.playerid = 0;
+ temp.points = 0;
+ temp.time = time_centiseconds;
+
+ highscores_push_record( &temp );
+
+ struct track_info *pti = &track_infos[ pr->track_id ];
+ pti->push = 1;
+
+ if( pti->achievement_id )
+ {
+ steam_set_achievement( pti->achievement_id );
+ steam_store_achievements();
+ }
+ }
+ else
+ {
+ vg_warn( "There is no associated track for this record...\n" );
+ }
+}
+
+/*
+ * Will scan the whole run for two things;
+ * 1: we set a new record for the total, complete loop around the course
+ * 2: the time of each segment will be recorded into the data buffer
+ * (not implemented: TODO)
+ */
+VG_STATIC void world_routes_verify_run( world_instance *world, u32 route )
+{
+ /* FIXME(10): ID mishmatch */
+ struct route *pr = &world->routes[route];
+ struct route_ui_bar *pui = &world_global.ui_bars[route];
+
+ u32 stack[64];
+ u32 si = world_routes_get_path( world, world->routes[route].start, stack );
+
+ /*
+ * we only care about gates that ref gates, so shuffle down the array
+ */
+ struct route_timing *timings[64];
+ u32 sj = 0, maxv = 0, begin = 0;
+ for( u32 i=0; i<si; i++ )
+ {
+ struct route_node *inode = &world->nodes[stack[i]];
+
+ if( inode->special_type == k_route_special_type_collector )
+ {
+ timings[sj ++] = &world->collectors[ inode->special_id ].timing;
+ }
+ else if( inode->special_type == k_route_special_type_gate )
+ {
+ timings[sj ++] = &world->gates[inode->special_id].timing;
+ }
+ }
+
+ for( u32 i=0; i<sj; i++ )
+ {
+ if( timings[i]->version > maxv )
+ {
+ maxv = timings[i]->version;
+ begin = i;
+ }
+ }
+
+ vg_info( "== begin verification (%u) ==\n", route );
+ vg_info( " current version: %u\n", world_global.current_run_version );
+
+ int verified = 0;
+ if( timings[begin]->version == world_global.current_run_version )
+ verified = 1;
+
+ int valid_segment_count = 0;
+
+ double lap_time = 0.0;
+
+ for( u32 i=0; i<sj; i++ )
+ {
+ u32 j = (sj+begin-i-1) % sj,
+ j1 = (j+1) % sj;
+
+ double diff = 0.0;
+
+ if( i<sj-1 )
+ {
+ /* j1v should equal jv+1 */
+ if( timings[j1]->version == timings[j]->version+1 )
+ {
+ diff = timings[j1]->time - timings[j]->time;
+ lap_time += diff;
+
+ if( verified && diff > 0.0 ) valid_segment_count ++;
+ }
+ else
+ verified = 0;
+ }
+
+ if( verified )
+ vg_success( " [ %u %f ] %f\n", timings[j1]->time,
+ timings[j1]->version, diff );
+ else
+ vg_warn( " [ %u %f ]\n", timings[j1]->time, timings[j1]->version );
+ }
+
+ pui->fade_start = pui->segment_start;
+ pui->fade_count = 0;
+ pui->fade_timer_start = world_global.time;
+
+ int orig_seg_count = pui->segment_count;
+
+ world_routes_ui_newseg( route );
+
+ if( verified )
+ {
+ world_routes_local_set_record( world, route, lap_time );
+ world_routes_ui_popfirst( pui );
+ pui->fade_count ++;
+ }
+ else
+ vg_info( " ctime: %f\n", lap_time );
+
+ /* remove any excess we had from previous runs */
+ int to_remove = orig_seg_count-valid_segment_count;
+ for( int i=0; i<to_remove; i++ )
+ {
+ world_routes_ui_popfirst( pui );
+ pui->fade_count ++;
+ }
+
+ world->routes[route].latest_pass = world_global.time;
+}
+
+VG_STATIC void world_routes_clear( world_instance *world )
+{
+ for( u32 i=0; i<world->route_count; i++ )
+ {
+ struct route *route = &world->routes[i];
+ route->active = 0;
+ }
+ world_global.current_run_version += 4;
+ world_global.last_use = 0.0;
+}
+
+/*
+ * When going through a gate this is called for bookkeeping purposes
+ */
+VG_STATIC void world_routes_activate_gate( world_instance *world, u32 id )
+{
+ struct route_gate *rg = &world->gates[id];
+ struct route_node *pnode = &world->nodes[rg->node_id],
+ *pdest = &world->nodes[pnode->next[0]];
+
+ world_global.last_use = world_global.time;
+
+ struct route_collector *rc = &world->collectors[ pdest->special_id ];
+
+ world_global.active_gate = id;
+ rg->timing.version = world_global.current_run_version;
+ rg->timing.time = world_global.time;
+
+ for( u32 i=0; i<world->route_count; i++ )
+ {
+ struct route *route = &world->routes[i];
+
+ int was_active = route->active;
+
+ route->active = 0;
+ for( u32 j=0; j<pdest->ref_count; j++ )
+ {
+ if( pdest->route_ids[j] == i )
+ {
+ world_routes_verify_run( world, i );
+ route->active = 1;
+ break;
+ }
+ }
+
+ if( was_active && !route->active )
+ {
+ struct route_ui_bar *pui = &world_global.ui_bars[i];
+ pui->fade_start = pui->segment_start;
+ pui->fade_count = pui->segment_count;
+ pui->fade_timer_start = world_global.time;
+
+ world_routes_ui_clear( pui );
+ vg_success( "CLEARING -> %u %u \n", pui->fade_start,
+ pui->fade_count );
+ }
+ }
+
+ world_global.current_run_version ++;
+
+ rc->timing.version = world_global.current_run_version;
+ rc->timing.time = world_global.time;
+ world_global.current_run_version ++;
+}
+
+/*
+ * Notify the UI system that we've reset the player
+ */
+VG_STATIC void world_routes_notify_reset(void)
+{
+ world_global.rewind_from = world_global.time;
+ world_global.rewind_to = world_global.last_use;
+}
+
+/* Rewind between the saved points in time */
+VG_STATIC void world_routes_rollback_time( double t )
+{
+ world_global.time = vg_lerp( world_global.rewind_to,
+ world_global.rewind_from, t );
+}
+
+/* draw lines along the paths */
+VG_STATIC void world_routes_debug( world_instance *world )
+{
+ for( int i=0; i<world->node_count; i++ )
+ {
+ struct route_node *rn = &world->nodes[i];
+ vg_line_pt3( rn->co, 1.0f, rn->special_type? 0xffffff00: 0xff00b2ff );
+ }
+
+ for( int i=0; i<world->route_count; i++ )
+ {
+ struct route *route = &world->routes[i];