5 #include "anim/animhost.qh"
7 #include "item/dialog.qh"
8 #include "item/listbox.qh"
9 #include "item/nexposee.qh"
11 #include "xonotic/commandbutton.qh"
12 #include "xonotic/mainwindow.qh"
13 #include "xonotic/serverlist.qh"
14 #include "xonotic/slider_resolution.qh"
16 .string controlledCvar;
18 #include "xonotic/util.qh"
20 #include <common/items/_mod.qh>
21 #include <common/weapons/_all.qh>
22 #include <common/mapinfo.qh>
23 #include <common/mutators/base.qh>
25 int mouseButtonsPressed;
35 // Used for having effects only execute once in main menu, not for every reload
36 // 0: never been in main menu before. 1: coming back to main menu. 2: in main menu.
37 int menuNotTheFirstFrame;
38 bool autocvar_menu_no_music_nor_welcome;
40 float conwidth_s, conheight_s;
41 float vidwidth_s, vidheight_s, vidpixelheight_s;
42 float realconwidth, realconheight;
47 vidwidth_s = vidheight_s = vidpixelheight_s = 0; // Force updateConwidths on next draw
55 if (isserver()) gamestatus |= GAME_ISSERVER;
56 if (clientstate() == CS_CONNECTED || isdemo()) gamestatus |= GAME_CONNECTED;
57 if (cvar("developer") > 0) gamestatus |= GAME_DEVELOPER;
62 bool restarting = false;
63 cvar_set("_menu_alpha", "0");
64 prvm_language = cvar_string("prvm_language");
65 if (prvm_language == "")
68 cvar_set("prvm_language", prvm_language);
69 localcmd("\nmenu_restart\n");
72 prvm_language = strzone(prvm_language);
73 cvar_set("_menu_prvm_language", prvm_language);
76 LOG_TRACEF("^4MQC Build information: ^1%s", WATERMARK);
79 // list all game dirs (TEST)
80 if (cvar("developer") > 0)
82 for (int i = 0; ; ++i)
84 string s = getgamedirinfo(i, GETGAMEDIRINFO_NAME);
86 LOG_TRACE(s, ": ", getgamedirinfo(i, GETGAMEDIRINFO_DESCRIPTION));
90 registercvar("_menu_cmd_closemenu_available", "0", 0);
91 cvar_set("_menu_cmd_closemenu_available", "1");
93 // needs to be done so early because of the constants they create
96 static_init_precache();
98 RegisterSLCategories();
100 float ddsload = cvar("r_texture_dds_load");
101 float texcomp = cvar("gl_texturecompression");
103 if (ddsload != cvar("r_texture_dds_load") || texcomp != cvar("gl_texturecompression")) localcmd("\nr_restart\n");
107 if (cvar("_menu_initialized")) // always show menu after menu_restart
110 cvar_set("_menu_initialized", "1");
114 const float MENU_ASPECT = 1280 / 1024;
116 void draw_reset_cropped()
118 draw_reset(conwidth, conheight, 0.5 * (realconwidth - conwidth), 0.5 * (realconheight - conheight));
120 void draw_reset_full()
122 draw_reset(realconwidth, realconheight, 0, 0);
125 void UpdateConWidthHeight(float w, float h, float p)
127 if (w != vidwidth_s || h != vidheight_s || p != vidpixelheight_s)
129 if (updateConwidths(w, h, p) && menuNotTheFirstFrame)
130 localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg")));
133 vidpixelheight_s = p;
135 conwidth_s = conwidth;
136 conheight_s = conheight;
137 realconwidth = cvar("vid_conwidth");
138 realconheight = cvar("vid_conheight");
139 if (realconwidth / realconheight > MENU_ASPECT)
142 conwidth = realconheight * MENU_ASPECT;
143 conheight = realconheight;
148 conwidth = realconwidth;
149 conheight = realconwidth / MENU_ASPECT;
153 if (conwidth_s != conwidth || conheight_s != conheight)
155 draw_reset_cropped();
156 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
161 vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame
165 string m_goto_buffer;
166 void m_init_delayed()
168 draw_reset_cropped();
170 menuInitialized = false;
171 if (!preMenuInit()) return;
172 menuInitialized = true;
175 if (cvar_string("menu_skin") != "")
177 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
178 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
180 if (fh < 0 && cvar_defstring("menu_skin") != "")
182 cvar_set("menu_skin", cvar_defstring("menu_skin"));
183 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
184 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
188 draw_currentSkin = "gfx/menu/default";
189 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
191 if (fh < 0) error("cannot load any menu skin\n");
192 draw_currentSkin = strzone(draw_currentSkin);
193 for (string s; (s = fgets(fh)); )
195 // these two are handled by skinlist.qc
196 if (substring(s, 0, 6) == "title ") continue;
197 if (substring(s, 0, 7) == "author ") continue;
198 int n = tokenize_console(s);
200 Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
204 int glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true);
207 for (int i = 0, n = search_getsize(glob); i < n; ++i)
208 precache_pic(search_getfilename(glob, i));
212 draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
214 anim = NEW(AnimHost);
215 main = NEW(MainWindow);
216 main.configureMainWindow(main);
218 main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
221 menuMousePos = '0.5 0.5 0';
227 m_goto(m_goto_buffer);
228 strfree(m_goto_buffer);
231 if (Menu_Active) m_display(); // delayed menu display
233 cvar_set("_menu_initialized", "2");
236 void m_keyup(float key, float ascii)
238 if (!menuInitialized) return;
239 if (!Menu_Active) return;
240 draw_reset_cropped();
241 main.keyUp(main, key, ascii, menuShiftState);
242 if (key >= K_MOUSE1 && key <= K_MOUSE3)
244 --mouseButtonsPressed;
245 if (!mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
246 if (mouseButtonsPressed < 0)
248 mouseButtonsPressed = 0;
249 LOG_TRACE("Warning: released an already released button");
252 if (key == K_ALT) menuShiftState &= ~S_ALT;
253 if (key == K_CTRL) menuShiftState &= ~S_CTRL;
254 if (key == K_SHIFT) menuShiftState &= ~S_SHIFT;
257 void m_keydown(float key, float ascii)
259 if (!menuInitialized) return;
260 if (!Menu_Active) return;
262 if (menuMouseMode && key >= K_MOUSE1 && key <= K_MOUSE3)
264 // detect a click outside of the game window
265 vector p = getmousepos();
266 if (p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight)
268 ++mouseButtonsPressed;
275 entity e = keyGrabber;
277 e.keyGrabbed(e, key, ascii);
281 draw_reset_cropped();
282 if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3)
283 main.mousePress(main, menuMousePos);
284 if (!main.keyDown(main, key, ascii, menuShiftState))
286 // disable menu on unhandled ESC
288 if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
292 if (key >= K_MOUSE1 && key <= K_MOUSE3)
294 ++mouseButtonsPressed;
295 if (mouseButtonsPressed > 10)
297 mouseButtonsPressed = 10;
298 LOG_TRACE("Warning: pressed an already pressed button");
301 if (key == K_ALT) menuShiftState |= S_ALT;
302 if (key == K_CTRL) menuShiftState |= S_CTRL;
303 if (key == K_SHIFT) menuShiftState |= S_SHIFT;
313 void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
315 vector sz = draw_PictureSize(img);
316 bool width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x);
317 vector isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y));
318 vector isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x));
324 isz = (width_is_larger ? isz_h : isz_w);
326 case SCALEMODE_LETTERBOX:
327 isz = (width_is_larger ? isz_w : isz_h);
329 case SCALEMODE_WIDTH:
332 case SCALEMODE_HEIGHT:
335 case SCALEMODE_STRETCH:
339 vector org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y));
340 draw_Picture(org, img, isz, '1 1 1', a);
343 void drawBackground(string img, float a, string algn, float force1)
345 if (main.mainNexposee.ModalController_state == 0) return;
347 int scalemode = SCALEMODE_CROP;
348 int len = strlen(algn);
349 for (int i = 0, l = 0; i < len; ++i)
351 string c = substring(algn, i, 1);
355 scalemode = SCALEMODE_CROP;
358 scalemode = SCALEMODE_LETTERBOX;
361 scalemode = SCALEMODE_HEIGHT;
364 scalemode = SCALEMODE_WIDTH;
367 scalemode = SCALEMODE_STRETCH;
369 case "1": case "4": case "7":
372 case "2": case "5": case "8":
375 case "3": case "6": case "9":
384 case "7": case "8": case "9":
387 case "4": case "5": case "6":
390 case "1": case "2": case "3":
399 draw_Picture_Aligned(v, scalemode, img, a);
403 // force all secondary layers to use alpha 1. Prevents ugly issues
404 // with overlap. It's a flag because it cannot be used for the
406 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), 1);
410 draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), a);
418 int menu_tooltips_old;
419 vector menuTooltipAveragedMousePos;
420 entity menuTooltipItem;
421 vector menuTooltipOrigin;
422 vector menuTooltipSize;
423 float menuTooltipAlpha;
424 string menuTooltipText;
425 int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
426 bool m_testmousetooltipbox(vector pos)
429 (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x)
430 && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y)
433 bool m_testtooltipbox(vector tooltippos)
435 if (tooltippos.x < 0) return false;
436 if (tooltippos.y < 0) return false;
437 if (tooltippos.x + menuTooltipSize.x > 1) return false;
438 if (tooltippos.y + menuTooltipSize.y > 1) return false;
439 menuTooltipOrigin = tooltippos;
442 bool m_allocatetooltipbox(vector pos)
445 avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
446 avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
450 avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x;
451 avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y;
455 vector v = pos + avoidplus;
456 if (m_testtooltipbox(v)) return true;
459 v.x = pos.x - menuTooltipSize.x * 0.5;
460 if (m_testtooltipbox(v)) return true;
463 v.x = pos.x - avoidminus.x;
464 if (m_testtooltipbox(v)) return true;
467 v.y = pos.y - avoidminus.y;
468 if (m_testtooltipbox(v)) return true;
471 v.x = pos.x - menuTooltipSize.x * 0.5;
472 if (m_testtooltipbox(v)) return true;
475 v.x = pos.x + avoidplus.x;
476 if (m_testtooltipbox(v)) return true;
480 entity m_findtooltipitem(entity root, vector pos)
483 for (entity it = root; it.instanceOfContainer; )
485 while (it.instanceOfNexposee && it.focusedChild)
487 it = it.focusedChild;
488 pos = globalToBox(pos, it.Container_origin, it.Container_size);
490 if (it.instanceOfNexposee)
492 it = it.itemFromPoint(it, pos);
493 if (it.tooltip) best = it;
494 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
497 else if (it.instanceOfModalController)
499 it = it.focusedChild;
503 it = it.itemFromPoint(it, pos);
506 if (it.tooltip) best = it;
507 else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
508 pos = globalToBox(pos, it.Container_origin, it.Container_size);
515 if (menu_tooltips == 2)
518 if (menuTooltipItem.controlledCvar)
520 string cvar_list = getCvarsMulti(menuTooltipItem);
522 cvar_list = strcat(menuTooltipItem.controlledCvar, " ", cvar_list);
524 cvar_list = menuTooltipItem.controlledCvar;
525 s = strcat("[", cvar_list, " \"", cvar_string(menuTooltipItem.controlledCvar), "\"]");
527 else if (menuTooltipItem.onClickCommand)
529 s = strcat("<", menuTooltipItem.onClickCommand, ">");
533 return menuTooltipItem.tooltip;
535 if (menuTooltipItem.tooltip) return strcat(menuTooltipItem.tooltip, " ", s);
538 return menuTooltipItem.tooltip;
540 void m_tooltip(vector pos)
542 static string prev_tooltip;
544 menu_tooltips = cvar("menu_tooltips");
547 // don't return immediately, fade out the active tooltip first
548 if (menuTooltipItem == NULL) return;
550 menu_tooltips_old = menu_tooltips;
554 float f = bound(0, frametime * 2, 1);
555 menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f;
556 if (vdist(pos - menuTooltipAveragedMousePos, <, 0.01))
558 it = m_findtooltipitem(main, pos);
560 if (it.instanceOfListBox && it.isScrolling(it)) it = NULL;
562 if (it && prev_tooltip != it.tooltip)
564 // fade out if tooltip of a certain item has changed
565 menuTooltipState = 3;
566 strcpy(prev_tooltip, it.tooltip);
568 else if (menuTooltipItem && !m_testmousetooltipbox(pos))
570 menuTooltipState = 3; // fade out if mouse touches it
578 vector fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
580 // float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
581 if (it != menuTooltipItem)
583 switch (menuTooltipState)
588 // another item: fade out first
589 menuTooltipState = 2;
594 menuTooltipState = 1;
595 menuTooltipItem = it;
597 menuTooltipOrigin.x = -1; // unallocated
599 strcpy(menuTooltipText, gettooltip());
603 for (getWrappedLine_remaining = menuTooltipText; getWrappedLine_remaining && i <= 16; ++i)
605 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
608 float f = draw_TextWidth(s, false, fontsize);
611 menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
612 menuTooltipSize.y = i * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
613 menuTooltipSize.z = 0;
617 // changing item while fading in: fade out first
618 menuTooltipState = 2;
621 // changing item while fading out: can't
625 else if (menuTooltipState == 2) // re-fade in?
627 menuTooltipState = 1;
630 switch (menuTooltipState)
633 menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1);
634 if (menuTooltipAlpha == 1) menuTooltipState = 0;
637 case 3: // forced fade out
638 menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1);
639 if (menuTooltipAlpha == 0)
641 menuTooltipState = 0;
642 menuTooltipItem = NULL;
647 if (menuTooltipItem == NULL)
649 strfree(menuTooltipText);
654 if (menu_tooltips != menu_tooltips_old)
656 if (menu_tooltips != 0 && menu_tooltips_old != 0) menuTooltipItem = NULL; // reload tooltip next frame
657 menu_tooltips_old = menu_tooltips;
659 else if (menuTooltipOrigin.x < 0) // unallocated?
661 m_allocatetooltipbox(pos);
663 if (menuTooltipOrigin.x >= 0)
666 vector p = SKINBORDER_TOOLTIP;
668 p.y *= 1 / conheight;
669 draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
670 p = menuTooltipOrigin;
671 p.x += SKINMARGIN_TOOLTIP_x / conwidth;
672 p.y += SKINMARGIN_TOOLTIP_y / conheight;
674 for (getWrappedLine_remaining = menuTooltipText; getWrappedLine_remaining && i <= 16; ++i, p.y += fontsize.y)
676 string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
679 draw_Text(p, s, fontsize, SKINCOLOR_TOOLTIP, SKINALPHA_TOOLTIP * menuTooltipAlpha, false);
685 const int MIN_DISCONNECTION_TIME = 1;
686 bool autocvar_g_campaign;
687 void m_draw(float width, float height)
689 static float connected_time;
690 if (clientstate() == CS_DISCONNECTED)
692 // avoid a bug where the main menu re-opens when changing maps
693 // potentially exclusive to `map <mapname>` cmd?
694 if (connected_time && time - connected_time > MIN_DISCONNECTION_TIME)
696 if (autocvar_g_campaign)
698 // in the case player uses the disconnect command (in the console or with a key)
699 // reset g_campaign and update menu items to reflect cvar values that may have been restored after quiting the campaign
700 // see also LEAVEMATCH_CMD
701 cvar_set("g_campaign", "0");
705 // reload the menu so that disconnecting players don't
706 // have to press ESC to open it again
709 localcmd("\nmenu_cmd directmenu Welcome RESET\n");
713 // FIXME?: find out if anything should be done to reset it more,
714 // this is just a fix to make main menu music replay nicely
715 menuNotTheFirstFrame = 1;
719 connected_time = time;
723 execute_next_frame();
725 menuMouseMode = cvar("menu_mouse_absolute");
727 if (anim) anim.tickAll(anim);
729 UpdateConWidthHeight(width, height, cvar("vid_pixelheight"));
731 if (!menuInitialized)
733 // TODO draw an info image about this situation
738 if (menuNotTheFirstFrame == 0) // only fade the menu in once ever
739 menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME
741 if (menuNotTheFirstFrame <= 1) // only once per menu reload
743 if (Menu_Active && !autocvar_menu_no_music_nor_welcome)
745 localcmd("cd loop $menu_cdtrack\n");
747 // TODO: enable this when we have a welcome sound
748 // FIXME: change the file used according to the selected announcer
749 // Only play the welcome announcement once, not on any menu reloads
750 //if (menuNotTheFirstFrame == 0)
751 //localcmd("play sound/announcer/default/welcome.wav\n");
754 menuNotTheFirstFrame = 2;
758 float realFrametime = frametime = min(0.2, t - menuPrevTime);
762 t = cvar("menu_slowmo");
775 if (getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU)
776 && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED))
777 setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU);
781 if (cvar("cl_capturevideo")) frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
783 prevMenuAlpha = menuAlpha;
786 if (menuAlpha == 0 && menuLogoAlpha < 2)
788 menuLogoAlpha += 2 * frametime;
792 menuAlpha = min(1, menuAlpha + 5 * frametime);
798 menuAlpha = max(0, menuAlpha - 5 * frametime);
802 draw_reset_cropped();
804 if (!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
806 if (menuLogoAlpha > 0)
809 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
810 drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true);
811 draw_reset_cropped();
812 if (menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
814 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
815 draw_drawMousePointer(menuMousePos);
820 else if (SKINALPHA_BACKGROUND_INGAME)
825 drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME,
826 SKINALIGN_BACKGROUND_INGAME, false);
827 draw_reset_cropped();
831 if (menuAlpha != prevMenuAlpha) cvar_set("_menu_alpha", ftos(menuAlpha));
833 draw_reset_cropped();
835 draw_reset_cropped();
839 if (prevMenuAlpha > 0) main.initializeDialog(main, main.firstChild);
840 draw_reset_cropped();
845 draw_alpha *= menuAlpha;
849 vector rawMousePos = getmousepos();
850 vector newMouse = globalToBox(rawMousePos, draw_shift, draw_scale);
851 if (rawMousePos != '0 0 0' && newMouse != menuMousePos)
853 menuMousePos = newMouse;
854 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
855 else main.mouseMove(main, menuMousePos);
858 else if (frametime > 0)
860 vector dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
861 if (dMouse != '0 0 0')
863 vector minpos = globalToBox('0 0 0', draw_shift, draw_scale);
864 vector maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale);
865 dMouse = globalToBoxSize(dMouse, draw_scale);
866 menuMousePos += dMouse * cvar("menu_mouse_speed");
867 menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x);
868 menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y);
869 if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
870 else main.mouseMove(main, menuMousePos);
875 m_tooltip(menuMousePos);
877 draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
879 draw_drawMousePointer(menuMousePos);
881 draw_reset_cropped();
891 setkeydest(KEY_MENU);
892 setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU));
894 if (!menuInitialized) return;
896 if (mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
897 mouseButtonsPressed = 0;
899 main.focusEnter(main);
900 main.showNotify(main);
906 setkeydest(KEY_GAME);
907 setmousetarget(MT_CLIENT);
909 if (!menuInitialized) return;
911 main.focusLeave(main);
912 main.hideNotify(main);
915 void m_toggle(int mode)
919 if (mode == 1) return;
924 if (mode == 0) return;
932 FOREACH_ENTITY_ORDERED(it.destroy, {
933 if (it.classname == "vtbl") continue;
936 cvar_set("_menu_cmd_closemenu_available", "0");
939 void m_focus_item_chain(entity outermost, entity innermost)
941 if (innermost.parent != outermost) m_focus_item_chain(outermost, innermost.parent);
942 innermost.parent.setFocus(innermost.parent, innermost);
945 void m_activate_window(entity wnd)
947 entity par = wnd.parent;
948 if (par) m_activate_window(par);
950 if (par.instanceOfModalController)
952 if (wnd.tabSelectingButton)
954 TabButton_Click(wnd.tabSelectingButton, wnd);
957 par.initializeDialog(par, wnd);
959 else if (par.instanceOfNexposee)
961 // nexposee (sorry for violating abstraction here)
962 par.selectedChild = wnd;
963 par.animationState = 1;
964 Container_setFocus(par, NULL);
966 else if (par.instanceOfContainer)
969 if (par.focused) par.setFocus(par, wnd);
973 void m_setpointerfocus(entity wnd)
975 if (!wnd.instanceOfContainer) return;
976 entity focus = wnd.preferredFocusedGrandChild(wnd);
978 menuMousePos = focus.origin + 0.5 * focus.size;
979 menuMousePos.x *= 1 / conwidth;
980 menuMousePos.y *= 1 / conheight;
981 entity par = wnd.parent;
982 if (par.focused) par.setFocus(par, wnd);
983 if (wnd.focused) m_focus_item_chain(wnd, focus);
986 void m_goto(string itemname)
988 if (!menuInitialized)
990 strcpy(m_goto_buffer, itemname);
993 if (itemname == "") // this can be called by GameCommand
995 if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))
1000 itemname = "nexposee";
1003 if (itemname == "nexposee")
1005 // unlike 'togglemenu 1', this closes modal and root dialogs if opened
1006 m_activate_window(main.mainNexposee);
1012 for (e = NULL; (e = find(e, name, itemname)); )
1013 if (e.classname != "vtbl") break;
1015 if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
1018 e.hideMenuOnClose = true;
1020 m_activate_window(e);
1021 m_setpointerfocus(e);
1027 void m_play_focus_sound()
1029 static float menuLastFocusSoundTime;
1030 if (cvar("menu_sounds") < 2) return;
1031 if (time - menuLastFocusSoundTime <= 0.25) return;
1032 localsound(MENU_SOUND_FOCUS);
1033 menuLastFocusSoundTime = time;
1036 void m_play_click_sound(string soundfile)
1038 if (!cvar("menu_sounds")) return;
1039 localsound(soundfile);