move mouse wakeup from SR to VG
[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( vg_input.display_input_method == k_input_method_controller )
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( vg_input.display_input_method == k_input_method_controller )
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( vg_input.display_input_method == k_input_method_controller )
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( vg_input.display_input_method == k_input_method_kbm )
271 {
272 vg_ui.wants_mouse = 1;
273 }
274
275 if( skaterift.activity != k_skaterift_menu ) return;
276
277 if( vg.settings_open )
278 {
279 if( button_down( k_srbind_mback ) )
280 {
281 vg_settings_close();
282 srinput.state = k_input_state_resume;
283 }
284
285 return;
286 }
287
288 if( menu.page == k_menu_page_credits )
289 {
290 ui_rect panel = { 0,0, 600, 400 },
291 screen = { 0,0, vg.window_x,vg.window_y };
292 ui_rect_center( screen, panel );
293 ui_fill( panel, GUI_COL_DARK );
294 ui_outline( panel, 1, GUI_COL_NORM, 0 );
295 ui_rect_pad( panel, (ui_px[]){8,8} );
296
297 ui_rect title;
298 ui_split( panel, k_ui_axis_h, 28*2, 0, title, panel );
299 ui_font_face( &vgf_default_title );
300 ui_text( title, "Skate Rift - Credits", 1, k_ui_align_middle_center, 0 );
301
302 ui_split( panel, k_ui_axis_h, 28, 0, title, panel );
303 ui_font_face( &vgf_default_large );
304 ui_text( title, "Mt.Zero Software", 1, k_ui_align_middle_center, 0 );
305
306 ui_split( panel, k_ui_axis_h, 8, 0, title, panel );
307 ui_split( panel, k_ui_axis_h, 28*2, 0, title, panel );
308 ui_font_face( &vgf_default_title );
309 ui_text( title, "Free Software", 1, k_ui_align_middle_center, 0 );
310
311 ui_split( panel, k_ui_axis_h, 8, 0, title, panel );
312 ui_font_face( &vgf_default_large );
313 ui_text( panel,
314 "Sam Lantinga - SDL2 - libsdl.org\n"
315 "Hunter WB - Anyascii\n"
316 "David Herberth - GLAD\n"
317 "Dominic Szablewski - QOI - qoiformat.org\n"
318 "Sean Barrett - stb_image, stb_vorbis,\n"
319 " stb_include\n"
320 "Khronos Group - OpenGL\n"
321 , 1, k_ui_align_left, 0 );
322
323 ui_rect end = { panel[0], panel[1] + panel[3] - 64, panel[2], 64 };
324
325 if( menu_button( end, 1, "Back" ) || button_down( k_srbind_mback ) )
326 {
327 menu.page = k_menu_page_main;
328 }
329
330 goto menu_draw;
331 }
332
333 /* TOP BAR
334 * -------------------------------------------------------------------*/
335
336 ui_font_face( &vgf_default_title );
337 ui_px height = vg_ui.font->ch + 16;
338 ui_rect topbar = { 0, 0, vg.window_x, height };
339
340 const char *opts[] = {
341 [k_menu_main_main] = "Menu",
342 [k_menu_main_map] = "Map",
343 [k_menu_main_settings ] = "Settings",
344 [k_menu_main_guide ] = "Guides"
345 };
346
347 /* TAB CONTROL */
348 u8 lb_down = 0, rb_down = 0;
349 vg_exec_input_program( k_vg_input_type_button_u8,
350 (vg_input_op[]){
351 vg_joy_button, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, vg_end
352 }, &rb_down );
353 vg_exec_input_program( k_vg_input_type_button_u8,
354 (vg_input_op[]){
355 vg_joy_button, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, vg_end
356 }, &lb_down );
357
358 if( menu.repeater > 0.0f )
359 {
360 menu.repeater -= vg_minf( vg.time_frame_delta, 0.5f );
361 }
362 else
363 {
364 if( lb_down != rb_down )
365 {
366 menu.main_index += rb_down;
367 menu.main_index -= lb_down;
368 menu.repeater += 0.2f;
369
370 if( menu.main_index == -1 )
371 menu.main_index ++;
372
373 if( menu.main_index == vg_list_size(opts) )
374 menu.main_index --;
375 }
376 }
377
378 ui_px x = 0, spacer = 8;
379 for( u32 draw=0; draw<2; draw ++ )
380 {
381 if( vg_input.display_input_method == k_input_method_controller )
382 {
383 if( draw )
384 {
385 ui_rect inf_lb = { x, 0, 32, height };
386 ui_fill( inf_lb, lb_down? GUI_COL_NORM: GUI_COL_NORM );
387 ui_text( inf_lb, "\x91", 1, k_ui_align_middle_center, 0 );
388 }
389 x += 32 + spacer;
390 }
391
392 for( i32 i=0; i<vg_list_size(opts); i ++ )
393 {
394 ui_rect box = { x, 0, ui_text_line_width(opts[i]) + 32, height };
395
396 if( draw )
397 {
398 enum ui_button_state state = ui_button_base( box );
399 if( state == k_ui_button_click )
400 {
401 ui_fill( box, GUI_COL_DARK );
402 menu.main_index = i;
403 }
404 else if( state == k_ui_button_holding_inside )
405 {
406 ui_fill( box, GUI_COL_DARK );
407 }
408 else if( state == k_ui_button_holding_outside )
409 {
410 ui_fill( box, GUI_COL_DARK );
411 ui_outline( box, 1, GUI_COL_CLICK, 0 );
412 }
413 else if( state == k_ui_button_hover )
414 {
415 ui_fill( box, GUI_COL_NORM );
416 ui_outline( box, 1, GUI_COL_ACTIVE, 0 );
417 }
418 else
419 ui_fill( box, i==menu.main_index? GUI_COL_ACTIVE: GUI_COL_NORM );
420
421 ui_text( box, opts[i], 1, k_ui_align_middle_center, 0 );
422 }
423
424 x += box[2] + spacer;
425 }
426
427 if( vg_input.display_input_method == k_input_method_controller )
428 {
429 if( draw )
430 {
431 ui_rect inf_rb = { x, 0, 32, height };
432 ui_fill( inf_rb, rb_down? GUI_COL_NORM: GUI_COL_NORM );
433 ui_text( inf_rb, "\x92", 1, k_ui_align_middle_center, 0 );
434 }
435 x += 32;
436 }
437
438 if( draw )
439 ui_fill( (ui_rect){ x+8,0, vg.window_x-(x+8),height }, GUI_COL_NORM );
440 else
441 {
442 x = vg.window_x/2 - x/2;
443 ui_fill( (ui_rect){ 0, 0, x-8, height }, GUI_COL_NORM );
444 }
445 }
446
447 if( menu.main_index == k_menu_main_map )
448 {
449 ui_font_face( &vgf_default_large );
450 ui_rect title = { vg.window_x/2 - 512/2, height+8, 512, 64 };
451
452 ui_px x = 8,
453 y = height+8;
454
455 struct ui_vert *vs =
456 ui_fill( (ui_rect){ x,y, 0,height },
457 world_static.active_instance? GUI_COL_DARK: GUI_COL_ACTIVE );
458
459 char buf[64];
460 vg_str str;
461 vg_strnull( &str, buf, sizeof(buf) );
462 vg_strcat( &str, "Hub World" );
463
464 if( world_static.active_instance &&
465 (vg_input.display_input_method == k_input_method_controller) )
466 {
467 vg_strcat( &str, " (" );
468 vg_input_string( &str, input_button_list[k_srbind_mhub], 1 );
469 vg_strcat( &str, ")" );
470 }
471
472 ui_px w = ui_text( (ui_rect){ x+8, y, 1000, height }, buf, 1,
473 k_ui_align_middle_left, 0 );
474 w *= vg_ui.font->sx;
475 x += w + 16;
476
477 vs[1].co[0] = x + 8;
478 vs[2].co[0] = x;
479
480 x += 2;
481
482 world_instance *world = &world_static.instances[1];
483 if( world->status == k_world_status_loaded )
484 {
485 const char *world_name =
486 mdl_pstr( &world->meta, world->info.pstr_name );
487
488 vg_strnull( &str, buf, sizeof(buf) );
489 vg_strcat( &str, world_name );
490
491 if( !world_static.active_instance &&
492 (vg_input.display_input_method == k_input_method_controller) )
493 {
494 vg_strcat( &str, " (" );
495 vg_input_string( &str, input_button_list[k_srbind_mhub], 1 );
496 vg_strcat( &str, ")" );
497 }
498
499 vs = ui_fill( (ui_rect){ x,y, 1000,height },
500 world_static.active_instance? GUI_COL_ACTIVE: GUI_COL_DARK );
501 w = ui_text( (ui_rect){ x+16,y, 1000,height }, buf,
502 1, k_ui_align_middle_left, 0 );
503
504 w = w*vg_ui.font->sx + 8*3;
505 x += w;
506
507 if( button_down( k_srbind_mhub ) ||
508 ui_button_base( (ui_rect){0,y,x,height} ) == k_ui_button_click )
509 {
510 world_switch_instance( world_static.active_instance ^ 0x1 );
511 skaterift.activity = k_skaterift_default;
512 world_map.view_ready = 0;
513 }
514
515 vs[0].co[0] += 8;
516 vs[1].co[0] = x + 8;
517 vs[2].co[0] = x;
518 }
519 }
520 else
521 {
522 if( button_down( k_srbind_mback ) )
523 {
524 skaterift.activity = k_skaterift_default;
525 return;
526 }
527
528 ui_rect list0 = { vg.window_x/2 - 512/2, height+32,
529 512, vg.window_y-height-64 }, list;
530 rect_copy( list0, list );
531 ui_rect_pad( list, (ui_px[2]){8,8} );
532
533 i32 *row = (i32 *[])
534 {
535 [k_menu_main_main] = &menu.main_row,
536 [k_menu_main_settings] = &menu.settings_row,
537 [k_menu_main_guide] = &menu.guides_row
538 }
539 [ menu.main_index ];
540
541 if( mv < 0 ) *row = (*row) +1;
542 if( mv > 0 ) *row = vg_max( 0, (*row) -1 );
543
544 /* MAIN / MAIN
545 * -------------------------------------------------------------------*/
546
547 if( menu.main_index == k_menu_main_main )
548 {
549 *row = vg_min( 2, *row );
550
551 if( menu_button( list, *row == 0, "Resume" ) )
552 {
553 skaterift.activity = k_skaterift_default;
554 return;
555 }
556
557 if( menu_button( list, *row == 1, "Credits" ) )
558 {
559 menu.page = k_menu_page_credits;
560 }
561
562 ui_rect end = { list[0], list[1]+list[3]-64, list[2], 72 };
563 if( menu_button( end, *row == 2, "Quit Game" ) )
564 {
565 vg.window_should_close = 1;
566 }
567 }
568 else if( menu.main_index == k_menu_main_settings )
569 {
570 ui_fill( list0, ui_opacity( GUI_COL_DARK, 0.36f ) );
571 ui_outline( list0, 1, GUI_COL_NORM, 0 );
572 *row = vg_min( 8, *row );
573
574 ui_font_face( &vgf_default_large );
575 list[1] -= 8;
576 menu_heading( list, "Game" );
577 menu_checkbox( list, *row == 0, "Show controls overlay",
578 &control_overlay.enabled );
579 menu_checkbox( list, *row == 1, "Auto connect to global server",
580 &network_client.auto_connect );
581
582 menu_heading( list, "Audio/Video" );
583 menu_slider( list, *row == 2, "Volume", 0, 100,
584 &vg_audio.external_global_volume, "%.f%%" );
585 menu_slider( list, *row == 3, "Resolution", 0, 100,
586 &k_render_scale, "%.f%%" );
587 menu_checkbox( list, *row == 4, "Motion Blur", &k_blur_effect );
588
589 menu_heading( list, "Camera" );
590 menu_slider( list, *row == 5, "Fov", 97, 135,
591 &k_fov, "%.1f\xb0" );
592 menu_slider( list, *row == 6, "Cam Height", -0.4f, +1.4f,
593 &k_cam_height, vg_lerpf(-0.4f,1.4f,k_cam_height)>=0.0f?
594 "+%.2fm": "%.2fm" );
595 menu_checkbox( list, *row == 7, "Invert Y Axis", &k_invert_y );
596
597
598 ui_rect end = { list[0], list[1]+list[3]-64, list[2], 72 };
599 ui_font_face( &vgf_default_small );
600 menu_heading( end, "Advanced" );
601 if( menu_button( end, *row == 8, "Open Engine Settings" ) )
602 {
603 vg_settings_open();
604 }
605 }
606 else if( menu.main_index == k_menu_main_guide )
607 {
608 ui_fill( list0, ui_opacity( GUI_COL_DARK, 0.36f ) );
609 ui_outline( list0, 1, GUI_COL_NORM, 0 );
610 *row = vg_min( 4, *row );
611
612 ui_font_face( &vgf_default_large );
613 list[1] -= 8;
614 menu_heading( list, "Controls" );
615 if( menu_button( list, *row == 0, "Skating \xb2" ) )
616 {
617 }
618 if( menu_button( list, *row == 1, "Tricks \xb2" ) )
619 {
620 }
621
622 menu_heading( list, "Workshop" );
623 if( menu_button( list, *row == 2, "Create a Board \xb2" ) )
624 {
625 }
626 if( menu_button( list, *row == 3, "Create a World \xb2" ) )
627 {
628 }
629 if( menu_button( list, *row == 4, "Create a Playermodel \xb2" ) )
630 {
631 }
632 }
633 }
634
635 menu_draw:
636
637 vg_ui.frosting = 0.015f;
638 ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y );
639 vg_ui.frosting = 0.0f;
640
641 ui_font_face( &vgf_default_small );
642 }