Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
menu.qc
Go to the documentation of this file.
1#include "menu.qh"
2
3#include "item.qh"
4
5#include "anim/animhost.qh"
6
7#include "item/dialog.qh"
8#include "item/listbox.qh"
9#include "item/nexposee.qh"
10
12#include "xonotic/mainwindow.qh"
13#include "xonotic/serverlist.qh"
15
17
18#include "xonotic/util.qh"
19
21#include <common/items/_mod.qh>
23#include <common/mapinfo.qh>
25
35
36// Used for having effects only execute once in main menu, not for every reload
37// 0: never been in main menu before. 1: coming back to main menu. 2: in main menu.
40
44
45void m_sync()
46{
48 vidwidth_s = vidheight_s = vidpixelheight_s = 0; // Force updateConwidths on next draw
49
51}
52
54{
55 gamestatus = 0;
58 if (cvar("developer") > 0) gamestatus |= GAME_DEVELOPER;
59}
60
61void m_init()
62{
63 bool restarting = false;
64 cvar_set("_menu_alpha", "0");
65 prvm_language = cvar_string("prvm_language");
66 if (prvm_language == "")
67 {
68 prvm_language = "en";
69 cvar_set("prvm_language", prvm_language);
70 localcmd("\nmenu_restart\n");
71 restarting = true;
72 }
74 cvar_set("_menu_prvm_language", prvm_language);
75
76#ifdef WATERMARK
77 LOG_TRACEF("^4MQC Build information: ^1%s", WATERMARK);
78#endif
79
81
82 registercvar("_menu_cmd_closemenu_available", "0", 0);
83 cvar_set("_menu_cmd_closemenu_available", "1");
84
85 // needs to be done so early because of the constants they create
89
91
92 float ddsload = cvar("r_texture_dds_load");
93 float ddsloadsw = cvar("r_texture_dds_swdecode");
94 float texcomp = cvar("gl_texturecompression");
96 if (ddsload != cvar("r_texture_dds_load") || ddsloadsw != cvar("r_texture_dds_swdecode") || texcomp != cvar("gl_texturecompression"))
97 localcmd("\nr_restart\n");
98
99 if (!restarting)
100 {
101 if (cvar("_menu_initialized"))
102 {
103 // always show menu after menu_restart
104 m_display();
105 // exec font cfg again in case font depends on menu cvars like prvm_language
106 localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg")));
107 }
108 else m_hide();
109 cvar_set("_menu_initialized", "1");
110 }
111}
112
113const float MENU_ASPECT = 1280 / 1024;
114
123
124void UpdateConWidthHeight(float w, float h, float p)
125{
126 if (w != vidwidth_s || h != vidheight_s || p != vidpixelheight_s)
127 {
129 localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg")));
130 vidwidth_s = w;
131 vidheight_s = h;
133 }
136 realconwidth = cvar("vid_conwidth");
137 realconheight = cvar("vid_conheight");
139 {
140 // widescreen
143 }
144 else
145 {
146 // squarescreen
149 }
150 if (main)
151 {
153 {
155 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
156 }
157 }
158 else
159 {
160 vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame
161 }
162}
163
166{
168
169 menuInitialized = false;
170 if (!preMenuInit()) return;
171 menuInitialized = true;
172
173 int fh = -1;
174 if (cvar_string("menu_skin") != "")
175 {
176 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
177 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
178 }
179 if (fh < 0 && cvar_defstring("menu_skin") != "")
180 {
181 cvar_set("menu_skin", cvar_defstring("menu_skin"));
182 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
183 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
184 }
185 if (fh < 0)
186 {
187 draw_currentSkin = "gfx/menu/wickedx"; // default menu skin
188 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
189 }
190 if (fh < 0) error("cannot load any menu skin\n");
191 draw_currentSkin = strzone(draw_currentSkin);
192 for (string s; (s = fgets(fh)); )
193 {
194 // these two are handled by skinlist.qc
195 if (substring(s, 0, 6) == "title ") continue;
196 if (substring(s, 0, 7) == "author ") continue;
197 int n = tokenize_console(s);
198 if (n < 2) continue;
199 Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
200 }
201 fclose(fh);
202
203 int glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true);
204 if (glob >= 0)
205 {
206 for (int i = 0, n = search_getsize(glob); i < n; ++i)
208 search_end(glob);
209 }
210
211 draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
212
213 anim = NEW(AnimHost);
215 main.configureMainWindow(main);
216
217 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
218 main.focused = true;
219 menuShiftState = 0;
220 menuMousePos = '0.5 0.5 0';
221
222 m_sync();
223
224 if (m_goto_buffer)
225 {
226 m_goto(m_goto_buffer, false);
228 }
229
230 if (Menu_Active) m_display(); // delayed menu display
231
232 cvar_set("_menu_initialized", "2");
233}
234
235void m_keyup(float key, float ascii)
236{
237 if (!menuInitialized) return;
238 if (!Menu_Active) return;
240 main.keyUp(main, key, ascii, menuShiftState);
241 if (key >= K_MOUSE1 && key <= K_MOUSE3)
242 {
244 if (!mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
245 if (mouseButtonsPressed < 0)
246 {
248 LOG_TRACE("Warning: released an already released button");
249 }
250 }
251 if (key == K_ALT) menuShiftState &= ~S_ALT;
252 if (key == K_CTRL) menuShiftState &= ~S_CTRL;
253 if (key == K_SHIFT) menuShiftState &= ~S_SHIFT;
254}
255
256void m_keydown(float key, float ascii)
257{
258 if (!menuInitialized) return;
259 if (!Menu_Active) return;
260
261 if (menuMouseMode && key >= K_MOUSE1 && key <= K_MOUSE3)
262 {
263 // detect a click outside of the game window
264 vector p = getmousepos();
265 if (p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight)
266 {
268 return;
269 }
270 }
271
272 if (keyGrabber)
273 {
274 entity e = keyGrabber;
276 e.keyGrabbed(e, key, ascii);
277 }
278 else
279 {
281 if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3)
282 main.mousePress(main, menuMousePos);
283 if (!main.keyDown(main, key, ascii, menuShiftState))
284 {
285 // disable menu on unhandled ESC
286 if (key == K_ESCAPE)
287 if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
288 m_hide();
289 }
290 }
291 if (key >= K_MOUSE1 && key <= K_MOUSE3)
292 {
294 if (mouseButtonsPressed > 10)
295 {
297 LOG_TRACE("Warning: pressed an already pressed button");
298 }
299 }
300 if (key == K_ALT) menuShiftState |= S_ALT;
301 if (key == K_CTRL) menuShiftState |= S_CTRL;
302 if (key == K_SHIFT) menuShiftState |= S_SHIFT;
303}
304
305enum {
311};
312void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
313{
314 vector sz = draw_PictureSize(img);
315 bool width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x);
316 vector isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y));
317 vector isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x));
318 vector isz;
319 switch (scalemode)
320 {
321 default:
322 case SCALEMODE_CROP:
323 isz = (width_is_larger ? isz_h : isz_w);
324 break;
326 isz = (width_is_larger ? isz_w : isz_h);
327 break;
328 case SCALEMODE_WIDTH:
329 isz = isz_w;
330 break;
331 case SCALEMODE_HEIGHT:
332 isz = isz_h;
333 break;
335 isz = '1 1 0';
336 break;
337 }
338 vector org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y));
339 draw_Picture(org, img, isz, '1 1 1', a);
340}
341
342void drawBackground(string img, float a, string algn, float force1)
343{
344 if (main.mainNexposee.ModalController_state == 0
345 && !(main.focusedChild && main.focusedChild.hideMenuOnClose && !(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
346 return;
347
348 vector v = '0 0 0';
349 int scalemode = SCALEMODE_CROP;
350 int len = strlen(algn);
351 for (int i = 0, l = 0; i < len; ++i)
352 {
353 string c = substring(algn, i, 1);
354 switch (c)
355 {
356 case "c":
357 scalemode = SCALEMODE_CROP;
358 goto nopic;
359 case "l":
360 scalemode = SCALEMODE_LETTERBOX;
361 goto nopic;
362 case "h":
363 scalemode = SCALEMODE_HEIGHT;
364 goto nopic;
365 case "w":
366 scalemode = SCALEMODE_WIDTH;
367 goto nopic;
368 case "s":
369 scalemode = SCALEMODE_STRETCH;
370 goto nopic;
371 case "1": case "4": case "7":
372 v.x = 0.0;
373 break;
374 case "2": case "5": case "8":
375 v.x = 0.5;
376 break;
377 case "3": case "6": case "9":
378 v.x = 1.0;
379 break;
380 default:
381 v.x = random();
382 break;
383 }
384 switch (c)
385 {
386 case "7": case "8": case "9":
387 v.y = 0.0;
388 break;
389 case "4": case "5": case "6":
390 v.y = 0.5;
391 break;
392 case "1": case "2": case "3":
393 v.y = 1.0;
394 break;
395 default:
396 v.y = random();
397 break;
398 }
399 if (l == 0)
400 {
401 draw_Picture_Aligned(v, scalemode, img, a);
402 }
403 else if (force1)
404 {
405 // force all secondary layers to use alpha 1. Prevents ugly issues
406 // with overlap. It's a flag because it cannot be used for the
407 // ingame background
408 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), 1);
409 }
410 else
411 {
412 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), a);
413 }
414 ++l;
415LABEL(nopic)
416 }
417}
418
427int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
429{
430 return !(
431 (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x)
432 && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y)
433 );
434}
435bool m_testtooltipbox(vector tooltippos)
436{
437 if (tooltippos.x < 0) return false;
438 if (tooltippos.y < 0) return false;
439 if (tooltippos.x + menuTooltipSize.x > 1) return false;
440 if (tooltippos.y + menuTooltipSize.y > 1) return false;
441 menuTooltipOrigin = tooltippos;
442 return true;
443}
445{
446 vector avoidplus;
447 avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
448 avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
449 avoidplus.z = 0;
450
451 vector avoidminus;
452 avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x;
453 avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y;
454 avoidminus.z = 0;
455
456 // bottom right
457 vector v = pos + avoidplus;
458 if (m_testtooltipbox(v)) return true;
459
460 // bottom center
461 v.x = pos.x - menuTooltipSize.x * 0.5;
462 if (m_testtooltipbox(v)) return true;
463
464 // bottom left
465 v.x = pos.x - avoidminus.x;
466 if (m_testtooltipbox(v)) return true;
467
468 // top left
469 v.y = pos.y - avoidminus.y;
470 if (m_testtooltipbox(v)) return true;
471
472 // top center
473 v.x = pos.x - menuTooltipSize.x * 0.5;
474 if (m_testtooltipbox(v)) return true;
475
476 // top right
477 v.x = pos.x + avoidplus.x;
478 if (m_testtooltipbox(v)) return true;
479
480 return false;
481}
483{
484 entity best = NULL;
485 for (entity it = root; it.instanceOfContainer; )
486 {
487 while (it.instanceOfNexposee && it.focusedChild)
488 {
489 it = it.focusedChild;
490 pos = globalToBox(pos, it.Container_origin, it.Container_size);
491 }
492 if (it.instanceOfNexposee)
493 {
494 it = it.itemFromPoint(it, pos);
495 if (it.tooltip) best = it;
496 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
497 it = NULL;
498 }
499 else if (it.instanceOfModalController)
500 {
501 it = it.focusedChild;
502 }
503 else
504 {
505 it = it.itemFromPoint(it, pos);
506 }
507 if (!it) break;
508 if (it.tooltip) best = it;
509 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
510 pos = globalToBox(pos, it.Container_origin, it.Container_size);
511 }
512
513 return best;
514}
515string gettooltip_dependency_string_numeric(entity e, .string cvarStr, .float cvarMin, .float cvarMax, float not_if_equal)
516{
517 if (e.(cvarMin) == e.(cvarMax))
518 {
519 if (not_if_equal)
520 return sprintf(_("^3%s^7 not equal to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMin)));
521 else
522 return sprintf("^3%s^7 \"%s\"", e.(cvarStr), ftos_mindecimals(e.(cvarMin)));
523 }
524 else if (e.(cvarMin) < e.(cvarMax))
525 return sprintf(_("^3%s^7 in range \"%s\" to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMin)), ftos_mindecimals(e.(cvarMax)));
526 else
527 return sprintf(_("^3%s^7 outside range \"%s\" to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMax)), ftos_mindecimals(e.(cvarMin)));
528}
530{
531 string s;
532 if (menu_tooltips == 2)
533 {
534 if (menuTooltipItem.controlledCvar)
535 {
536 string cvar_list = getCvarsMulti(menuTooltipItem);
537 if (cvar_list)
538 cvar_list = strcat(menuTooltipItem.controlledCvar, " ", cvar_list);
539 else
540 cvar_list = menuTooltipItem.controlledCvar;
541 s = strcat("^3", cvar_list,
542 " ^7\"", cvar_string(menuTooltipItem.controlledCvar), "^7\" "
543 "^8[\"", cvar_defstring(menuTooltipItem.controlledCvar), "^8\"]^7");
544 }
545 else if (menuTooltipItem.onClickCommand)
546 s = strcat("<", menuTooltipItem.onClickCommand, "^7>");
547 else
548 s = "";
549
550 if (menuTooltipItem.tooltip)
551 s = strcat(rgb_to_hexcolor(SKINCOLOR_TOOLTIP), (s == "" ? menuTooltipItem.tooltip : strcat(menuTooltipItem.tooltip, "\n\n", s)));
552
553 if (menuTooltipItem.disabled)
554 {
555 if (menuTooltipItem.func_setDependent)
556 s = strcat(s, "\n\n", _("Has special requirements"));
557 else if (menuTooltipItem.cvarString_setDependent)
558 s = strcat(s, "\n\n",
559 sprintf(_("Requires ^3%s^7 not equal to \"%s^7\""), menuTooltipItem.cvarString_setDependent, menuTooltipItem.cvarValue_setDependent));
560 else if (menuTooltipItem.cvar1_setDependent)
561 {
562 s = strcat(s, "\n\n");
563 string cvar1_s, cvar2_s, cvar3_s;
565 if (menuTooltipItem.cvar2_setDependent)
566 {
568 if (menuTooltipItem.cvar3_setDependent)
569 {
571 s = strcat(s, sprintf((menuTooltipItem.op_setDependent & BIT(0) ? _("Requires %s, %s, and %s") : _("Requires %s, %s, or %s")), cvar1_s, cvar2_s, cvar3_s));
572 }
573 else
574 s = strcat(s, sprintf((menuTooltipItem.op_setDependent & BIT(0) ? _("Requires %s or %s") : _("Requires %s and %s")), cvar1_s, cvar2_s));
575 }
576 else
577 s = strcat(s, sprintf(_("Requires %s"), cvar1_s));
578 }
579 // if this point is reached, it's disabled for some other unknown reason
580 }
581 }
582 else
583 s = menuTooltipItem.tooltip;
584 return s;
585}
587{
588 const float MAX_TOOLTIP_LINES = 16;
589 static string prev_tooltip;
590 entity it;
591 menu_tooltips = cvar("menu_tooltips");
592 if (!menu_tooltips)
593 {
594 // don't return immediately, fade out the active tooltip first
595 if (menuTooltipItem == NULL) return;
596 it = NULL;
598 }
599 else
600 {
601 float f = bound(0, frametime * 2, 1);
603 if (vdist(pos - menuTooltipAveragedMousePos, <, 0.01))
604 {
605 it = m_findtooltipitem(main, pos);
606
607 if (it.instanceOfListBox && it.isScrolling(it)) it = NULL;
608
609 if (it && prev_tooltip != it.tooltip)
610 {
611 // fade out if tooltip of a certain item has changed
613 strcpy(prev_tooltip, it.tooltip);
614 }
615 else if (menuTooltipItem && !m_testmousetooltipbox(pos))
616 {
617 menuTooltipState = 3; // fade out if mouse touches it
618 }
619 }
620 else
621 {
622 it = NULL;
623 }
624 }
625 vector fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
626
627 // float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
628 if (it != menuTooltipItem)
629 {
630 switch (menuTooltipState)
631 {
632 case 0:
633 if (menuTooltipItem)
634 {
635 // another item: fade out first
637 }
638 else
639 {
640 // new item: fade in
642 menuTooltipItem = it;
643
644 menuTooltipOrigin.x = -1; // unallocated
645
647
648 float w = 0;
649 float lines = 0;
651 for (int k = 0; k < n && lines <= MAX_TOOLTIP_LINES; ++k)
652 {
653 if (argv(k) == "")
654 {
655 lines += 0.5; // insert a half-height separator between paragraphs (marked by 2 newlines)
656 continue;
657 }
658 for (getWrappedLine_remaining = argv(k); getWrappedLine_remaining && lines <= MAX_TOOLTIP_LINES; ++lines)
659 {
660 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithColors);
661 if (lines + 1 > MAX_TOOLTIP_LINES)
662 s = "...";
663 float f = draw_TextWidth_WithColors(s, fontsize);
664 if (f > w) w = f;
665 }
666 }
667 menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
668 menuTooltipSize.y = lines * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
669 menuTooltipSize.z = 0;
670 }
671 break;
672 case 1:
673 // changing item while fading in: fade out first
675 break;
676 case 2:
677 // changing item while fading out: can't
678 break;
679 }
680 }
681 else if (menuTooltipState == 2) // re-fade in?
682 {
684 }
685
686 switch (menuTooltipState)
687 {
688 case 1: // fade in
691 break;
692 case 2: // fade out
693 case 3: // forced fade out
695 if (menuTooltipAlpha == 0)
696 {
699 }
700 break;
701 }
702
703 if (menuTooltipItem == NULL)
704 {
706 return;
707 }
708 else
709 {
711 {
712 if (menu_tooltips != 0 && menu_tooltips_old != 0) menuTooltipItem = NULL; // reload tooltip next frame
714 }
715 else if (menuTooltipOrigin.x < 0) // unallocated?
716 {
718 }
719 if (menuTooltipOrigin.x >= 0)
720 {
721 // draw the tooltip!
722 vector p = SKINBORDER_TOOLTIP;
723 p.x *= 1 / conwidth;
724 p.y *= 1 / conheight;
727 p.x += SKINMARGIN_TOOLTIP_x / conwidth;
728 p.y += SKINMARGIN_TOOLTIP_y / conheight;
729 float lines = 0;
731 for (int k = 0; k < n && lines <= MAX_TOOLTIP_LINES; ++k)
732 {
733 if (argv(k) == "")
734 {
735 p.y += fontsize.y / 2;
736 continue;
737 }
738 for (getWrappedLine_remaining = argv(k); getWrappedLine_remaining && lines <= MAX_TOOLTIP_LINES; ++lines)
739 {
740 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithColors);
741 if (lines + 1 > MAX_TOOLTIP_LINES)
742 s = "...";
743 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, true);
744 p.y += fontsize.y;
745 }
746 }
747 }
748 }
749}
750
753void m_draw(float width, float height)
754{
755 static float connected_time;
757 {
758 // avoid a bug where the main menu re-opens when changing maps
759 // potentially exclusive to `map <mapname>` cmd?
760 if (connected_time && time - connected_time > MIN_DISCONNECTION_TIME)
761 {
763 {
764 // in the case player uses the disconnect command (in the console or with a key)
765 // reset g_campaign and update menu items to reflect cvar values that may have been restored after quiting the campaign
766 // see also LEAVEMATCH_CMD
767 cvar_set("g_campaign", "0");
768 m_sync();
769 }
770
771 // reload the menu so that disconnecting players don't
772 // have to press ESC to open it again
773 m_toggle(true);
774
775 localcmd("\nmenu_cmd directmenu Welcome RESET\n");
776 connected_time = 0;
777
778 // reset main menu
779 // FIXME?: find out if anything should be done to reset it more,
780 // this is just a fix to make main menu music replay nicely
782 }
783 }
784 else
785 connected_time = time;
786
787 m_gamestatus();
788
790
791 menuMouseMode = cvar("menu_mouse_absolute");
792
793 if (anim) anim.tickAll(anim);
794
795 UpdateConWidthHeight(width, height, cvar("vid_pixelheight"));
796
797 if (!menuInitialized)
798 {
799 // TODO draw an info image about this situation
801 return;
802 }
803
804 if (menuNotTheFirstFrame == 0) // only fade the menu in once ever
805 menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME
806
807 if (menuNotTheFirstFrame <= 1) // only once per menu reload
808 {
810 {
811 localcmd("cd loop $menu_cdtrack\n");
812
813 // TODO: enable this when we have a welcome sound
814 // FIXME: change the file used according to the selected announcer
815 // Only play the welcome announcement once, not on any menu reloads
816 //if (menuNotTheFirstFrame == 0)
817 //localcmd("play sound/announcer/default/welcome.wav\n");
818 }
819
821 }
822
823 float t = gettime();
824 float realFrametime = frametime = min(0.2, t - menuPrevTime);
825 menuPrevTime = t;
826 time += frametime;
827
828 t = cvar("menu_slowmo");
829 if (t)
830 {
831 frametime *= t;
832 realFrametime *= t;
833 }
834 else
835 {
836 t = 1;
837 }
838
839 if (Menu_Active)
840 {
844 else m_hide();
845 }
846
847 if (cvar("cl_capturevideo")) frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
848
850 if (Menu_Active)
851 {
852 if (menuAlpha == 0 && menuLogoAlpha < 2)
853 {
855 }
856 else
857 {
858 menuAlpha = min(1, menuAlpha + 5 * frametime);
859 menuLogoAlpha = 2;
860 }
861 }
862 else
863 {
864 menuAlpha = max(0, menuAlpha - 5 * frametime);
865 menuLogoAlpha = 2;
866 }
867
869
871 {
872 if (menuLogoAlpha > 0)
873 {
875 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
876 drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true);
879 {
880 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
882 draw_alpha = 1;
883 }
884 }
885 }
886 else if (SKINALPHA_BACKGROUND_INGAME)
887 {
888 if (menuAlpha > 0)
889 {
891 drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME,
892 SKINALIGN_BACKGROUND_INGAME, false);
894 }
895 }
896
897 if (menuAlpha != prevMenuAlpha) cvar_set("_menu_alpha", ftos(menuAlpha));
898
900 preMenuDraw();
902
903 if (menuAlpha <= 0)
904 {
905 if (prevMenuAlpha > 0) main.initializeDialog(main, main.firstChild);
907 postMenuDraw();
908 return;
909 }
910
912
913 if (menuMouseMode)
914 {
915 vector rawMousePos = getmousepos();
916 vector newMouse = globalToBox(rawMousePos, draw_shift, draw_scale);
917 if (rawMousePos != '0 0 0' && newMouse != menuMousePos)
918 {
919 menuMousePos = newMouse;
920 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
921 else main.mouseMove(main, menuMousePos);
922 }
923 }
924 else if (frametime > 0)
925 {
926 vector dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
927 if (dMouse != '0 0 0')
928 {
929 vector minpos = globalToBox('0 0 0', draw_shift, draw_scale);
931 dMouse = globalToBoxSize(dMouse, draw_scale);
932 menuMousePos += dMouse * cvar("menu_mouse_speed");
933 menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x);
934 menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y);
935 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
936 else main.mouseMove(main, menuMousePos);
937 }
938 }
939 main.draw(main);
940
942
943 draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
944
946
948 postMenuDraw();
949
950 frametime = 0;
951 IL_ENDFRAME();
952}
953
955{
956 Menu_Active = true;
959
960 if (!menuInitialized) return;
961
962 if (mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
964
965 main.focusEnter(main);
966 main.showNotify(main);
967}
968
969void m_hide()
970{
971 Menu_Active = false;
974
975 if (!menuInitialized) return;
976
977 main.focusLeave(main);
978 main.hideNotify(main);
979}
980
981void m_toggle(int mode)
982{
983 if (Menu_Active)
984 {
985 if (mode == 1) return;
986 // when togglemenu is called without arguments (mode is -1)
987 // the menu is closed only when connected
988 if (mode == -1 && !(gamestatus & GAME_CONNECTED)) return;
989 // togglemenu 0 always closes the menu
990 m_hide();
991 }
992 else
993 {
994 if (mode == 0) return;
995 m_display();
996 }
997}
998
1000{
1001 m_hide();
1002 FOREACH_ENTITY_ORDERED(it.destroy, {
1003 if (it.classname == "vtbl") continue;
1004 it.destroy(it);
1005 });
1006 cvar_set("_menu_cmd_closemenu_available", "0");
1007}
1008
1009void m_focus_item_chain(entity outermost, entity innermost)
1010{
1011 if (innermost.parent != outermost) m_focus_item_chain(outermost, innermost.parent);
1012 innermost.parent.setFocus(innermost.parent, innermost);
1013}
1014
1016{
1017 entity par = wnd.parent;
1018 if (par) m_activate_window(par);
1019
1020 if (par.instanceOfModalController)
1021 {
1022 if (wnd.tabSelectingButton)
1023 // tabs
1024 TabButton_Click(wnd.tabSelectingButton, wnd);
1025 else
1026 // root
1027 par.initializeDialog(par, wnd);
1028 }
1029 else if (par.instanceOfNexposee)
1030 {
1031 // nexposee (sorry for violating abstraction here)
1032 par.selectedChild = wnd;
1033 par.animationState = 1;
1035 }
1036 else if (par.instanceOfContainer)
1037 {
1038 // other containers
1039 if (par.focused) par.setFocus(par, wnd);
1040 }
1041}
1042
1044{
1045 if (!wnd.instanceOfContainer) return;
1046 entity focus = wnd.preferredFocusedGrandChild(wnd);
1047 if (!focus) return;
1048 menuMousePos = focus.origin + 0.5 * focus.size;
1049 menuMousePos.x *= 1 / conwidth;
1050 menuMousePos.y *= 1 / conheight;
1051 entity par = wnd.parent;
1052 if (par.focused) par.setFocus(par, wnd);
1053 if (wnd.focused) m_focus_item_chain(wnd, focus);
1054}
1055
1056void m_goto(string itemname, bool hide_menu_on_close)
1057{
1058 if (!menuInitialized)
1059 {
1060 strcpy(m_goto_buffer, itemname);
1061 return;
1062 }
1063 if (itemname == "") // this can be called by GameCommand
1064 {
1066 {
1067 m_hide();
1068 return;
1069 }
1070 itemname = "nexposee";
1071 }
1072
1073 if (itemname == "nexposee")
1074 {
1075 // unlike 'togglemenu 1', this closes modal and root dialogs if opened
1076 m_activate_window(main.mainNexposee);
1077 m_display();
1078 }
1079 else
1080 {
1081 entity e;
1082 for (e = NULL; (e = find(e, name, itemname)); )
1083 if (e.classname != "vtbl") break;
1084
1085 if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
1086 {
1087 m_hide();
1090 m_display();
1091 if (hide_menu_on_close)
1092 {
1093 while(e.parent && !(e.instanceOfDialog && e.isTabRoot))
1094 e = e.parent;
1095 if (e.instanceOfDialog)
1096 e.hideMenuOnClose = true;
1097 }
1098 }
1099 }
1100}
1101
1103{
1104 static float menuLastFocusSoundTime;
1105 if (cvar("menu_sounds") < 2) return;
1106 if (time - menuLastFocusSoundTime <= 0.25) return;
1108 menuLastFocusSoundTime = time;
1109}
1110
1111void m_play_click_sound(string soundfile)
1112{
1113 if (!cvar("menu_sounds")) return;
1114 localsound(soundfile);
1115}
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
float height
Definition bobbing.qc:3
void CheckEngineExtensions(void)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
ERASEABLE string rgb_to_hexcolor(vector rgb)
Definition color.qh:183
void execute_next_frame()
Definition util.qc:1854
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1137
string getWrappedLine_remaining
Definition util.qh:147
#define LABEL(id)
Definition compiler.qh:34
void Container_setFocus(entity me, entity other)
Definition container.qc:299
float frametime
const float FILE_READ
float time
#define argv_end_index
#define strlen
#define tokenize_console
#define tokenizebyseparator
#define argv_start_index
const int S_SHIFT
Definition hud.qh:129
const int S_ALT
Definition hud.qh:131
const int S_CTRL
Definition hud.qh:130
string prvm_language
Definition i18n.qh:8
best
Definition all.qh:82
ERASEABLE void IL_ENDFRAME()
#define FOREACH_ENTITY_ORDERED(cond, body)
Definition iter.qh:138
float K_SHIFT
Definition keycodes.qc:22
float K_MOUSE1
Definition keycodes.qc:129
float K_CTRL
Definition keycodes.qc:21
float K_MOUSE3
Definition keycodes.qc:131
float K_ALT
Definition keycodes.qc:20
float K_ESCAPE
Definition keycodes.qc:9
#define m_keydown
Definition _all.inc:193
#define main
Definition _all.inc:202
#define m_draw
Definition _all.inc:189
#define m_toggle
Definition _all.inc:197
void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize)
Definition draw.qh:44
#define LOG_TRACEF(...)
Definition log.qh:77
#define LOG_TRACE(...)
Definition log.qh:76
vector globalToBox(vector v, vector theOrigin, vector theScale)
Definition draw.qc:30
void draw_reset(float cw, float ch, float ox, float oy)
Definition draw.qc:21
void draw_Picture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:72
void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
Definition draw.qc:282
void draw_setMousePointer(string pic, vector theSize, vector theOffset)
Definition draw.qc:9
vector draw_PictureSize(string pic)
Definition draw.qc:80
vector globalToBoxSize(vector v, vector theScale)
Definition draw.qc:38
void draw_drawMousePointer(vector where)
Definition draw.qc:16
float draw_TextWidth_WithColors(string s, vector theFontSize)
Definition draw.qc:391
void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:97
vector draw_shift
Definition draw.qh:7
vector draw_scale
Definition draw.qh:8
float draw_alpha
Definition draw.qh:9
void loadAllCvars(entity root)
Definition util.qc:53
string getCvarsMulti(entity me)
Definition util.qc:60
void updateCompression()
Definition util.qc:604
float cvar1Max_setDependent
Definition util.qh:5
float cvar3Max_setDependent
Definition util.qh:11
string cvar1_setDependent
Definition util.qh:3
float cvar2Min_setDependent
Definition util.qh:7
string cvar3_setDependent
Definition util.qh:9
float cvar1Min_setDependent
Definition util.qh:4
float cvar2Max_setDependent
Definition util.qh:8
float cvar3Min_setDependent
Definition util.qh:10
string cvar2_setDependent
Definition util.qh:6
bool m_testmousetooltipbox(vector pos)
Definition menu.qc:428
vector menuTooltipAveragedMousePos
Definition menu.qc:421
int menu_tooltips
Definition menu.qc:419
void m_play_click_sound(string soundfile)
Definition menu.qc:1111
float prevMenuAlpha
Definition menu.qc:32
void m_hide()
Definition menu.qc:969
int menuShiftState
Definition menu.qc:28
float menuLogoAlpha
Definition menu.qc:31
float conwidth_s
Definition menu.qc:41
float conheight_s
Definition menu.qc:41
void draw_reset_full()
Definition menu.qc:119
bool m_testtooltipbox(vector tooltippos)
Definition menu.qc:435
bool m_allocatetooltipbox(vector pos)
Definition menu.qc:444
vector menuTooltipOrigin
Definition menu.qc:423
void m_init()
Definition menu.qc:61
void m_display()
Definition menu.qc:954
void m_play_focus_sound()
Definition menu.qc:1102
float realconwidth
Definition menu.qc:43
void m_activate_window(entity wnd)
Definition menu.qc:1015
float realconheight
Definition menu.qc:43
void m_init_delayed()
Definition menu.qc:165
void m_tooltip(vector pos)
Definition menu.qc:586
string menuTooltipText
Definition menu.qc:426
int menu_tooltips_old
Definition menu.qc:420
const int MIN_DISCONNECTION_TIME
Definition menu.qc:751
int menuNotTheFirstFrame
Definition menu.qc:38
vector menuTooltipSize
Definition menu.qc:424
string gettooltip()
Definition menu.qc:529
float vidheight_s
Definition menu.qc:42
vector menuMousePos
Definition menu.qc:27
bool menuInitialized
Definition menu.qc:33
void m_focus_item_chain(entity outermost, entity innermost)
Definition menu.qc:1009
int menuMouseMode
Definition menu.qc:34
string controlledCvar
Definition menu.qc:16
float vidwidth_s
Definition menu.qc:42
entity m_findtooltipitem(entity root, vector pos)
Definition menu.qc:482
float menuPrevTime
Definition menu.qc:29
void m_keyup(float key, float ascii)
Definition menu.qc:235
entity menuTooltipItem
Definition menu.qc:422
int menuTooltipState
Definition menu.qc:427
void m_setpointerfocus(entity wnd)
Definition menu.qc:1043
void m_sync()
Definition menu.qc:45
float menuTooltipAlpha
Definition menu.qc:425
bool autocvar_g_campaign
Definition menu.qc:752
void draw_reset_cropped()
Definition menu.qc:115
void UpdateConWidthHeight(float w, float h, float p)
Definition menu.qc:124
void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
Definition menu.qc:312
bool autocvar_menu_no_music_nor_welcome
Definition menu.qc:39
void drawBackground(string img, float a, string algn, float force1)
Definition menu.qc:342
void Shutdown()
Definition menu.qc:999
string gettooltip_dependency_string_numeric(entity e,.string cvarStr,.float cvarMin,.float cvarMax, float not_if_equal)
Definition menu.qc:515
float vidpixelheight_s
Definition menu.qc:42
void m_goto(string itemname, bool hide_menu_on_close)
Definition menu.qc:1056
const float MENU_ASPECT
Definition menu.qc:113
int mouseButtonsPressed
Definition menu.qc:26
@ SCALEMODE_CROP
Definition menu.qc:306
@ SCALEMODE_HEIGHT
Definition menu.qc:309
@ SCALEMODE_LETTERBOX
Definition menu.qc:307
@ SCALEMODE_WIDTH
Definition menu.qc:308
@ SCALEMODE_STRETCH
Definition menu.qc:310
string m_goto_buffer
Definition menu.qc:164
void m_gamestatus()
Definition menu.qc:53
float menuAlpha
Definition menu.qc:30
const string MENU_SOUND_FOCUS
Definition menu.qh:52
const int GAME_DEVELOPER
Definition menu.qh:13
void preMenuDraw()
this is run before the menu is drawn.
Definition util.qc:541
float preMenuInit()
you have to define this for pre-menu initialization.
Definition util.qc:504
entity anim
Definition menu.qh:25
float conwidth
Definition menu.qh:36
entity keyGrabber
Definition menu.qh:32
bool Menu_Active
Definition menu.qh:15
void postMenuDraw()
this is run just after the menu is drawn (or not).
Definition util.qc:538
const int GAME_ISSERVER
Definition menu.qh:11
string name
Definition menu.qh:30
const int GAME_CONNECTED
Definition menu.qh:12
int gamestatus
Definition menu.qh:16
float conheight
Definition menu.qh:36
float updateConwidths(float width, float height, float pixelheight)
float registercvar(string name, string value, float flags)
void localcmd(string command,...)
void cvar_set(string name, string value)
float MT_CLIENT
Definition menudefs.qc:64
string fgets(float fhandle)
float isdemo()
void fclose(float fhandle)
float CS_CONNECTED
Definition menudefs.qc:71
float bound(float min, float value, float max)
float KEY_MENU_GRABBED
Definition menudefs.qc:31
string substring(string s, float start, float length)
void setmousetarget(float trg)
float cvar(string name)
float fopen(string filename, float mode)
entity find(entity start,.string field, string match)
string search_getfilename(float handle, float num)
float gettime(void)
float clientstate(void)
float random(void)
string precache_pic(string name,...)
const string cvar_string(string name)
float search_getsize(float handle)
float KEY_GAME
Definition menudefs.qc:29
float KEY_MENU
Definition menudefs.qc:30
float search_begin(string pattern, float caseinsensitive, float quiet)
float MT_MENU
Definition menudefs.qc:63
void localsound(string sample)
float isserver(void)
float getkeydest(void)
float min(float f,...)
void setkeydest(float dest)
string ftos(float f)
float getmousetarget(void)
const string cvar_defstring(string name)
vector getmousepos(void)
string strzone(string s)
string argv(float n)
void search_end(float handle)
float max(float f,...)
float CS_DISCONNECTED
Definition menudefs.qc:70
void TabButton_Click(entity button, entity tab)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NEW(cname,...)
Definition oo.qh:117
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
vector
Definition self.qh:92
vector org
Definition self.qh:92
void RegisterSLCategories()
Definition serverlist.qc:44
#define static_init_late()
Definition static.qh:38
#define static_init_precache()
Definition static.qh:43
#define static_init()
Definition static.qh:33
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
ERASEABLE string ftos_mindecimals(float number)
Converts a number to a string with the minimum number of decimals It assumes that an extreme accuracy...
Definition string.qh:497
const vector eY
Definition vector.qh:45
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
const vector eX
Definition vector.qh:44