add rest of original settings
[carveJwlIkooP6JGAAIwe30JlM.git] / menu.c
1 #pragma once
2 #include "skaterift.h"
3 #include "menu.h"
4 #include "model.h"
5 #include "entity.h"
6 #include "input.h"
7 #include "world_map.h"
8 #include "ent_miniworld.h"
9 #include "audio.h"
10 #include "workshop.h"
11 #include "gui.h"
12 #include "control_overlay.h"
13 #include "network.h"
14 #include "shaders/model_menu.h"
15
16 struct global_menu menu = { .skip_starter = 0 };
17
18 void menu_at_begin(void)
19 {
20 if( menu.skip_starter ) return;
21
22 skaterift.activity = k_skaterift_menu;
23 menu.page = k_menu_page_starter;
24 }
25
26 void menu_init(void)
27 {
28 vg_console_reg_var( "skip_starter_menu", &menu.skip_starter,
29 k_var_dtype_i32, VG_VAR_PERSISTENT );
30 }
31
32 void menu_open( enum menu_page page )
33 {
34 skaterift.activity = k_skaterift_menu;
35
36 if( page != k_menu_page_any )
37 {
38 menu.page = page;
39 }
40 }
41
42 bool menu_viewing_map(void)
43 {
44 return (skaterift.activity == k_skaterift_menu) &&
45 (menu.page == k_menu_page_main) &&
46 (menu.main_index == k_menu_main_map);
47 }
48
49 static void menu_decor_select( ui_rect rect )
50 {
51 ui_px b = vg_ui.font->sx, hb = b/2;
52 ui_rect a0 = { rect[0] - 20 - hb, rect[1] + rect[3]/2 - hb, b,b },
53 a1 = { rect[0] + rect[2] + 20 + hb, rect[1] + rect[3]/2 - hb, b,b };
54
55 ui_text( a0, "\x95", 1, k_ui_align_middle_center, 0 );
56 ui_text( a1, "\x93", 1, k_ui_align_middle_center, 0 );
57 }
58
59 static void menu_standard_widget( ui_rect inout_panel, ui_rect rect, ui_px s )
60 {
61 ui_split( inout_panel, k_ui_axis_h, vg_ui.font->sy*s*2,
62 8, rect, inout_panel );
63 }
64
65 static bool menu_slider( ui_rect inout_panel, bool select, const char *label,
66 const f32 disp_min, const f32 disp_max, f32 *value,
67 const char *format )
68 {
69 ui_rect rect, box;
70 menu_standard_widget( inout_panel, rect, 1 );
71 ui_label( rect, label, 1, 8, box );
72
73 f32 t;
74 enum ui_button_state state = ui_slider_base( box, 0, 1, value, &t ),
75 mask_using =
76 k_ui_button_holding_inside |
77 k_ui_button_holding_outside |
78 k_ui_button_click,
79 mask_brighter = mask_using | k_ui_button_hover;
80
81 if( menu.input_mode == k_menu_input_mode_keys )
82 {
83 if( select )
84 {
85 f32 m = axis_state( k_sraxis_mbrowse_h );
86 if( fabsf(m) > 0.5f )
87 {
88 *value += m * vg.time_frame_delta * (1.0f/2.0f);
89 *value = vg_clampf( *value, 0, 1 );
90 }
91
92 menu_decor_select( rect );
93 state |= k_ui_button_hover;
94 }
95 }
96
97 ui_rect line = { box[0], box[1], t * (f32)box[2], box[3] };
98 ui_fill( line, state&mask_brighter? GUI_COL_ACTIVE: GUI_COL_NORM );
99 ui_fill( (ui_rect){ box[0]+line[2], box[1], box[2]-line[2], box[3] },
100 GUI_COL_DARK );
101
102 ui_outline( box, 1, state? GUI_COL_HI: GUI_COL_ACTIVE, 0 );
103 ui_slider_text( box, format, disp_min + (*value)*( disp_max-disp_min ) );
104
105 return (state & mask_using) && 1;
106 }
107
108 static bool menu_button( ui_rect inout_panel, bool select, const char *text )
109 {
110 ui_rect rect;
111 menu_standard_widget( inout_panel, rect, 1 );
112
113 enum ui_button_state state = k_ui_button_none;
114
115 if( menu.input_mode == k_menu_input_mode_keys )
116 {
117 if( select )
118 {
119 menu_decor_select( rect );
120
121 if( button_down( k_srbind_maccept ) )
122 state = k_ui_button_click;
123 }
124 }
125 else
126 {
127 state = ui_button_base( rect );
128 select = 0;
129 }
130
131 if( state == k_ui_button_click )
132 {
133 ui_fill( rect, GUI_COL_DARK );
134 }
135 else if( state == k_ui_button_holding_inside )
136 {
137 ui_fill( rect, GUI_COL_DARK );
138 }
139 else if( state == k_ui_button_holding_outside )
140 {
141 ui_fill( rect, GUI_COL_DARK );
142 ui_outline( rect, 1, GUI_COL_CLICK, 0 );
143 }
144 else if( state == k_ui_button_hover )
145 {
146 ui_fill( rect, GUI_COL_ACTIVE );
147 ui_outline( rect, 1, GUI_COL_CLICK, 0 );
148 }
149 else
150 {
151 ui_fill( rect, select? GUI_COL_ACTIVE: GUI_COL_NORM );
152 if( select )
153 ui_outline(rect, 1, GUI_COL_HI, 0 );
154 }
155
156 ui_text( rect, text, 1, k_ui_align_middle_center, 0 );
157 return state == k_ui_button_click;
158 }
159
160 static bool menu_checkbox( ui_rect inout_panel, bool select,
161 const char *str_label, i32 *data )
162 {
163 ui_rect rect, label, box;
164 menu_standard_widget( inout_panel, rect, 1 );
165
166 ui_split( rect, k_ui_axis_v, -rect[3], 0, label, box );
167 ui_text( label, str_label, k_ui_scale, k_ui_align_middle_left, 0 );
168
169 enum ui_button_state state = k_ui_button_none;
170
171 if( menu.input_mode == k_menu_input_mode_keys )
172 {
173 if( select )
174 {
175 menu_decor_select( rect );
176
177 if( button_down( k_srbind_maccept ) )
178 {
179 *data = (*data) ^ 0x1;
180 state = k_ui_button_click;
181 }
182 }
183 }
184 else
185 {
186 state = ui_checkbox_base( box, data );
187 select = 0;
188 }
189
190 if( state == k_ui_button_holding_inside )
191 {
192 ui_fill( box, GUI_COL_ACTIVE );
193 ui_outline( box, 1, GUI_COL_CLICK, 0 );
194 }
195 else if( state == k_ui_button_holding_outside )
196 {
197 ui_fill( box, GUI_COL_DARK );
198 ui_outline( box, 1, GUI_COL_CLICK, 0 );
199 }
200 else if( state == k_ui_button_hover )
201 {
202 ui_fill( box, GUI_COL_ACTIVE );
203 ui_outline( box, 1, GUI_COL_HI, 0 );
204 }
205 else
206 {
207 ui_fill( box, select? GUI_COL_ACTIVE: GUI_COL_DARK );
208 ui_outline( box, 1, select? GUI_COL_HI: GUI_COL_NORM, 0 );
209 }
210
211 bool changed = (state == k_ui_button_click);
212
213 if( *data )
214 {
215 ui_rect_pad( box, (ui_px[2]){8,8} );
216 ui_fill( box, GUI_COL_HI );
217 }
218
219 return changed;
220 }
221
222 static void menu_heading( ui_rect inout_panel, const char *label )
223 {
224 ui_rect rect;
225 menu_standard_widget( inout_panel, rect, 1 );
226
227 rect[0] -= 8;
228 rect[2] += 16;
229
230 u32 c0 = ui_opacity( GUI_COL_DARK, 0.36f ),
231 c1 = ui_opacity( GUI_COL_DARK, 0.5f );
232
233 struct ui_vert *vs = ui_fill( rect, c0 );
234
235 vs[0].colour = c1;
236 vs[1].colour = c1;
237
238 rect[1] += 4;
239 ui_text( rect, label, 1, k_ui_align_middle_center, 1 );
240 rect[0] += 1;
241 rect[1] -= 1;
242 ui_text( rect, label, 1, k_ui_align_middle_center,
243 ui_colour(k_ui_blue+k_ui_brighter) );
244 }
245
246 void menu_gui(void)
247 {
248 if( button_down( k_srbind_mopen ) )
249 {
250 if( skaterift.activity == k_skaterift_default )
251 {
252 menu_open( k_menu_page_main );
253 return;
254 }
255 }
256
257 if( skaterift.activity != k_skaterift_menu )
258 return;
259
260 /* get buttons inputs
261 * -------------------------------------------------------------------*/
262 int ml = button_down( k_srbind_mleft ),
263 mr = button_down( k_srbind_mright ),
264 mu = button_down( k_srbind_mup ),
265 md = button_down( k_srbind_mdown ),
266 mh = ml-mr,
267 mv = mu-md,
268 enter = button_down( k_srbind_maccept );
269
270 if( mh||mv||enter )
271 {
272 menu.input_mode = k_menu_input_mode_keys;
273 }
274
275 /* get mouse inputs
276 * --------------------------------------------------------------------*/
277 menu.mouse_dist += v2_length( vg.mouse_delta ); /* TODO: Move to UI */
278 if( menu.mouse_dist > 10.0f )
279 {
280 menu.input_mode = k_menu_input_mode_mouse;
281 menu.mouse_dist = 0.0f;
282 }
283
284 if( ui_clicking(UI_MOUSE_LEFT) || ui_clicking(UI_MOUSE_RIGHT) )
285 {
286 menu.input_mode = k_menu_input_mode_mouse;
287 }
288
289 if( menu.input_mode == k_menu_input_mode_mouse )
290 {
291 /*
292 * handle mouse input
293 * ------------------------------------------------------------*/
294 vg_ui.wants_mouse = 1;
295 }
296
297 if( skaterift.activity != k_skaterift_menu ) return;
298
299 if( vg.settings_open )
300 {
301 if( button_down( k_srbind_mback ) )
302 {
303 vg_settings_close();
304 srinput.state = k_input_state_resume;
305 }
306
307 return;
308 }
309
310 if( menu.page == k_menu_page_credits )
311 {
312 ui_rect panel = { 0,0, 600, 400 },
313 screen = { 0,0, vg.window_x,vg.window_y };
314 ui_rect_center( screen, panel );
315 ui_fill( panel, GUI_COL_DARK );
316 ui_outline( panel, 1, GUI_COL_NORM, 0 );
317 ui_rect_pad( panel, (ui_px[]){8,8} );
318
319 ui_rect title;
320 ui_split( panel, k_ui_axis_h, 28*2, 0, title, panel );
321 ui_font_face( &vgf_default_title );
322 ui_text( title, "Skate Rift - Credits", 1, k_ui_align_middle_center, 0 );
323
324 ui_split( panel, k_ui_axis_h, 28, 0, title, panel );
325 ui_font_face( &vgf_default_large );
326 ui_text( title, "Mt.Zero Software", 1, k_ui_align_middle_center, 0 );
327
328 ui_split( panel, k_ui_axis_h, 8, 0, title, panel );
329 ui_split( panel, k_ui_axis_h, 28*2, 0, title, panel );
330 ui_font_face( &vgf_default_title );
331 ui_text( title, "Free Software", 1, k_ui_align_middle_center, 0 );
332
333 ui_split( panel, k_ui_axis_h, 8, 0, title, panel );
334 ui_font_face( &vgf_default_large );
335 ui_text( panel,
336 "Sam Lantinga - SDL2 - libsdl.org\n"
337 "Hunter WB - Anyascii\n"
338 "David Herberth - GLAD\n"
339 "Dominic Szablewski - QOI - qoiformat.org\n"
340 "Sean Barrett - stb_image, stb_vorbis,\n"
341 " stb_include\n"
342 "Khronos Group - OpenGL\n"
343 , 1, k_ui_align_left, 0 );
344
345 if( button_down( k_srbind_mback ) )
346 {
347 menu.page = k_menu_page_main;
348 }
349
350 goto menu_draw;
351 }
352
353 /* TOP BAR
354 * -------------------------------------------------------------------*/
355
356 ui_font_face( &vgf_default_title );
357 ui_px height = vg_ui.font->ch + 16;
358 ui_rect topbar = { 0, 0, vg.window_x, height };
359
360 const char *opts[] = {
361 [k_menu_main_main] = "Menu",
362 [k_menu_main_map] = "Map",
363 [k_menu_main_settings ] = "Settings",
364 [k_menu_main_guide ] = "Guides"
365 };
366
367 /* TAB CONTROL */
368 u8 lb_down = 0, rb_down = 0;
369 vg_exec_input_program( k_vg_input_type_button_u8,
370 (vg_input_op[]){
371 vg_joy_button, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, vg_end
372 }, &rb_down );
373 vg_exec_input_program( k_vg_input_type_button_u8,
374 (vg_input_op[]){
375 vg_joy_button, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, vg_end
376 }, &lb_down );
377
378 if( menu.repeater > 0.0f )
379 {
380 menu.repeater -= vg_minf( vg.time_frame_delta, 0.5f );
381 }
382 else
383 {
384 if( lb_down != rb_down )
385 {
386 menu.main_index += rb_down;
387 menu.main_index -= lb_down;
388 menu.repeater += 0.2f;
389
390 if( menu.main_index == -1 )
391 menu.main_index ++;
392
393 if( menu.main_index == vg_list_size(opts) )
394 menu.main_index --;
395 }
396 }
397
398 ui_px x = 0, spacer = 8;
399 for( u32 draw=0; draw<2; draw ++ )
400 {
401 if( menu.input_mode == k_menu_input_mode_keys )
402 {
403 if( draw )
404 {
405 ui_rect inf_lb = { x, 0, 32, height };
406 ui_fill( inf_lb, lb_down? GUI_COL_NORM: GUI_COL_NORM );
407 ui_text( inf_lb, "\x91", 1, k_ui_align_middle_center, 0 );
408 }
409 x += 32 + spacer;
410 }
411
412 for( i32 i=0; i<vg_list_size(opts); i ++ )
413 {
414 ui_rect box = { x, 0, ui_text_line_width(opts[i]) + 32, height };
415
416 if( draw )
417 {
418 enum ui_button_state state = ui_button_base( box );
419 if( state == k_ui_button_click )
420 {
421 ui_fill( box, GUI_COL_DARK );
422 menu.main_index = i;
423 }
424 else if( state == k_ui_button_holding_inside )
425 {
426 ui_fill( box, GUI_COL_DARK );
427 }
428 else if( state == k_ui_button_holding_outside )
429 {
430 ui_fill( box, GUI_COL_DARK );
431 ui_outline( box, 1, GUI_COL_CLICK, 0 );
432 }
433 else if( state == k_ui_button_hover )
434 {
435 ui_fill( box, GUI_COL_NORM );
436 ui_outline( box, 1, GUI_COL_ACTIVE, 0 );
437 }
438 else
439 ui_fill( box, i==menu.main_index? GUI_COL_ACTIVE: GUI_COL_NORM );
440
441 ui_text( box, opts[i], 1, k_ui_align_middle_center, 0 );
442 }
443
444 x += box[2] + spacer;
445 }
446
447 if( menu.input_mode == k_menu_input_mode_keys )
448 {
449 if( draw )
450 {
451 ui_rect inf_rb = { x, 0, 32, height };
452 ui_fill( inf_rb, rb_down? GUI_COL_NORM: GUI_COL_NORM );
453 ui_text( inf_rb, "\x92", 1, k_ui_align_middle_center, 0 );
454 }
455 x += 32;
456 }
457
458 if( draw )
459 ui_fill( (ui_rect){ x+8,0, vg.window_x-(x+8),height }, GUI_COL_NORM );
460 else
461 {
462 x = vg.window_x/2 - x/2;
463 ui_fill( (ui_rect){ 0, 0, x-8, height }, GUI_COL_NORM );
464 }
465 }
466
467 if( menu.main_index == k_menu_main_map )
468 {
469 ui_font_face( &vgf_default_large );
470 ui_rect title = { vg.window_x/2 - 512/2, height+8, 512, 64 };
471
472 ui_px x = 8,
473 y = height+8;
474
475 struct ui_vert *vs =
476 ui_fill( (ui_rect){ x,y, 0,height },
477 world_static.active_instance? GUI_COL_DARK: GUI_COL_ACTIVE );
478
479 char buf[64];
480 vg_str str;
481 vg_strnull( &str, buf, sizeof(buf) );
482 vg_strcat( &str, "Hub World" );
483
484 if( world_static.active_instance &&
485 (vg_input.display_input_method == k_input_method_controller) )
486 {
487 vg_strcat( &str, " (" );
488 vg_input_string( &str, input_button_list[k_srbind_mhub], 1 );
489 vg_strcat( &str, ")" );
490 }
491
492 ui_px w = ui_text( (ui_rect){ x+8, y, 1000, height }, buf, 1,
493 k_ui_align_middle_left, 0 );
494 w *= vg_ui.font->sx;
495 x += w + 16;
496
497 vs[1].co[0] = x + 8;
498 vs[2].co[0] = x;
499
500 x += 2;
501
502 world_instance *world = &world_static.instances[1];
503 if( world->status == k_world_status_loaded )
504 {
505 const char *world_name =
506 mdl_pstr( &world->meta, world->info.pstr_name );
507
508 vg_strnull( &str, buf, sizeof(buf) );
509 vg_strcat( &str, world_name );
510
511 if( !world_static.active_instance &&
512 (vg_input.display_input_method == k_input_method_controller) )
513 {
514 vg_strcat( &str, " (" );
515 vg_input_string( &str, input_button_list[k_srbind_mhub], 1 );
516 vg_strcat( &str, ")" );
517 }
518
519 vs = ui_fill( (ui_rect){ x,y, 1000,height },
520 world_static.active_instance? GUI_COL_ACTIVE: GUI_COL_DARK );
521 w = ui_text( (ui_rect){ x+16,y, 1000,height }, buf,
522 1, k_ui_align_middle_left, 0 );
523
524 w = w*vg_ui.font->sx + 8*3;
525 x += w;
526
527 if( button_down( k_srbind_mhub ) ||
528 ui_button_base( (ui_rect){0,y,x,height} ) == k_ui_button_click )
529 {
530 world_switch_instance( world_static.active_instance ^ 0x1 );
531 skaterift.activity = k_skaterift_default;
532 world_map.view_ready = 0;
533 }
534
535 vs[0].co[0] += 8;
536 vs[1].co[0] = x + 8;
537 vs[2].co[0] = x;
538 }
539 }
540 else
541 {
542 if( button_down( k_srbind_mback ) )
543 {
544 skaterift.activity = k_skaterift_default;
545 return;
546 }
547
548 ui_rect list0 = { vg.window_x/2 - 512/2, height+32,
549 512, vg.window_y-height-64 }, list;
550 rect_copy( list0, list );
551 ui_rect_pad( list, (ui_px[2]){8,8} );
552
553 i32 *row = (i32 *[])
554 {
555 [k_menu_main_main] = &menu.main_row,
556 [k_menu_main_settings] = &menu.settings_row,
557 [k_menu_main_guide] = &menu.guides_row
558 }
559 [ menu.main_index ];
560
561 if( mv < 0 ) *row = (*row) +1;
562 if( mv > 0 ) *row = vg_max( 0, (*row) -1 );
563
564 /* MAIN / MAIN
565 * -------------------------------------------------------------------*/
566
567 if( menu.main_index == k_menu_main_main )
568 {
569 *row = vg_min( 2, *row );
570
571 if( menu_button( list, *row == 0, "Resume" ) )
572 {
573 skaterift.activity = k_skaterift_default;
574 return;
575 }
576
577 if( menu_button( list, *row == 1, "Credits" ) )
578 {
579 menu.page = k_menu_page_credits;
580 }
581
582 ui_rect end = { list[0], list[1]+list[3]-64, list[2], 72 };
583 if( menu_button( end, *row == 2, "Quit Game" ) )
584 {
585 vg.window_should_close = 1;
586 }
587 }
588 else if( menu.main_index == k_menu_main_settings )
589 {
590 ui_fill( list0, ui_opacity( GUI_COL_DARK, 0.36f ) );
591 ui_outline( list0, 1, GUI_COL_NORM, 0 );
592 *row = vg_min( 8, *row );
593
594 ui_font_face( &vgf_default_large );
595 list[1] -= 8;
596 menu_heading( list, "Game" );
597 menu_checkbox( list, *row == 0, "Show controls overlay",
598 &control_overlay.enabled );
599 menu_checkbox( list, *row == 1, "Auto connect to global server",
600 &network_client.auto_connect );
601
602 menu_heading( list, "Audio/Video" );
603 menu_slider( list, *row == 2, "Volume", 0, 100,
604 &vg_audio.external_global_volume, "%.f%%" );
605 menu_slider( list, *row == 3, "Resolution", 0, 100,
606 &k_render_scale, "%.f%%" );
607 menu_checkbox( list, *row == 4, "Motion Blur", &k_blur_effect );
608
609 menu_heading( list, "Camera" );
610 menu_slider( list, *row == 5, "Fov", 97, 135,
611 &k_fov, "%.1f\xb0" );
612 menu_slider( list, *row == 6, "Cam Height", -0.4f, +1.4f,
613 &k_cam_height, vg_lerpf(-0.4f,1.4f,k_cam_height)>=0.0f?
614 "+%.2fm": "%.2fm" );
615 menu_checkbox( list, *row == 7, "Invert Y Axis", &k_invert_y );
616
617
618 ui_rect end = { list[0], list[1]+list[3]-64, list[2], 72 };
619 ui_font_face( &vgf_default_small );
620 menu_heading( end, "Advanced" );
621 if( menu_button( end, *row == 8, "Open Engine Settings" ) )
622 {
623 vg_settings_open();
624 }
625 }
626 else if( menu.main_index == k_menu_main_guide )
627 {
628 ui_fill( list0, ui_opacity( GUI_COL_DARK, 0.36f ) );
629 ui_outline( list0, 1, GUI_COL_NORM, 0 );
630 *row = vg_min( 4, *row );
631
632 ui_font_face( &vgf_default_large );
633 list[1] -= 8;
634 menu_heading( list, "Controls" );
635 if( menu_button( list, *row == 0, "Skating \xb2" ) )
636 {
637 }
638 if( menu_button( list, *row == 1, "Tricks \xb2" ) )
639 {
640 }
641
642 menu_heading( list, "Workshop" );
643 if( menu_button( list, *row == 2, "Create a Board \xb2" ) )
644 {
645 }
646 if( menu_button( list, *row == 3, "Create a World \xb2" ) )
647 {
648 }
649 if( menu_button( list, *row == 4, "Create a Playermodel \xb2" ) )
650 {
651 }
652 }
653 }
654
655 menu_draw:
656
657 vg_ui.frosting = 0.015f;
658 ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y );
659 vg_ui.frosting = 0.0f;
660
661 ui_font_face( &vgf_default_small );
662 }