8 #include "ent_miniworld.h"
12 #include "control_overlay.h"
14 #include "shaders/model_menu.h"
16 struct global_menu menu
= { .skip_starter
= 0 };
18 void menu_at_begin(void)
20 if( menu
.skip_starter
) return;
22 skaterift
.activity
= k_skaterift_menu
;
23 menu
.page
= k_menu_page_starter
;
28 vg_console_reg_var( "skip_starter_menu", &menu
.skip_starter
,
29 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
30 vg_tex2d_load_qoi_async_file( "textures/prem.qoi",
31 VG_TEX2D_CLAMP
|VG_TEX2D_NOMIP
|VG_TEX2D_NEAREST
,
35 void menu_open( enum menu_page page
)
37 skaterift
.activity
= k_skaterift_menu
;
39 if( page
!= k_menu_page_any
)
45 bool menu_viewing_map(void)
47 return (skaterift
.activity
== k_skaterift_menu
) &&
48 (menu
.page
== k_menu_page_main
) &&
49 (menu
.main_index
== k_menu_main_map
);
52 static void menu_decor_select( ui_rect rect
)
54 ui_px b
= vg_ui
.font
->sx
, hb
= b
/2;
55 ui_rect a0
= { rect
[0] - 20 - hb
, rect
[1] + rect
[3]/2 - hb
, b
,b
},
56 a1
= { rect
[0] + rect
[2] + 20 + hb
, rect
[1] + rect
[3]/2 - hb
, b
,b
};
58 ui_text( a0
, "\x95", 1, k_ui_align_middle_center
, 0 );
59 ui_text( a1
, "\x93", 1, k_ui_align_middle_center
, 0 );
62 static void menu_standard_widget( ui_rect inout_panel
, ui_rect rect
, ui_px s
)
64 ui_split( inout_panel
, k_ui_axis_h
, vg_ui
.font
->sy
*s
*2,
65 8, rect
, inout_panel
);
68 static bool menu_slider( ui_rect inout_panel
, bool select
, const char *label
,
69 const f32 disp_min
, const f32 disp_max
, f32
*value
,
73 menu_standard_widget( inout_panel
, rect
, 1 );
74 ui_label( rect
, label
, 1, 8, box
);
77 enum ui_button_state state
= ui_slider_base( box
, 0, 1, value
, &t
),
79 k_ui_button_holding_inside
|
80 k_ui_button_holding_outside
|
82 mask_brighter
= mask_using
| k_ui_button_hover
;
84 if( vg_input
.display_input_method
== k_input_method_controller
)
88 f32 m
= axis_state( k_sraxis_mbrowse_h
);
91 *value
+= m
* vg
.time_frame_delta
* (1.0f
/2.0f
);
92 *value
= vg_clampf( *value
, 0, 1 );
95 menu_decor_select( rect
);
96 state
|= k_ui_button_hover
;
99 state
= k_ui_button_none
;
102 ui_rect line
= { box
[0], box
[1], t
* (f32
)box
[2], box
[3] };
103 ui_fill( line
, state
&mask_brighter
? GUI_COL_ACTIVE
: GUI_COL_NORM
);
104 ui_fill( (ui_rect
){ box
[0]+line
[2], box
[1], box
[2]-line
[2], box
[3] },
107 ui_outline( box
, 1, state
? GUI_COL_HI
: GUI_COL_ACTIVE
, 0 );
108 ui_slider_text( box
, format
, disp_min
+ (*value
)*( disp_max
-disp_min
) );
110 return (state
& mask_using
) && 1;
113 static bool menu_button( ui_rect inout_panel
, bool select
, const char *text
)
116 menu_standard_widget( inout_panel
, rect
, 1 );
118 enum ui_button_state state
= k_ui_button_none
;
120 if( vg_input
.display_input_method
== k_input_method_controller
)
124 menu_decor_select( rect
);
126 if( button_down( k_srbind_maccept
) )
127 state
= k_ui_button_click
;
132 state
= ui_button_base( rect
);
136 if( state
== k_ui_button_click
)
138 ui_fill( rect
, GUI_COL_DARK
);
140 else if( state
== k_ui_button_holding_inside
)
142 ui_fill( rect
, GUI_COL_DARK
);
144 else if( state
== k_ui_button_holding_outside
)
146 ui_fill( rect
, GUI_COL_DARK
);
147 ui_outline( rect
, 1, GUI_COL_CLICK
, 0 );
149 else if( state
== k_ui_button_hover
)
151 ui_fill( rect
, GUI_COL_ACTIVE
);
152 ui_outline( rect
, 1, GUI_COL_CLICK
, 0 );
156 ui_fill( rect
, select
? GUI_COL_ACTIVE
: GUI_COL_NORM
);
158 ui_outline(rect
, 1, GUI_COL_HI
, 0 );
161 ui_text( rect
, text
, 1, k_ui_align_middle_center
, 0 );
163 if( state
== k_ui_button_click
)
166 audio_oneshot( &audio_ui
[0], 1.0f
, 0.0f
);
173 static bool menu_checkbox( ui_rect inout_panel
, bool select
,
174 const char *str_label
, i32
*data
)
176 ui_rect rect
, label
, box
;
177 menu_standard_widget( inout_panel
, rect
, 1 );
179 ui_split( rect
, k_ui_axis_v
, -rect
[3], 0, label
, box
);
180 ui_text( label
, str_label
, k_ui_scale
, k_ui_align_middle_left
, 0 );
182 enum ui_button_state state
= k_ui_button_none
;
184 if( vg_input
.display_input_method
== k_input_method_controller
)
188 menu_decor_select( rect
);
190 if( button_down( k_srbind_maccept
) )
192 *data
= (*data
) ^ 0x1;
193 state
= k_ui_button_click
;
199 state
= ui_checkbox_base( box
, data
);
203 if( state
== k_ui_button_holding_inside
)
205 ui_fill( box
, GUI_COL_ACTIVE
);
206 ui_outline( box
, 1, GUI_COL_CLICK
, 0 );
208 else if( state
== k_ui_button_holding_outside
)
210 ui_fill( box
, GUI_COL_DARK
);
211 ui_outline( box
, 1, GUI_COL_CLICK
, 0 );
213 else if( state
== k_ui_button_hover
)
215 ui_fill( box
, GUI_COL_ACTIVE
);
216 ui_outline( box
, 1, GUI_COL_HI
, 0 );
220 ui_fill( box
, select
? GUI_COL_ACTIVE
: GUI_COL_DARK
);
221 ui_outline( box
, 1, select
? GUI_COL_HI
: GUI_COL_NORM
, 0 );
226 ui_rect_pad( box
, (ui_px
[2]){8,8} );
227 ui_fill( box
, GUI_COL_HI
);
230 if( state
== k_ui_button_click
)
233 audio_oneshot( &audio_ui
[0], 1.0f
, 0.0f
);
240 static void menu_heading( ui_rect inout_panel
, const char *label
, u32 colour
)
243 menu_standard_widget( inout_panel
, rect
, 1 );
248 u32 c0
= ui_opacity( GUI_COL_DARK
, 0.36f
),
249 c1
= ui_opacity( GUI_COL_DARK
, 0.5f
);
251 struct ui_vert
*vs
= ui_fill( rect
, c0
);
257 ui_text( rect
, label
, 1, k_ui_align_middle_center
, 1 );
260 ui_text( rect
, label
, 1, k_ui_align_middle_center
, colour
? colour
:
261 ui_colour(k_ui_blue
+k_ui_brighter
) );
264 static u32
medal_colour( u32 flags
)
266 if( flags
& k_ent_route_flag_achieve_gold
)
267 return ui_colour( k_ui_yellow
);
268 else if( flags
& k_ent_route_flag_achieve_silver
)
269 return ui_colour( k_ui_fg
);
273 static i32
menu_nav( i32
*p_row
, int mv
, i32 max
)
275 i32 row_prev
= *p_row
;
277 if( mv
< 0 ) *p_row
= vg_min( max
, (*p_row
) +1 );
278 if( mv
> 0 ) *p_row
= vg_max( 0, (*p_row
) -1 );
280 if( *p_row
!= row_prev
)
283 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
290 static void menu_try_find_cam( i32 id
)
292 world_instance
*world
= &world_static
.instances
[0];
293 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_npc
); i
++ )
295 ent_npc
*fnpc
= mdl_arritm( &world
->ent_npc
, i
);
296 if( (fnpc
->id
== 50) && (fnpc
->context
== id
) )
298 if( mdl_entity_id_type(fnpc
->camera
) == k_ent_camera
)
300 u32 index
= mdl_entity_id_id( fnpc
->camera
);
301 menu
.bg_cam
= mdl_arritm( &world
->ent_camera
, index
);
308 static void menu_link_modal( const char *url
)
312 /* Only reset the choice of browser if 'No' was selected. */
313 if( menu
.web_choice
== 2 )
319 if( button_down( k_srbind_mopen
) )
321 if( skaterift
.activity
== k_skaterift_default
)
323 menu_open( k_menu_page_main
);
328 if( skaterift
.activity
!= k_skaterift_menu
)
334 /* get buttons inputs
335 * -------------------------------------------------------------------*/
336 int ml
= button_press( k_srbind_mleft
),
337 mr
= button_press( k_srbind_mright
),
338 mu
= button_press( k_srbind_mup
),
339 md
= button_press( k_srbind_mdown
),
342 enter
= button_down( k_srbind_maccept
);
345 u8 lb_down
= 0, rb_down
= 0;
346 vg_exec_input_program( k_vg_input_type_button_u8
,
348 vg_joy_button
, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
, vg_end
350 vg_exec_input_program( k_vg_input_type_button_u8
,
352 vg_joy_button
, SDL_CONTROLLER_BUTTON_LEFTSHOULDER
, vg_end
355 int mtab
= (i32
)rb_down
- (i32
)lb_down
;
357 if( menu
.repeater
> 0.0f
)
359 menu
.repeater
-= vg_minf( vg
.time_frame_delta
, 0.5f
);
366 if( mv
|| mh
|| mtab
)
367 menu
.repeater
+= 0.2f
;
370 if( vg_input
.display_input_method
== k_input_method_kbm
)
372 vg_ui
.wants_mouse
= 1;
375 if( skaterift
.activity
!= k_skaterift_menu
) return;
380 menu_try_find_cam( 3 );
382 ui_rect panel
= { 0,0, 800, 200 },
383 screen
= { 0,0, vg
.window_x
,vg
.window_y
};
384 ui_rect_center( screen
, panel
);
385 ui_fill( panel
, GUI_COL_DARK
);
386 ui_outline( panel
, 1, GUI_COL_NORM
, 0 );
387 ui_rect_pad( panel
, (ui_px
[]){8,8} );
390 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
391 ui_font_face( &vgf_default_title
);
392 ui_text( title
, "Open Link?", 1, k_ui_align_middle_center
, 0 );
394 ui_split( panel
, k_ui_axis_h
, 28, 0, title
, panel
);
395 ui_font_face( &vgf_default_large
);
396 ui_text( title
, menu
.web_link
, 1, k_ui_align_middle_center
, 0 );
398 ui_rect end
= { panel
[0], panel
[1] + panel
[3] - 48, panel
[2], 48 };
401 ui_split_ratio( end
, k_ui_axis_v
, 2.0/3.0, 2, a
, c
);
402 ui_split_ratio( a
, k_ui_axis_v
, 1.0/2.0, 2, a
, b
);
404 i32 R
= menu_nav( &menu
.web_choice
, mh
, 2 );
406 if( menu_button( a
, R
==0, "Steam Overlay" ) )
410 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
411 SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage( hSteamFriends
,
413 k_EActivateGameOverlayToWebPageMode_Default
);
414 menu
.web_link
= NULL
;
418 if( menu_button( b
, R
==1, "Web Browser" ) )
422 vg_strnull( &str
, buf
, sizeof(buf
) );
424 vg_strcat( &str
, "start " );
426 vg_strcat( &str
, "xdg-open " );
428 vg_strcat( &str
, menu
.web_link
);
430 if( vg_strgood(&str
) )
433 menu
.web_link
= NULL
;
436 if( menu_button( c
, R
==2, "No" ) || button_down( k_srbind_mback
) )
439 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
441 menu
.web_link
= NULL
;
448 if( vg
.settings_open
)
450 if( button_down( k_srbind_mback
) )
453 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
456 srinput
.state
= k_input_state_resume
;
462 if( menu
.page
== k_menu_page_credits
)
464 ui_rect panel
= { 0,0, 600, 400 },
465 screen
= { 0,0, vg
.window_x
,vg
.window_y
};
466 ui_rect_center( screen
, panel
);
467 ui_fill( panel
, GUI_COL_DARK
);
468 ui_outline( panel
, 1, GUI_COL_NORM
, 0 );
469 ui_rect_pad( panel
, (ui_px
[]){8,8} );
472 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
473 ui_font_face( &vgf_default_title
);
474 ui_text( title
, "Skate Rift - Credits", 1, k_ui_align_middle_center
, 0 );
476 ui_split( panel
, k_ui_axis_h
, 28, 0, title
, panel
);
477 ui_font_face( &vgf_default_large
);
478 ui_text( title
, "Mt.Zero Software", 1, k_ui_align_middle_center
, 0 );
480 ui_split( panel
, k_ui_axis_h
, 8, 0, title
, panel
);
481 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
482 ui_font_face( &vgf_default_title
);
483 ui_text( title
, "Free Software", 1, k_ui_align_middle_center
, 0 );
485 ui_split( panel
, k_ui_axis_h
, 8, 0, title
, panel
);
486 ui_font_face( &vgf_default_large
);
488 "Sam Lantinga - SDL2 - libsdl.org\n"
489 "Hunter WB - Anyascii\n"
490 "David Herberth - GLAD\n"
491 "Dominic Szablewski - QOI - qoiformat.org\n"
492 "Sean Barrett - stb_image, stb_vorbis,\n"
494 "Khronos Group - OpenGL\n"
495 , 1, k_ui_align_left
, 0 );
497 ui_rect end
= { panel
[0], panel
[1] + panel
[3] - 64, panel
[2], 64 };
499 if( menu_button( end
, 1, "Back" ) || button_down( k_srbind_mback
) )
501 menu
.page
= k_menu_page_main
;
506 else if( menu
.page
== k_menu_page_starter
)
508 i32 R
= menu_nav( &menu
.intro_row
, mv
, 3 );
509 ui_rect panel
= { 0,0, 600, 400 },
510 screen
= { 0,0, vg
.window_x
,vg
.window_y
};
511 ui_rect_center( screen
, panel
);
512 ui_fill( panel
, ui_opacity( GUI_COL_DARK
, 0.35f
) );
513 ui_outline( panel
, 1, GUI_COL_NORM
, 0 );
514 ui_rect_pad( panel
, (ui_px
[]){8,8} );
517 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
518 ui_font_face( &vgf_default_title
);
519 ui_text( title
, "Welcome to Skate Rift", 1, k_ui_align_middle_center
, 0 );
521 ui_split( panel
, k_ui_axis_h
, 28, 0, title
, panel
);
522 ui_font_face( &vgf_default_large
);
524 menu_checkbox( panel
, R
== 0,
525 "Show controls overlay (good for new players)",
526 &control_overlay
.enabled
);
527 menu_checkbox( panel
, R
== 1, "Auto connect to global server",
528 &network_client
.auto_connect
);
530 ui_rect end
= { panel
[0], panel
[1] + panel
[3] - 100, panel
[2], 100 };
531 menu_checkbox( end
, R
== 2, "Don't show this again", &menu
.skip_starter
);
532 if( menu_button( end
, R
== 3, "OK" ) )
534 menu
.page
= k_menu_page_main
;
535 skaterift
.activity
= k_skaterift_default
;
538 menu_try_find_cam( 3 );
541 else if( menu
.page
== k_menu_page_premium
)
543 i32 R
= menu_nav( &menu
.prem_row
, mh
, 1 );
544 ui_rect panel
= { 0,0, 600, 400+240 },
545 screen
= { 0,0, vg
.window_x
,vg
.window_y
};
546 ui_rect_center( screen
, panel
);
547 ui_fill( panel
, ui_opacity( GUI_COL_DARK
, 0.35f
) );
548 ui_outline( panel
, 1, GUI_COL_NORM
, 0 );
549 ui_rect_pad( panel
, (ui_px
[]){8,8} );
552 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
553 ui_font_face( &vgf_default_title
);
554 ui_text( title
, "Content is in the full game.",
555 1, k_ui_align_middle_center
, 0 );
557 ui_split( panel
, k_ui_axis_h
, 28, 0, title
, panel
);
558 ui_font_face( &vgf_default_large
);
561 ui_split( panel
, k_ui_axis_h
, 456, 0, img
, panel
);
562 ui_image( img
, menu
.prem_tex
);
564 ui_rect end
= { panel
[0], panel
[1] + panel
[3] - 48, panel
[2], 48 }, a
,b
;
565 ui_split_ratio( end
, k_ui_axis_v
, 0.5f
, 2, a
, b
);
567 if( menu_button( a
, R
== 0, "Store Page" ) )
570 SteamAPI_ISteamFriends_ActivateGameOverlayToStore(
571 SteamAPI_SteamFriends(), 2103940, k_EOverlayToStoreFlag_None
);
574 if( menu_button( b
, R
== 1, "Nah" ) || button_down( k_srbind_mback
) )
577 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
579 skaterift
.activity
= k_skaterift_default
;
587 * -------------------------------------------------------------------*/
589 ui_font_face( &vgf_default_title
);
590 ui_px height
= vg_ui
.font
->ch
+ 16;
591 ui_rect topbar
= { 0, 0, vg
.window_x
, height
};
593 const char *opts
[] = {
594 [k_menu_main_main
] = "Menu",
595 [k_menu_main_map
] = "Map",
596 [k_menu_main_settings
] = "Settings",
597 [k_menu_main_guide
] = "Guides"
602 menu
.main_index
+= mtab
;
604 if( menu
.main_index
== -1 )
607 if( menu
.main_index
== vg_list_size(opts
) )
611 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
615 ui_px x
= 0, spacer
= 8;
616 for( u32 draw
=0; draw
<2; draw
++ )
618 if( vg_input
.display_input_method
== k_input_method_controller
)
622 ui_rect inf_lb
= { x
, 0, 32, height
};
623 ui_fill( inf_lb
, lb_down
? GUI_COL_NORM
: GUI_COL_NORM
);
624 ui_text( inf_lb
, "\x91", 1, k_ui_align_middle_center
, 0 );
629 for( i32 i
=0; i
<vg_list_size(opts
); i
++ )
631 ui_rect box
= { x
, 0, ui_text_line_width(opts
[i
]) + 32, height
};
635 enum ui_button_state state
= ui_button_base( box
);
636 if( state
== k_ui_button_click
)
638 ui_fill( box
, GUI_COL_DARK
);
641 else if( state
== k_ui_button_holding_inside
)
643 ui_fill( box
, GUI_COL_DARK
);
645 else if( state
== k_ui_button_holding_outside
)
647 ui_fill( box
, GUI_COL_DARK
);
648 ui_outline( box
, 1, GUI_COL_CLICK
, 0 );
650 else if( state
== k_ui_button_hover
)
652 ui_fill( box
, GUI_COL_NORM
);
653 ui_outline( box
, 1, GUI_COL_ACTIVE
, 0 );
656 ui_fill( box
, i
==menu
.main_index
? GUI_COL_ACTIVE
: GUI_COL_NORM
);
658 ui_text( box
, opts
[i
], 1, k_ui_align_middle_center
, 0 );
661 x
+= box
[2] + spacer
;
664 if( vg_input
.display_input_method
== k_input_method_controller
)
668 ui_rect inf_rb
= { x
, 0, 32, height
};
669 ui_fill( inf_rb
, rb_down
? GUI_COL_NORM
: GUI_COL_NORM
);
670 ui_text( inf_rb
, "\x92", 1, k_ui_align_middle_center
, 0 );
676 ui_fill( (ui_rect
){ x
+8,0, vg
.window_x
-(x
+8),height
}, GUI_COL_NORM
);
679 x
= vg
.window_x
/2 - x
/2;
680 ui_fill( (ui_rect
){ 0, 0, x
-8, height
}, GUI_COL_NORM
);
684 if( menu
.main_index
== k_menu_main_map
)
687 ui_font_face( &vgf_default_large
);
688 ui_rect title
= { vg
.window_x
/2 - 512/2, height
+8, 512, 64 };
694 ui_fill( (ui_rect
){ x
,y
, 0,height
},
695 world_static
.active_instance
? GUI_COL_DARK
: GUI_COL_ACTIVE
);
699 vg_strnull( &str
, buf
, sizeof(buf
) );
700 vg_strcat( &str
, "Hub World" );
702 if( world_static
.active_instance
)
704 vg_strcat( &str
, " (" );
705 vg_input_string( &str
, input_button_list
[k_srbind_mhub
], 1 );
706 vg_strcatch( &str
, '\x06' );
707 vg_strcatch( &str
, '\x00' );
708 vg_strcat( &str
, "\x1B[0m)" );
711 ui_px w
= ui_text( (ui_rect
){ x
+8, y
, 1000, height
}, buf
, 1,
712 k_ui_align_middle_left
, 0 );
721 world_instance
*world
= &world_static
.instances
[1];
722 if( world
->status
== k_world_status_loaded
)
724 const char *world_name
=
725 mdl_pstr( &world
->meta
, world
->info
.pstr_name
);
727 vg_strnull( &str
, buf
, sizeof(buf
) );
728 vg_strcat( &str
, world_name
);
730 if( !world_static
.active_instance
&&
731 (vg_input
.display_input_method
== k_input_method_controller
) )
733 vg_strcat( &str
, " (" );
734 vg_input_string( &str
, input_button_list
[k_srbind_mhub
], 1 );
735 vg_strcatch( &str
, '\x06' );
736 vg_strcatch( &str
, '\x00' );
737 vg_strcat( &str
, "\x1B[0m)" );
740 vs
= ui_fill( (ui_rect
){ x
,y
, 1000,height
},
741 world_static
.active_instance
? GUI_COL_ACTIVE
: GUI_COL_DARK
);
742 w
= ui_text( (ui_rect
){ x
+16,y
, 1000,height
}, buf
,
743 1, k_ui_align_middle_left
, 0 );
745 w
= w
*vg_ui
.font
->sx
+ 8*3;
748 if( button_down( k_srbind_mhub
) ||
749 ui_button_base( (ui_rect
){0,y
,x
,height
} ) == k_ui_button_click
)
751 world_switch_instance( world_static
.active_instance
^ 0x1 );
752 skaterift
.activity
= k_skaterift_default
;
753 world_map
.view_ready
= 0;
764 if( world_static
.active_instance
)
766 ui_rect stat_panel
= { x
,y
, 256,vg
.window_y
-y
};
767 u32 c0
= ui_opacity( GUI_COL_DARK
, 0.36f
);
768 struct ui_vert
*vs
= ui_fill( stat_panel
, c0
);
770 ui_rect_pad( stat_panel
, (ui_px
[2]){8,0} );
772 for( u32 i
=0; i
<mdl_arrcount( &world
->ent_region
); i
++ )
774 ent_region
*region
= mdl_arritm( &world
->ent_region
, i
);
776 if( !region
->zone_volume
)
779 const char *title
= mdl_pstr( &world
->meta
, region
->pstr_title
);
780 ui_font_face( &vgf_default_large
);
783 menu_standard_widget( stat_panel
, title_box
, 1 );
787 ui_font_face( &vgf_default_small
);
789 ent_volume
*volume
= mdl_arritm(&world
->ent_volume
,
790 mdl_entity_id_id(region
->zone_volume
));
792 u32 combined
= k_ent_route_flag_achieve_gold
|
793 k_ent_route_flag_achieve_silver
;
798 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_route
); j
++ )
800 ent_route
*route
= mdl_arritm(&world
->ent_route
, j
);
803 m4x3_mulv( volume
->to_local
, route
->board_transform
[3], local
);
804 if( !((fabsf(local
[0]) <= 1.0f
) &&
805 (fabsf(local
[1]) <= 1.0f
) &&
806 (fabsf(local
[2]) <= 1.0f
)) )
811 combined
&= route
->flags
;
813 vg_strnull( &str
, buf
, sizeof(buf
) );
814 vg_strcat( &str
, "(Race) " );
815 vg_strcat( &str
, mdl_pstr(&world
->meta
, route
->pstr_name
));
817 if( route
->flags
& k_ent_route_flag_achieve_silver
)
818 vg_strcat( &str
, " \xb3");
819 if( route
->flags
& k_ent_route_flag_achieve_gold
)
820 vg_strcat( &str
, "\xb3");
823 ui_standard_widget( stat_panel
, r
, 1 );
824 ui_text( r
, buf
, 1, k_ui_align_middle_left
,
825 medal_colour(route
->flags
) );
828 for( u32 j
=0; j
<mdl_arrcount(&world
->ent_challenge
); j
++ )
830 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, j
);
833 m4x3_mulv( volume
->to_local
, challenge
->transform
.co
, local
);
834 if( !((fabsf(local
[0]) <= 1.0f
) &&
835 (fabsf(local
[1]) <= 1.0f
) &&
836 (fabsf(local
[2]) <= 1.0f
)) )
841 vg_strnull( &str
, buf
, sizeof(buf
) );
842 vg_strcat( &str
, mdl_pstr(&world
->meta
, challenge
->pstr_alias
));
845 if( challenge
->status
)
847 flags
|= k_ent_route_flag_achieve_gold
;
848 flags
|= k_ent_route_flag_achieve_silver
;
849 vg_strcat( &str
, " \xb3\xb3" );
855 ui_standard_widget( stat_panel
, r
, 1 );
856 ui_text( r
, buf
, 1, k_ui_align_middle_left
, medal_colour(flags
) );
863 if( combined
& k_ent_route_flag_achieve_gold
)
864 title_col
= ui_colour( k_ui_yellow
);
865 else if( combined
& k_ent_route_flag_achieve_silver
)
866 title_col
= ui_colour( k_ui_fg
);
868 menu_heading( title_box
, title
, title_col
);
871 vs
[2].co
[1] = stat_panel
[1];
872 vs
[3].co
[1] = stat_panel
[1];
877 if( button_down( k_srbind_mback
) )
880 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
882 skaterift
.activity
= k_skaterift_default
;
886 ui_rect list0
= { vg
.window_x
/2 - 512/2, height
+32,
887 512, vg
.window_y
-height
-64 }, list
;
888 rect_copy( list0
, list
);
889 ui_rect_pad( list
, (ui_px
[2]){8,8} );
892 * -------------------------------------------------------------------*/
894 if( menu
.main_index
== k_menu_main_main
)
896 i32 R
= menu_nav( &menu
.main_row
, mv
, 2 );
898 if( menu_button( list
, R
== 0, "Resume" ) )
900 skaterift
.activity
= k_skaterift_default
;
904 if( menu_button( list
, R
== 1, "Credits" ) )
906 menu
.page
= k_menu_page_credits
;
909 ui_rect end
= { list
[0], list
[1]+list
[3]-64, list
[2], 72 };
910 if( menu_button( end
, R
== 2, "Quit Game" ) )
912 vg
.window_should_close
= 1;
915 else if( menu
.main_index
== k_menu_main_settings
)
917 ui_fill( list0
, ui_opacity( GUI_COL_DARK
, 0.36f
) );
918 ui_outline( list0
, 1, GUI_COL_NORM
, 0 );
919 i32 R
= menu_nav( &menu
.settings_row
, mv
, 8 );
921 ui_font_face( &vgf_default_large
);
923 menu_heading( list
, "Game", 0 );
924 menu_checkbox( list
, R
== 0, "Show controls overlay",
925 &control_overlay
.enabled
);
926 menu_checkbox( list
, R
== 1, "Auto connect to global server",
927 &network_client
.auto_connect
);
929 menu_heading( list
, "Audio/Video", 0 );
930 menu_slider( list
, R
== 2, "Volume", 0, 100,
931 &vg_audio
.external_global_volume
, "%.f%%" );
932 menu_slider( list
, R
== 3, "Resolution", 0, 100,
933 &k_render_scale
, "%.f%%" );
934 menu_checkbox( list
, R
== 4, "Motion Blur", &k_blur_effect
);
936 menu_heading( list
, "Camera", 0 );
937 menu_slider( list
, R
== 5, "Fov", 97, 135,
938 &k_fov
, "%.1f\xb0" );
939 menu_slider( list
, R
== 6, "Cam Height", -0.4f
, +1.4f
,
940 &k_cam_height
, vg_lerpf(-0.4f
,1.4f
,k_cam_height
)>=0.0f
?
942 menu_checkbox( list
, R
== 7, "Invert Y Axis", &k_invert_y
);
945 ui_rect end
= { list
[0], list
[1]+list
[3]-64, list
[2], 72 };
946 ui_font_face( &vgf_default_small
);
947 menu_heading( end
, "Advanced", 0 );
948 if( menu_button( end
, R
== 8, "Open Engine Settings" ) )
953 else if( menu
.main_index
== k_menu_main_guide
)
959 marg
= list0
[0]+list0
[2],
960 pw
= vg
.window_x
- marg
,
961 infx
= pw
/2 - w
/2 + marg
;
962 ui_rect inf
= { infx
, height
+32, w
, 800 };
964 struct ui_vert
*vs
= NULL
;
966 if( menu
.guide_sel
&& (menu
.guide_sel
<= 3) )
968 vs
= ui_fill( inf
, ui_opacity( GUI_COL_DARK
, 0.20f
) );
969 ui_rect_pad( inf
, (ui_px
[]){8,8} );
972 ui_fill( list0
, ui_opacity( GUI_COL_DARK
, 0.36f
) );
973 ui_outline( list0
, 1, GUI_COL_NORM
, 0 );
974 i32 R
= menu_nav( &menu
.guides_row
, mv
, 6 );
976 ui_font_face( &vgf_default_large
);
978 menu_heading( list
, "Info", 0 );
979 if( menu
.guide_sel
== 1 )
981 menu_try_find_cam( 1 );
984 ui_split( inf
, k_ui_axis_h
, 28*2, 0, title
, inf
);
985 ui_font_face( &vgf_default_title
);
986 ui_text( title
, "Where to go", 1, k_ui_align_middle_center
, 0 );
988 ui_split( inf
, k_ui_axis_h
, 28, 0, title
, inf
);
989 ui_font_face( &vgf_default_large
);
991 "Visit the sandcastles built by John Cockroach to be\n"
992 "transported into the real location. Use the map in the\n"
993 "menu to return back this hub island.\n"
995 "You can begin training at the Volcano Island, where you\n"
996 "can learn movement skills. Then travel to Mt.Zero Island\n"
997 "or Bort Downtown to test them out. Finally the most\n"
998 "challenging course can be taken on at the Valley.\n"
1000 "Also visit the Steam Workshop for community made\n"
1001 "locations to skate!"
1002 , 1, k_ui_align_left
, 0 );
1004 vs
[2].co
[1] = vs
[0].co
[1] + 84 + vgf_default_large
.sy
*11 + 16;
1005 vs
[3].co
[1] = vs
[2].co
[1];
1007 if( menu_button( list
, R
== 0, "Where to go" ) ) menu
.guide_sel
= 1;
1009 if( menu
.guide_sel
== 3 )
1011 menu_try_find_cam( 2 );
1014 ui_split( inf
, k_ui_axis_h
, 28*2, 0, title
, inf
);
1015 ui_font_face( &vgf_default_title
);
1016 ui_text( title
, "Online", 1, k_ui_align_middle_center
, 0 );
1018 ui_split( inf
, k_ui_axis_h
, 28, 0, title
, inf
);
1019 ui_font_face( &vgf_default_large
);
1021 "Connection to the global server is managed by this radar\n"
1022 "dish in the hub island. Come back here to turn the\n"
1023 "connection on or off.\n"
1025 "You'll then see other players or your friends if you go\n"
1026 "to the same location, and your highscores will be saved\n"
1027 "onto the scoreboards."
1028 , 1, k_ui_align_left
, 0 );
1030 vs
[2].co
[1] = vs
[0].co
[1] + 84 + vgf_default_large
.sy
*7 + 16;
1031 vs
[3].co
[1] = vs
[2].co
[1];
1033 if( menu_button( list
, R
== 1, "Playing Online" ) ) menu
.guide_sel
= 3;
1035 menu_heading( list
, "Controls", 0 );
1036 if( menu_button( list
, R
== 2, "Skating \xb2" ) )
1040 "https://skaterift.com/index.php?page=movement" );
1042 if( menu
.guide_sel
== 0 || menu
.guide_sel
> 3 ) menu_try_find_cam( 3 );
1044 if( menu_button( list
, R
== 3, "Tricks \xb2" ) )
1048 "https://skaterift.com/index.php?page=tricks" );
1051 menu_heading( list
, "Workshop", 0 );
1052 if( menu_button( list
, R
== 4, "Create a Board \xb2" ) )
1056 "https://skaterift.com/index.php?page=workshop_board" );
1058 if( menu_button( list
, R
== 5, "Create a World \xb2" ) )
1062 "https://skaterift.com/index.php?page=workshop_world" );
1064 if( menu_button( list
, R
== 6, "Create a Playermodel \xb2" ) )
1068 "https://skaterift.com/index.php?page=workshop_player" );
1075 vg_ui
.frosting
= 0.015f
;
1076 ui_flush( k_ui_shader_colour
, vg
.window_x
, vg
.window_y
);
1077 vg_ui
.frosting
= 0.0f
;
1079 ui_font_face( &vgf_default_small
);