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")) // always show menu after menu_restart
102 m_display();
103 else m_hide();
104 cvar_set("_menu_initialized", "1");
105 }
106}
107
108const float MENU_ASPECT = 1280 / 1024;
109
118
119void UpdateConWidthHeight(float w, float h, float p)
120{
121 if (w != vidwidth_s || h != vidheight_s || p != vidpixelheight_s)
122 {
124 localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg")));
125 vidwidth_s = w;
126 vidheight_s = h;
128 }
131 realconwidth = cvar("vid_conwidth");
132 realconheight = cvar("vid_conheight");
134 {
135 // widescreen
138 }
139 else
140 {
141 // squarescreen
144 }
145 if (main)
146 {
148 {
150 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
151 }
152 }
153 else
154 {
155 vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame
156 }
157}
158
161{
163
164 menuInitialized = false;
165 if (!preMenuInit()) return;
166 menuInitialized = true;
167
168 int fh = -1;
169 if (cvar_string("menu_skin") != "")
170 {
171 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
172 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
173 }
174 if (fh < 0 && cvar_defstring("menu_skin") != "")
175 {
176 cvar_set("menu_skin", cvar_defstring("menu_skin"));
177 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
178 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
179 }
180 if (fh < 0)
181 {
182 draw_currentSkin = "gfx/menu/wickedx"; // default menu skin
183 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
184 }
185 if (fh < 0) error("cannot load any menu skin\n");
186 draw_currentSkin = strzone(draw_currentSkin);
187 for (string s; (s = fgets(fh)); )
188 {
189 // these two are handled by skinlist.qc
190 if (substring(s, 0, 6) == "title ") continue;
191 if (substring(s, 0, 7) == "author ") continue;
192 int n = tokenize_console(s);
193 if (n < 2) continue;
194 Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
195 }
196 fclose(fh);
197
198 int glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true);
199 if (glob >= 0)
200 {
201 for (int i = 0, n = search_getsize(glob); i < n; ++i)
203 search_end(glob);
204 }
205
206 draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
207
208 anim = NEW(AnimHost);
210 main.configureMainWindow(main);
211
212 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
213 main.focused = true;
214 menuShiftState = 0;
215 menuMousePos = '0.5 0.5 0';
216
217 m_sync();
218
219 if (m_goto_buffer)
220 {
221 m_goto(m_goto_buffer, false);
223 }
224
225 if (Menu_Active) m_display(); // delayed menu display
226
227 cvar_set("_menu_initialized", "2");
228}
229
230void m_keyup(float key, float ascii)
231{
232 if (!menuInitialized) return;
233 if (!Menu_Active) return;
235 main.keyUp(main, key, ascii, menuShiftState);
236 if (key >= K_MOUSE1 && key <= K_MOUSE3)
237 {
239 if (!mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
240 if (mouseButtonsPressed < 0)
241 {
243 LOG_TRACE("Warning: released an already released button");
244 }
245 }
246 if (key == K_ALT) menuShiftState &= ~S_ALT;
247 if (key == K_CTRL) menuShiftState &= ~S_CTRL;
248 if (key == K_SHIFT) menuShiftState &= ~S_SHIFT;
249}
250
251void m_keydown(float key, float ascii)
252{
253 if (!menuInitialized) return;
254 if (!Menu_Active) return;
255
256 if (menuMouseMode && key >= K_MOUSE1 && key <= K_MOUSE3)
257 {
258 // detect a click outside of the game window
259 vector p = getmousepos();
260 if (p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight)
261 {
263 return;
264 }
265 }
266
267 if (keyGrabber)
268 {
269 entity e = keyGrabber;
271 e.keyGrabbed(e, key, ascii);
272 }
273 else
274 {
276 if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3)
277 main.mousePress(main, menuMousePos);
278 if (!main.keyDown(main, key, ascii, menuShiftState))
279 {
280 // disable menu on unhandled ESC
281 if (key == K_ESCAPE)
282 if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
283 m_hide();
284 }
285 }
286 if (key >= K_MOUSE1 && key <= K_MOUSE3)
287 {
289 if (mouseButtonsPressed > 10)
290 {
292 LOG_TRACE("Warning: pressed an already pressed button");
293 }
294 }
295 if (key == K_ALT) menuShiftState |= S_ALT;
296 if (key == K_CTRL) menuShiftState |= S_CTRL;
297 if (key == K_SHIFT) menuShiftState |= S_SHIFT;
298}
299
300enum {
306};
307void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
308{
309 vector sz = draw_PictureSize(img);
310 bool width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x);
311 vector isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y));
312 vector isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x));
313 vector isz;
314 switch (scalemode)
315 {
316 default:
317 case SCALEMODE_CROP:
318 isz = (width_is_larger ? isz_h : isz_w);
319 break;
321 isz = (width_is_larger ? isz_w : isz_h);
322 break;
323 case SCALEMODE_WIDTH:
324 isz = isz_w;
325 break;
326 case SCALEMODE_HEIGHT:
327 isz = isz_h;
328 break;
330 isz = '1 1 0';
331 break;
332 }
333 vector org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y));
334 draw_Picture(org, img, isz, '1 1 1', a);
335}
336
337void drawBackground(string img, float a, string algn, float force1)
338{
339 if (main.mainNexposee.ModalController_state == 0
340 && !(main.focusedChild && main.focusedChild.hideMenuOnClose && !(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
341 return;
342
343 vector v = '0 0 0';
344 int scalemode = SCALEMODE_CROP;
345 int len = strlen(algn);
346 for (int i = 0, l = 0; i < len; ++i)
347 {
348 string c = substring(algn, i, 1);
349 switch (c)
350 {
351 case "c":
352 scalemode = SCALEMODE_CROP;
353 goto nopic;
354 case "l":
355 scalemode = SCALEMODE_LETTERBOX;
356 goto nopic;
357 case "h":
358 scalemode = SCALEMODE_HEIGHT;
359 goto nopic;
360 case "w":
361 scalemode = SCALEMODE_WIDTH;
362 goto nopic;
363 case "s":
364 scalemode = SCALEMODE_STRETCH;
365 goto nopic;
366 case "1": case "4": case "7":
367 v.x = 0.0;
368 break;
369 case "2": case "5": case "8":
370 v.x = 0.5;
371 break;
372 case "3": case "6": case "9":
373 v.x = 1.0;
374 break;
375 default:
376 v.x = random();
377 break;
378 }
379 switch (c)
380 {
381 case "7": case "8": case "9":
382 v.y = 0.0;
383 break;
384 case "4": case "5": case "6":
385 v.y = 0.5;
386 break;
387 case "1": case "2": case "3":
388 v.y = 1.0;
389 break;
390 default:
391 v.y = random();
392 break;
393 }
394 if (l == 0)
395 {
396 draw_Picture_Aligned(v, scalemode, img, a);
397 }
398 else if (force1)
399 {
400 // force all secondary layers to use alpha 1. Prevents ugly issues
401 // with overlap. It's a flag because it cannot be used for the
402 // ingame background
403 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), 1);
404 }
405 else
406 {
407 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), a);
408 }
409 ++l;
410LABEL(nopic)
411 }
412}
413
422int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
424{
425 return !(
426 (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x)
427 && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y)
428 );
429}
430bool m_testtooltipbox(vector tooltippos)
431{
432 if (tooltippos.x < 0) return false;
433 if (tooltippos.y < 0) return false;
434 if (tooltippos.x + menuTooltipSize.x > 1) return false;
435 if (tooltippos.y + menuTooltipSize.y > 1) return false;
436 menuTooltipOrigin = tooltippos;
437 return true;
438}
440{
441 vector avoidplus;
442 avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
443 avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
444 avoidplus.z = 0;
445
446 vector avoidminus;
447 avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x;
448 avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y;
449 avoidminus.z = 0;
450
451 // bottom right
452 vector v = pos + avoidplus;
453 if (m_testtooltipbox(v)) return true;
454
455 // bottom center
456 v.x = pos.x - menuTooltipSize.x * 0.5;
457 if (m_testtooltipbox(v)) return true;
458
459 // bottom left
460 v.x = pos.x - avoidminus.x;
461 if (m_testtooltipbox(v)) return true;
462
463 // top left
464 v.y = pos.y - avoidminus.y;
465 if (m_testtooltipbox(v)) return true;
466
467 // top center
468 v.x = pos.x - menuTooltipSize.x * 0.5;
469 if (m_testtooltipbox(v)) return true;
470
471 // top right
472 v.x = pos.x + avoidplus.x;
473 if (m_testtooltipbox(v)) return true;
474
475 return false;
476}
478{
479 entity best = NULL;
480 for (entity it = root; it.instanceOfContainer; )
481 {
482 while (it.instanceOfNexposee && it.focusedChild)
483 {
484 it = it.focusedChild;
485 pos = globalToBox(pos, it.Container_origin, it.Container_size);
486 }
487 if (it.instanceOfNexposee)
488 {
489 it = it.itemFromPoint(it, pos);
490 if (it.tooltip) best = it;
491 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
492 it = NULL;
493 }
494 else if (it.instanceOfModalController)
495 {
496 it = it.focusedChild;
497 }
498 else
499 {
500 it = it.itemFromPoint(it, pos);
501 }
502 if (!it) break;
503 if (it.tooltip) best = it;
504 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
505 pos = globalToBox(pos, it.Container_origin, it.Container_size);
506 }
507
508 return best;
509}
510string gettooltip_dependency_string_numeric(entity e, .string cvarStr, .float cvarMin, .float cvarMax, float not_if_equal)
511{
512 if (e.(cvarMin) == e.(cvarMax))
513 {
514 if (not_if_equal)
515 return sprintf(_("^3%s^7 not equal to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMin)));
516 else
517 return sprintf("^3%s^7 \"%s\"", e.(cvarStr), ftos_mindecimals(e.(cvarMin)));
518 }
519 else if (e.(cvarMin) < e.(cvarMax))
520 return sprintf(_("^3%s^7 in range \"%s\" to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMin)), ftos_mindecimals(e.(cvarMax)));
521 else
522 return sprintf(_("^3%s^7 outside range \"%s\" to \"%s\""), e.(cvarStr), ftos_mindecimals(e.(cvarMax)), ftos_mindecimals(e.(cvarMin)));
523}
525{
526 string s;
527 if (menu_tooltips == 2)
528 {
529 if (menuTooltipItem.controlledCvar)
530 {
531 string cvar_list = getCvarsMulti(menuTooltipItem);
532 if (cvar_list)
533 cvar_list = strcat(menuTooltipItem.controlledCvar, " ", cvar_list);
534 else
535 cvar_list = menuTooltipItem.controlledCvar;
536 s = strcat("^3", cvar_list,
537 " ^7\"", cvar_string(menuTooltipItem.controlledCvar), "^7\" "
538 "^8[\"", cvar_defstring(menuTooltipItem.controlledCvar), "^8\"]^7");
539 }
540 else if (menuTooltipItem.onClickCommand)
541 s = strcat("<", menuTooltipItem.onClickCommand, "^7>");
542 else
543 s = "";
544
545 if (menuTooltipItem.tooltip)
546 s = strcat(rgb_to_hexcolor(SKINCOLOR_TOOLTIP), (s == "" ? menuTooltipItem.tooltip : strcat(menuTooltipItem.tooltip, "\n\n", s)));
547
548 if (menuTooltipItem.disabled)
549 {
550 if (menuTooltipItem.func_setDependent)
551 s = strcat(s, "\n\n", _("Has special requirements"));
552 else if (menuTooltipItem.cvarString_setDependent)
553 s = strcat(s, "\n\n",
554 sprintf(_("Requires ^3%s^7 not equal to \"%s^7\""), menuTooltipItem.cvarString_setDependent, menuTooltipItem.cvarValue_setDependent));
555 else if (menuTooltipItem.cvar1_setDependent)
556 {
557 s = strcat(s, "\n\n");
558 string cvar1_s, cvar2_s, cvar3_s;
560 if (menuTooltipItem.cvar2_setDependent)
561 {
563 if (menuTooltipItem.cvar3_setDependent)
564 {
566 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));
567 }
568 else
569 s = strcat(s, sprintf((menuTooltipItem.op_setDependent & BIT(0) ? _("Requires %s or %s") : _("Requires %s and %s")), cvar1_s, cvar2_s));
570 }
571 else
572 s = strcat(s, sprintf(_("Requires %s"), cvar1_s));
573 }
574 // if this point is reached, it's disabled for some other unknown reason
575 }
576 }
577 else
578 s = menuTooltipItem.tooltip;
579 return s;
580}
582{
583 const float MAX_TOOLTIP_LINES = 16;
584 static string prev_tooltip;
585 entity it;
586 menu_tooltips = cvar("menu_tooltips");
587 if (!menu_tooltips)
588 {
589 // don't return immediately, fade out the active tooltip first
590 if (menuTooltipItem == NULL) return;
591 it = NULL;
593 }
594 else
595 {
596 float f = bound(0, frametime * 2, 1);
598 if (vdist(pos - menuTooltipAveragedMousePos, <, 0.01))
599 {
600 it = m_findtooltipitem(main, pos);
601
602 if (it.instanceOfListBox && it.isScrolling(it)) it = NULL;
603
604 if (it && prev_tooltip != it.tooltip)
605 {
606 // fade out if tooltip of a certain item has changed
608 strcpy(prev_tooltip, it.tooltip);
609 }
610 else if (menuTooltipItem && !m_testmousetooltipbox(pos))
611 {
612 menuTooltipState = 3; // fade out if mouse touches it
613 }
614 }
615 else
616 {
617 it = NULL;
618 }
619 }
620 vector fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
621
622 // float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
623 if (it != menuTooltipItem)
624 {
625 switch (menuTooltipState)
626 {
627 case 0:
628 if (menuTooltipItem)
629 {
630 // another item: fade out first
632 }
633 else
634 {
635 // new item: fade in
637 menuTooltipItem = it;
638
639 menuTooltipOrigin.x = -1; // unallocated
640
642
643 float w = 0;
644 float lines = 0;
646 for (int k = 0; k < n && lines <= MAX_TOOLTIP_LINES; ++k)
647 {
648 if (argv(k) == "")
649 {
650 lines += 0.5; // insert a half-height separator between paragraphs (marked by 2 newlines)
651 continue;
652 }
653 for (getWrappedLine_remaining = argv(k); getWrappedLine_remaining && lines <= MAX_TOOLTIP_LINES; ++lines)
654 {
655 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithColors);
656 if (lines + 1 > MAX_TOOLTIP_LINES)
657 s = "...";
658 float f = draw_TextWidth_WithColors(s, fontsize);
659 if (f > w) w = f;
660 }
661 }
662 menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
663 menuTooltipSize.y = lines * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
664 menuTooltipSize.z = 0;
665 }
666 break;
667 case 1:
668 // changing item while fading in: fade out first
670 break;
671 case 2:
672 // changing item while fading out: can't
673 break;
674 }
675 }
676 else if (menuTooltipState == 2) // re-fade in?
677 {
679 }
680
681 switch (menuTooltipState)
682 {
683 case 1: // fade in
686 break;
687 case 2: // fade out
688 case 3: // forced fade out
690 if (menuTooltipAlpha == 0)
691 {
694 }
695 break;
696 }
697
698 if (menuTooltipItem == NULL)
699 {
701 return;
702 }
703 else
704 {
706 {
707 if (menu_tooltips != 0 && menu_tooltips_old != 0) menuTooltipItem = NULL; // reload tooltip next frame
709 }
710 else if (menuTooltipOrigin.x < 0) // unallocated?
711 {
713 }
714 if (menuTooltipOrigin.x >= 0)
715 {
716 // draw the tooltip!
717 vector p = SKINBORDER_TOOLTIP;
718 p.x *= 1 / conwidth;
719 p.y *= 1 / conheight;
722 p.x += SKINMARGIN_TOOLTIP_x / conwidth;
723 p.y += SKINMARGIN_TOOLTIP_y / conheight;
724 float lines = 0;
726 for (int k = 0; k < n && lines <= MAX_TOOLTIP_LINES; ++k)
727 {
728 if (argv(k) == "")
729 {
730 p.y += fontsize.y / 2;
731 continue;
732 }
733 for (getWrappedLine_remaining = argv(k); getWrappedLine_remaining && lines <= MAX_TOOLTIP_LINES; ++lines)
734 {
735 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithColors);
736 if (lines + 1 > MAX_TOOLTIP_LINES)
737 s = "...";
738 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, true);
739 p.y += fontsize.y;
740 }
741 }
742 }
743 }
744}
745
748void m_draw(float width, float height)
749{
750 static float connected_time;
752 {
753 // avoid a bug where the main menu re-opens when changing maps
754 // potentially exclusive to `map <mapname>` cmd?
755 if (connected_time && time - connected_time > MIN_DISCONNECTION_TIME)
756 {
758 {
759 // in the case player uses the disconnect command (in the console or with a key)
760 // reset g_campaign and update menu items to reflect cvar values that may have been restored after quiting the campaign
761 // see also LEAVEMATCH_CMD
762 cvar_set("g_campaign", "0");
763 m_sync();
764 }
765
766 // reload the menu so that disconnecting players don't
767 // have to press ESC to open it again
768 m_toggle(true);
769
770 localcmd("\nmenu_cmd directmenu Welcome RESET\n");
771 connected_time = 0;
772
773 // reset main menu
774 // FIXME?: find out if anything should be done to reset it more,
775 // this is just a fix to make main menu music replay nicely
777 }
778 }
779 else
780 connected_time = time;
781
782 m_gamestatus();
783
785
786 menuMouseMode = cvar("menu_mouse_absolute");
787
788 if (anim) anim.tickAll(anim);
789
790 UpdateConWidthHeight(width, height, cvar("vid_pixelheight"));
791
792 if (!menuInitialized)
793 {
794 // TODO draw an info image about this situation
796 return;
797 }
798
799 if (menuNotTheFirstFrame == 0) // only fade the menu in once ever
800 menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME
801
802 if (menuNotTheFirstFrame <= 1) // only once per menu reload
803 {
805 {
806 localcmd("cd loop $menu_cdtrack\n");
807
808 // TODO: enable this when we have a welcome sound
809 // FIXME: change the file used according to the selected announcer
810 // Only play the welcome announcement once, not on any menu reloads
811 //if (menuNotTheFirstFrame == 0)
812 //localcmd("play sound/announcer/default/welcome.wav\n");
813 }
814
816 }
817
818 float t = gettime();
819 float realFrametime = frametime = min(0.2, t - menuPrevTime);
820 menuPrevTime = t;
821 time += frametime;
822
823 t = cvar("menu_slowmo");
824 if (t)
825 {
826 frametime *= t;
827 realFrametime *= t;
828 }
829 else
830 {
831 t = 1;
832 }
833
834 if (Menu_Active)
835 {
839 else m_hide();
840 }
841
842 if (cvar("cl_capturevideo")) frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
843
845 if (Menu_Active)
846 {
847 if (menuAlpha == 0 && menuLogoAlpha < 2)
848 {
850 }
851 else
852 {
853 menuAlpha = min(1, menuAlpha + 5 * frametime);
854 menuLogoAlpha = 2;
855 }
856 }
857 else
858 {
859 menuAlpha = max(0, menuAlpha - 5 * frametime);
860 menuLogoAlpha = 2;
861 }
862
864
866 {
867 if (menuLogoAlpha > 0)
868 {
870 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
871 drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true);
874 {
875 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
877 draw_alpha = 1;
878 }
879 }
880 }
881 else if (SKINALPHA_BACKGROUND_INGAME)
882 {
883 if (menuAlpha > 0)
884 {
886 drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME,
887 SKINALIGN_BACKGROUND_INGAME, false);
889 }
890 }
891
892 if (menuAlpha != prevMenuAlpha) cvar_set("_menu_alpha", ftos(menuAlpha));
893
895 preMenuDraw();
897
898 if (menuAlpha <= 0)
899 {
900 if (prevMenuAlpha > 0) main.initializeDialog(main, main.firstChild);
902 postMenuDraw();
903 return;
904 }
905
907
908 if (menuMouseMode)
909 {
910 vector rawMousePos = getmousepos();
911 vector newMouse = globalToBox(rawMousePos, draw_shift, draw_scale);
912 if (rawMousePos != '0 0 0' && newMouse != menuMousePos)
913 {
914 menuMousePos = newMouse;
915 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
916 else main.mouseMove(main, menuMousePos);
917 }
918 }
919 else if (frametime > 0)
920 {
921 vector dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
922 if (dMouse != '0 0 0')
923 {
924 vector minpos = globalToBox('0 0 0', draw_shift, draw_scale);
926 dMouse = globalToBoxSize(dMouse, draw_scale);
927 menuMousePos += dMouse * cvar("menu_mouse_speed");
928 menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x);
929 menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y);
930 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
931 else main.mouseMove(main, menuMousePos);
932 }
933 }
934 main.draw(main);
935
937
938 draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
939
941
943 postMenuDraw();
944
945 frametime = 0;
946 IL_ENDFRAME();
947}
948
950{
951 Menu_Active = true;
954
955 if (!menuInitialized) return;
956
957 if (mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
959
960 main.focusEnter(main);
961 main.showNotify(main);
962}
963
964void m_hide()
965{
966 Menu_Active = false;
969
970 if (!menuInitialized) return;
971
972 main.focusLeave(main);
973 main.hideNotify(main);
974}
975
976void m_toggle(int mode)
977{
978 if (Menu_Active)
979 {
980 if (mode == 1) return;
981 // when togglemenu is called without arguments (mode is -1)
982 // the menu is closed only when connected
983 if (mode == -1 && !(gamestatus & GAME_CONNECTED)) return;
984 // togglemenu 0 always closes the menu
985 m_hide();
986 }
987 else
988 {
989 if (mode == 0) return;
990 m_display();
991 }
992}
993
995{
996 m_hide();
997 FOREACH_ENTITY_ORDERED(it.destroy, {
998 if (it.classname == "vtbl") continue;
999 it.destroy(it);
1000 });
1001 cvar_set("_menu_cmd_closemenu_available", "0");
1002}
1003
1004void m_focus_item_chain(entity outermost, entity innermost)
1005{
1006 if (innermost.parent != outermost) m_focus_item_chain(outermost, innermost.parent);
1007 innermost.parent.setFocus(innermost.parent, innermost);
1008}
1009
1011{
1012 entity par = wnd.parent;
1013 if (par) m_activate_window(par);
1014
1015 if (par.instanceOfModalController)
1016 {
1017 if (wnd.tabSelectingButton)
1018 // tabs
1019 TabButton_Click(wnd.tabSelectingButton, wnd);
1020 else
1021 // root
1022 par.initializeDialog(par, wnd);
1023 }
1024 else if (par.instanceOfNexposee)
1025 {
1026 // nexposee (sorry for violating abstraction here)
1027 par.selectedChild = wnd;
1028 par.animationState = 1;
1030 }
1031 else if (par.instanceOfContainer)
1032 {
1033 // other containers
1034 if (par.focused) par.setFocus(par, wnd);
1035 }
1036}
1037
1039{
1040 if (!wnd.instanceOfContainer) return;
1041 entity focus = wnd.preferredFocusedGrandChild(wnd);
1042 if (!focus) return;
1043 menuMousePos = focus.origin + 0.5 * focus.size;
1044 menuMousePos.x *= 1 / conwidth;
1045 menuMousePos.y *= 1 / conheight;
1046 entity par = wnd.parent;
1047 if (par.focused) par.setFocus(par, wnd);
1048 if (wnd.focused) m_focus_item_chain(wnd, focus);
1049}
1050
1051void m_goto(string itemname, bool hide_menu_on_close)
1052{
1053 if (!menuInitialized)
1054 {
1055 strcpy(m_goto_buffer, itemname);
1056 return;
1057 }
1058 if (itemname == "") // this can be called by GameCommand
1059 {
1061 {
1062 m_hide();
1063 return;
1064 }
1065 itemname = "nexposee";
1066 }
1067
1068 if (itemname == "nexposee")
1069 {
1070 // unlike 'togglemenu 1', this closes modal and root dialogs if opened
1071 m_activate_window(main.mainNexposee);
1072 m_display();
1073 }
1074 else
1075 {
1076 entity e;
1077 for (e = NULL; (e = find(e, name, itemname)); )
1078 if (e.classname != "vtbl") break;
1079
1080 if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
1081 {
1082 m_hide();
1085 m_display();
1086 if (hide_menu_on_close)
1087 {
1088 while(e.parent && !(e.instanceOfDialog && e.isTabRoot))
1089 e = e.parent;
1090 if (e.instanceOfDialog)
1091 e.hideMenuOnClose = true;
1092 }
1093 }
1094 }
1095}
1096
1098{
1099 static float menuLastFocusSoundTime;
1100 if (cvar("menu_sounds") < 2) return;
1101 if (time - menuLastFocusSoundTime <= 0.25) return;
1103 menuLastFocusSoundTime = time;
1104}
1105
1106void m_play_click_sound(string soundfile)
1107{
1108 if (!cvar("menu_sounds")) return;
1109 localsound(soundfile);
1110}
#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:1748
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:995
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:423
vector menuTooltipAveragedMousePos
Definition menu.qc:416
int menu_tooltips
Definition menu.qc:414
void m_play_click_sound(string soundfile)
Definition menu.qc:1106
float prevMenuAlpha
Definition menu.qc:32
void m_hide()
Definition menu.qc:964
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:114
bool m_testtooltipbox(vector tooltippos)
Definition menu.qc:430
bool m_allocatetooltipbox(vector pos)
Definition menu.qc:439
vector menuTooltipOrigin
Definition menu.qc:418
void m_init()
Definition menu.qc:61
void m_display()
Definition menu.qc:949
void m_play_focus_sound()
Definition menu.qc:1097
float realconwidth
Definition menu.qc:43
void m_activate_window(entity wnd)
Definition menu.qc:1010
float realconheight
Definition menu.qc:43
void m_init_delayed()
Definition menu.qc:160
void m_tooltip(vector pos)
Definition menu.qc:581
string menuTooltipText
Definition menu.qc:421
int menu_tooltips_old
Definition menu.qc:415
const int MIN_DISCONNECTION_TIME
Definition menu.qc:746
int menuNotTheFirstFrame
Definition menu.qc:38
vector menuTooltipSize
Definition menu.qc:419
string gettooltip()
Definition menu.qc:524
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:1004
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:477
float menuPrevTime
Definition menu.qc:29
void m_keyup(float key, float ascii)
Definition menu.qc:230
entity menuTooltipItem
Definition menu.qc:417
int menuTooltipState
Definition menu.qc:422
void m_setpointerfocus(entity wnd)
Definition menu.qc:1038
void m_sync()
Definition menu.qc:45
float menuTooltipAlpha
Definition menu.qc:420
bool autocvar_g_campaign
Definition menu.qc:747
void draw_reset_cropped()
Definition menu.qc:110
void UpdateConWidthHeight(float w, float h, float p)
Definition menu.qc:119
void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
Definition menu.qc:307
bool autocvar_menu_no_music_nor_welcome
Definition menu.qc:39
void drawBackground(string img, float a, string algn, float force1)
Definition menu.qc:337
void Shutdown()
Definition menu.qc:994
string gettooltip_dependency_string_numeric(entity e,.string cvarStr,.float cvarMin,.float cvarMax, float not_if_equal)
Definition menu.qc:510
float vidpixelheight_s
Definition menu.qc:42
void m_goto(string itemname, bool hide_menu_on_close)
Definition menu.qc:1051
const float MENU_ASPECT
Definition menu.qc:108
int mouseButtonsPressed
Definition menu.qc:26
@ SCALEMODE_CROP
Definition menu.qc:301
@ SCALEMODE_HEIGHT
Definition menu.qc:304
@ SCALEMODE_LETTERBOX
Definition menu.qc:302
@ SCALEMODE_WIDTH
Definition menu.qc:303
@ SCALEMODE_STRETCH
Definition menu.qc:305
string m_goto_buffer
Definition menu.qc:159
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