Xonotic
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 
11 #include "xonotic/commandbutton.qh"
12 #include "xonotic/mainwindow.qh"
13 #include "xonotic/serverlist.qh"
15 
17 
18 #include "xonotic/util.qh"
19 
20 #include <common/items/_mod.qh>
21 #include <common/weapons/_all.qh>
22 #include <common/mapinfo.qh>
23 #include <common/mutators/base.qh>
24 
29 float menuAlpha;
35 
39 
40 void m_sync()
41 {
43  vidwidth_s = vidheight_s = vidpixelheight_s = 0; // Force updateConwidths on next draw
44 
46 }
47 
49 {
50  gamestatus = 0;
53  if (cvar("developer") > 0) gamestatus |= GAME_DEVELOPER;
54 }
55 
56 void m_init()
57 {
58  bool restarting = false;
59  cvar_set("_menu_alpha", "0");
60  prvm_language = cvar_string("prvm_language");
61  if (prvm_language == "")
62  {
63  prvm_language = "en";
64  cvar_set("prvm_language", prvm_language);
65  localcmd("\nmenu_restart\n");
66  restarting = true;
67  }
69  cvar_set("_menu_prvm_language", prvm_language);
70 
71 #ifdef WATERMARK
72  LOG_TRACEF("^4MQC Build information: ^1%s", WATERMARK);
73 #endif
74 
75  // list all game dirs (TEST)
76  if (cvar("developer") > 0)
77  {
78  for (int i = 0; ; ++i)
79  {
80  string s = getgamedirinfo(i, GETGAMEDIRINFO_NAME);
81  if (!s) break;
82  LOG_TRACE(s, ": ", getgamedirinfo(i, GETGAMEDIRINFO_DESCRIPTION));
83  }
84  }
85 
86  // needs to be done so early because of the constants they create
87  static_init();
90 
92 
93  float ddsload = cvar("r_texture_dds_load");
94  float texcomp = cvar("gl_texturecompression");
96  if (ddsload != cvar("r_texture_dds_load") || texcomp != cvar("gl_texturecompression")) localcmd("\nr_restart\n");
97 
98  if (!restarting)
99  {
100  if (cvar("_menu_initialized")) // always show menu after menu_restart
101  m_display();
102  else m_hide();
103  cvar_set("_menu_initialized", "1");
104  }
105 }
106 
107 const float MENU_ASPECT = 1280 / 1024;
108 
110 {
112 }
114 {
116 }
117 
118 void UpdateConWidthHeight(float w, float h, float p)
119 {
120  if (w != vidwidth_s || h != vidheight_s || p != vidpixelheight_s)
121  {
122  if (updateConwidths(w, h, p)) localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg")));
123  vidwidth_s = w;
124  vidheight_s = h;
125  vidpixelheight_s = p;
126  }
129  realconwidth = cvar("vid_conwidth");
130  realconheight = cvar("vid_conheight");
132  {
133  // widescreen
136  }
137  else
138  {
139  // squarescreen
142  }
143  if (main)
144  {
146  {
148  main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
149  }
150  }
151  else
152  {
153  vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame
154  }
155 }
156 
159 {
161 
162  menuInitialized = false;
163  if (!preMenuInit()) return;
164  menuInitialized = true;
165 
166  int fh = -1;
167  if (cvar_string("menu_skin") != "")
168  {
169  draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
170  fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
171  }
172  if (fh < 0 && cvar_defstring("menu_skin") != "")
173  {
174  cvar_set("menu_skin", cvar_defstring("menu_skin"));
175  draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
176  fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
177  }
178  if (fh < 0)
179  {
180  draw_currentSkin = "gfx/menu/default";
181  fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
182  }
183  if (fh < 0) error("cannot load any menu skin\n");
185  for (string s; (s = fgets(fh)); )
186  {
187  // these two are handled by skinlist.qc
188  if (substring(s, 0, 6) == "title ") continue;
189  if (substring(s, 0, 7) == "author ") continue;
190  int n = tokenize_console(s);
191  if (n < 2) continue;
192  Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
193  }
194  fclose(fh);
195 
196  int glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true);
197  if (glob >= 0)
198  {
199  for (int i = 0, n = search_getsize(glob); i < n; ++i)
201  search_end(glob);
202  }
203 
204  draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
205 
206  anim = NEW(AnimHost);
207  main = NEW(MainWindow);
208  main.configureMainWindow(main);
209 
210  main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
211  main.focused = true;
212  menuShiftState = 0;
213  menuMousePos = '0.5 0.5 0';
214 
215  m_sync();
216 
217  if (m_goto_buffer)
218  {
221  }
222 
223  if (Menu_Active) m_display(); // delayed menu display
224 }
225 
226 void m_keyup(float key, float ascii)
227 {
228  if (!menuInitialized) return;
229  if (!Menu_Active) return;
231  main.keyUp(main, key, ascii, menuShiftState);
232  if (key >= K_MOUSE1 && key <= K_MOUSE3)
233  {
235  if (!mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
236  if (mouseButtonsPressed < 0)
237  {
239  LOG_TRACE("Warning: released an already released button");
240  }
241  }
242  if (key == K_ALT) menuShiftState &= ~S_ALT;
243  if (key == K_CTRL) menuShiftState &= ~S_CTRL;
244  if (key == K_SHIFT) menuShiftState &= ~S_SHIFT;
245 }
246 
247 void m_keydown(float key, float ascii)
248 {
249  if (!menuInitialized) return;
250  if (!Menu_Active) return;
251 
252  if (menuMouseMode && key >= K_MOUSE1 && key <= K_MOUSE3)
253  {
254  // detect a click outside of the game window
255  vector p = getmousepos();
256  if (p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight)
257  {
259  return;
260  }
261  }
262 
263  if (keyGrabber)
264  {
265  entity e = keyGrabber;
266  keyGrabber = NULL;
267  e.keyGrabbed(e, key, ascii);
268  }
269  else
270  {
272  if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3)
273  main.mousePress(main, menuMousePos);
274  if (!main.keyDown(main, key, ascii, menuShiftState))
275  {
276  // disable menu on unhandled ESC
277  if (key == K_ESCAPE)
278  if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
279  m_hide();
280  }
281  }
282  if (key >= K_MOUSE1 && key <= K_MOUSE3)
283  {
285  if (mouseButtonsPressed > 10)
286  {
287  mouseButtonsPressed = 10;
288  LOG_TRACE("Warning: pressed an already pressed button");
289  }
290  }
291  if (key == K_ALT) menuShiftState |= S_ALT;
292  if (key == K_CTRL) menuShiftState |= S_CTRL;
293  if (key == K_SHIFT) menuShiftState |= S_SHIFT;
294 }
295 
296 enum {
302 };
303 void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
304 {
305  vector sz = draw_PictureSize(img);
306  bool width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x);
307  vector isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y));
308  vector isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x));
309  vector isz;
310  switch (scalemode)
311  {
312  default:
313  case SCALEMODE_CROP:
314  isz = (width_is_larger ? isz_h : isz_w);
315  break;
316  case SCALEMODE_LETTERBOX:
317  isz = (width_is_larger ? isz_w : isz_h);
318  break;
319  case SCALEMODE_WIDTH:
320  isz = isz_w;
321  break;
322  case SCALEMODE_HEIGHT:
323  isz = isz_h;
324  break;
325  case SCALEMODE_STRETCH:
326  isz = '1 1 0';
327  break;
328  }
329  vector org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y));
330  draw_Picture(org, img, isz, '1 1 1', a);
331 }
332 
333 void drawBackground(string img, float a, string algn, float force1)
334 {
335  if (main.mainNexposee.ModalController_state == 0) return;
336  vector v = '0 0 0';
337  int scalemode = SCALEMODE_CROP;
338  int len = strlen(algn);
339  for (int i = 0, l = 0; i < len; ++i)
340  {
341  string c = substring(algn, i, 1);
342  switch (c)
343  {
344  case "c":
345  scalemode = SCALEMODE_CROP;
346  goto nopic;
347  case "l":
348  scalemode = SCALEMODE_LETTERBOX;
349  goto nopic;
350  case "h":
351  scalemode = SCALEMODE_HEIGHT;
352  goto nopic;
353  case "w":
354  scalemode = SCALEMODE_WIDTH;
355  goto nopic;
356  case "s":
357  scalemode = SCALEMODE_STRETCH;
358  goto nopic;
359  case "1": case "4": case "7":
360  v.x = 0.0;
361  break;
362  case "2": case "5": case "8":
363  v.x = 0.5;
364  break;
365  case "3": case "6": case "9":
366  v.x = 1.0;
367  break;
368  default:
369  v.x = random();
370  break;
371  }
372  switch (c)
373  {
374  case "7": case "8": case "9":
375  v.y = 0.0;
376  break;
377  case "4": case "5": case "6":
378  v.y = 0.5;
379  break;
380  case "1": case "2": case "3":
381  v.y = 1.0;
382  break;
383  default:
384  v.y = random();
385  break;
386  }
387  if (l == 0)
388  {
389  draw_Picture_Aligned(v, scalemode, img, a);
390  }
391  else if (force1)
392  {
393  // force all secondary layers to use alpha 1. Prevents ugly issues
394  // with overlap. It's a flag because it cannot be used for the
395  // ingame background
396  draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), 1);
397  }
398  else
399  {
400  draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), a);
401  }
402  ++l;
403 LABEL(nopic)
404  }
405 }
406 
415 int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
417 {
418  return !(
419  (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x)
420  && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y)
421  );
422 }
423 bool m_testtooltipbox(vector tooltippos)
424 {
425  if (tooltippos.x < 0) return false;
426  if (tooltippos.y < 0) return false;
427  if (tooltippos.x + menuTooltipSize.x > 1) return false;
428  if (tooltippos.y + menuTooltipSize.y > 1) return false;
429  menuTooltipOrigin = tooltippos;
430  return true;
431 }
433 {
434  vector avoidplus;
435  avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
436  avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
437  avoidplus.z = 0;
438 
439  vector avoidminus;
440  avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x;
441  avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y;
442  avoidminus.z = 0;
443 
444  // bottom right
445  vector v = pos + avoidplus;
446  if (m_testtooltipbox(v)) return true;
447 
448  // bottom center
449  v.x = pos.x - menuTooltipSize.x * 0.5;
450  if (m_testtooltipbox(v)) return true;
451 
452  // bottom left
453  v.x = pos.x - avoidminus.x;
454  if (m_testtooltipbox(v)) return true;
455 
456  // top left
457  v.y = pos.y - avoidminus.y;
458  if (m_testtooltipbox(v)) return true;
459 
460  // top center
461  v.x = pos.x - menuTooltipSize.x * 0.5;
462  if (m_testtooltipbox(v)) return true;
463 
464  // top right
465  v.x = pos.x + avoidplus.x;
466  if (m_testtooltipbox(v)) return true;
467 
468  return false;
469 }
471 {
472  entity best = NULL;
473  for (entity it = root; it.instanceOfContainer; )
474  {
475  while (it.instanceOfNexposee && it.focusedChild)
476  {
477  it = it.focusedChild;
478  pos = globalToBox(pos, it.Container_origin, it.Container_size);
479  }
480  if (it.instanceOfNexposee)
481  {
482  it = it.itemFromPoint(it, pos);
483  if (it.tooltip) best = it;
484  else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
485  it = NULL;
486  }
487  else if (it.instanceOfModalController)
488  {
489  it = it.focusedChild;
490  }
491  else
492  {
493  it = it.itemFromPoint(it, pos);
494  }
495  if (!it) break;
496  if (it.tooltip) best = it;
497  else if (menu_tooltips == 2 && (it.controlledCvar || it.onClickCommand)) best = it;
498  pos = globalToBox(pos, it.Container_origin, it.Container_size);
499  }
500 
501  return best;
502 }
503 string gettooltip()
504 {
505  if (menu_tooltips == 2)
506  {
507  string s;
508  if (menuTooltipItem.controlledCvar)
509  {
510  string cvar_list = getCvarsMulti(menuTooltipItem);
511  if (cvar_list)
512  cvar_list = strcat(menuTooltipItem.controlledCvar, " ", cvar_list);
513  else
514  cvar_list = menuTooltipItem.controlledCvar;
515  s = strcat("[", cvar_list, " \"", cvar_string(menuTooltipItem.controlledCvar), "\"]");
516  }
517  else if (menuTooltipItem.onClickCommand)
518  {
519  s = strcat("<", menuTooltipItem.onClickCommand, ">");
520  }
521  else
522  {
523  return menuTooltipItem.tooltip;
524  }
525  if (menuTooltipItem.tooltip) return strcat(menuTooltipItem.tooltip, " ", s);
526  return s;
527  }
528  return menuTooltipItem.tooltip;
529 }
530 void m_tooltip(vector pos)
531 {
532  static string prev_tooltip;
533  entity it;
534  menu_tooltips = cvar("menu_tooltips");
535  if (!menu_tooltips)
536  {
537  // don't return immediately, fade out the active tooltip first
538  if (menuTooltipItem == NULL) return;
539  it = NULL;
541  }
542  else
543  {
544  float f = bound(0, frametime * 2, 1);
546  if (vdist(pos - menuTooltipAveragedMousePos, <, 0.01))
547  {
548  it = m_findtooltipitem(main, pos);
549 
550  if (it.instanceOfListBox && it.isScrolling(it)) it = NULL;
551 
552  if (it && prev_tooltip != it.tooltip)
553  {
554  // fade out if tooltip of a certain item has changed
555  menuTooltipState = 3;
556  strcpy(prev_tooltip, it.tooltip);
557  }
558  else if (menuTooltipItem && !m_testmousetooltipbox(pos))
559  {
560  menuTooltipState = 3; // fade out if mouse touches it
561  }
562  }
563  else
564  {
565  it = NULL;
566  }
567  }
568  vector fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
569 
570  // float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out
571  if (it != menuTooltipItem)
572  {
573  switch (menuTooltipState)
574  {
575  case 0:
576  if (menuTooltipItem)
577  {
578  // another item: fade out first
579  menuTooltipState = 2;
580  }
581  else
582  {
583  // new item: fade in
584  menuTooltipState = 1;
585  menuTooltipItem = it;
586 
587  menuTooltipOrigin.x = -1; // unallocated
588 
590 
591  int i = 0;
592  float w = 0;
594  {
595  string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
596  if (i == 16)
597  s = "...";
598  float f = draw_TextWidth(s, false, fontsize);
599  if (f > w) w = f;
600  }
601  menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
602  menuTooltipSize.y = i * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
603  menuTooltipSize.z = 0;
604  }
605  break;
606  case 1:
607  // changing item while fading in: fade out first
608  menuTooltipState = 2;
609  break;
610  case 2:
611  // changing item while fading out: can't
612  break;
613  }
614  }
615  else if (menuTooltipState == 2) // re-fade in?
616  {
617  menuTooltipState = 1;
618  }
619 
620  switch (menuTooltipState)
621  {
622  case 1: // fade in
624  if (menuTooltipAlpha == 1) menuTooltipState = 0;
625  break;
626  case 2: // fade out
627  case 3: // forced fade out
629  if (menuTooltipAlpha == 0)
630  {
631  menuTooltipState = 0;
633  }
634  break;
635  }
636 
637  if (menuTooltipItem == NULL)
638  {
640  return;
641  }
642  else
643  {
645  {
646  if (menu_tooltips != 0 && menu_tooltips_old != 0) menuTooltipItem = NULL; // reload tooltip next frame
648  }
649  else if (menuTooltipOrigin.x < 0) // unallocated?
650  {
652  }
653  if (menuTooltipOrigin.x >= 0)
654  {
655  // draw the tooltip!
656  vector p = SKINBORDER_TOOLTIP;
657  p.x *= 1 / conwidth;
658  p.y *= 1 / conheight;
660  p = menuTooltipOrigin;
661  p.x += SKINMARGIN_TOOLTIP_x / conwidth;
662  p.y += SKINMARGIN_TOOLTIP_y / conheight;
663  int i = 0;
664  for (getWrappedLine_remaining = menuTooltipText; getWrappedLine_remaining && i <= 16; ++i, p.y += fontsize.y)
665  {
666  string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
667  if (i == 16)
668  s = "...";
669  draw_Text(p, s, fontsize, SKINCOLOR_TOOLTIP, SKINALPHA_TOOLTIP * menuTooltipAlpha, false);
670  }
671  }
672  }
673 }
674 
676 void m_draw(float width, float height)
677 {
679  {
680  static float connected_time;
681  if (clientstate() == CS_DISCONNECTED)
682  {
683  if (connected_time && time - connected_time > autocvar_menu_force_on_disconnection)
684  {
685  m_toggle(true);
686  connected_time = 0;
687  }
688  }
689  else
690  connected_time = time;
691  }
692 
693  m_gamestatus();
694 
696 
697  menuMouseMode = cvar("menu_mouse_absolute");
698 
699  if (anim) anim.tickAll(anim);
700 
701  UpdateConWidthHeight(width, height, cvar("vid_pixelheight"));
702 
703  if (!menuInitialized)
704  {
705  // TODO draw an info image about this situation
706  m_init_delayed();
707  return;
708  }
710  {
711  menuNotTheFirstFrame = true;
712  if (Menu_Active && !cvar("menu_video_played"))
713  {
714  localcmd("cd loop $menu_cdtrack\n");
715  // TODO: use this when we have a welcome sound
716  //localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n");
717  menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME
718  }
719  // ALWAYS set this cvar; if we start but menu is not active, this means we want no background music!
720  localcmd("set menu_video_played 1\n");
721  }
722 
723  float t = gettime();
724  float realFrametime = frametime = min(0.2, t - menuPrevTime);
725  menuPrevTime = t;
726  time += frametime;
727 
728  t = cvar("menu_slowmo");
729  if (t)
730  {
731  frametime *= t;
732  realFrametime *= t;
733  }
734  else
735  {
736  t = 1;
737  }
738 
739  if (Menu_Active)
740  {
744  else m_hide();
745  }
746 
747  if (cvar("cl_capturevideo")) frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
748 
750  if (Menu_Active)
751  {
752  if (menuAlpha == 0 && menuLogoAlpha < 2)
753  {
754  menuLogoAlpha += 2 * frametime;
755  }
756  else
757  {
758  menuAlpha = min(1, menuAlpha + 5 * frametime);
759  menuLogoAlpha = 2;
760  }
761  }
762  else
763  {
764  menuAlpha = max(0, menuAlpha - 5 * frametime);
765  menuLogoAlpha = 2;
766  }
767 
769 
771  {
772  if (menuLogoAlpha > 0)
773  {
774  draw_reset_full();
775  draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
776  drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true);
778  if (menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
779  {
780  draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
782  draw_alpha = 1;
783  }
784  }
785  }
786  else if (SKINALPHA_BACKGROUND_INGAME)
787  {
788  if (menuAlpha > 0)
789  {
790  draw_reset_full();
791  drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME,
792  SKINALIGN_BACKGROUND_INGAME, false);
794  }
795  }
796 
797  if (menuAlpha != prevMenuAlpha) cvar_set("_menu_alpha", ftos(menuAlpha));
798 
800  preMenuDraw();
802 
803  if (menuAlpha <= 0)
804  {
805  if (prevMenuAlpha > 0) main.initializeDialog(main, main.firstChild);
807  postMenuDraw();
808  return;
809  }
810 
812 
813  if (menuMouseMode)
814  {
815  vector rawMousePos = getmousepos();
816  vector newMouse = globalToBox(rawMousePos, draw_shift, draw_scale);
817  if (rawMousePos != '0 0 0' && newMouse != menuMousePos)
818  {
819  menuMousePos = newMouse;
820  if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
821  else main.mouseMove(main, menuMousePos);
822  }
823  }
824  else if (frametime > 0)
825  {
826  vector dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
827  if (dMouse != '0 0 0')
828  {
829  vector minpos = globalToBox('0 0 0', draw_shift, draw_scale);
830  vector maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale);
831  dMouse = globalToBoxSize(dMouse, draw_scale);
832  menuMousePos += dMouse * cvar("menu_mouse_speed");
833  menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x);
834  menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y);
835  if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos);
836  else main.mouseMove(main, menuMousePos);
837  }
838  }
839  main.draw(main);
840 
842 
843  draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
844 
846 
848  postMenuDraw();
849 
850  frametime = 0;
851  IL_ENDFRAME();
852 }
853 
854 void m_display()
855 {
856  Menu_Active = true;
859 
860  if (!menuInitialized) return;
861 
862  if (mouseButtonsPressed) main.mouseRelease(main, menuMousePos);
864 
865  main.focusEnter(main);
866  main.showNotify(main);
867 }
868 
869 void m_hide()
870 {
871  Menu_Active = false;
874 
875  if (!menuInitialized) return;
876 
877  main.focusLeave(main);
878  main.hideNotify(main);
879 }
880 
881 void m_toggle(int mode)
882 {
883  if (Menu_Active)
884  {
885  if (mode == 1) return;
886  m_hide();
887  }
888  else
889  {
890  if (mode == 0) return;
891  m_display();
892  }
893 }
894 
895 void Shutdown()
896 {
897  m_hide();
898  FOREACH_ENTITY_ORDERED(it.destroy, {
899  if (it.classname == "vtbl") continue;
900  it.destroy(it);
901  });
902 }
903 
904 void m_focus_item_chain(entity outermost, entity innermost)
905 {
906  if (innermost.parent != outermost) m_focus_item_chain(outermost, innermost.parent);
907  innermost.parent.setFocus(innermost.parent, innermost);
908 }
909 
911 {
912  entity par = wnd.parent;
913  if (par) m_activate_window(par);
914 
915  if (par.instanceOfModalController)
916  {
917  if (wnd.tabSelectingButton)
918  // tabs
919  TabButton_Click(wnd.tabSelectingButton, wnd);
920  else
921  // root
922  par.initializeDialog(par, wnd);
923  }
924  else if (par.instanceOfNexposee)
925  {
926  // nexposee (sorry for violating abstraction here)
927  par.selectedChild = wnd;
928  par.animationState = 1;
929  Container_setFocus(par, NULL);
930  }
931  else if (par.instanceOfContainer)
932  {
933  // other containers
934  if (par.focused) par.setFocus(par, wnd);
935  }
936 }
937 
939 {
940  if (!wnd.instanceOfContainer) return;
941  entity focus = wnd.preferredFocusedGrandChild(wnd);
942  if (!focus) return;
943  menuMousePos = focus.origin + 0.5 * focus.size;
944  menuMousePos.x *= 1 / conwidth;
945  menuMousePos.y *= 1 / conheight;
946  entity par = wnd.parent;
947  if (par.focused) par.setFocus(par, wnd);
948  if (wnd.focused) m_focus_item_chain(wnd, focus);
949 }
950 
951 void m_goto(string itemname)
952 {
953  if (!menuInitialized)
954  {
955  strcpy(m_goto_buffer, itemname);
956  return;
957  }
958  if (itemname == "") // this can be called by GameCommand
959  {
961  {
962  m_hide();
963  }
964  else
965  {
966  m_activate_window(main.mainNexposee);
967  m_display();
968  }
969  }
970  else
971  {
972  entity e;
973  for (e = NULL; (e = find(e, name, itemname)); )
974  if (e.classname != "vtbl") break;
975 
976  if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED))))
977  {
978  if(!Menu_Active)
979  e.hideMenuOnClose = true;
980  m_hide();
983  m_display();
984  }
985  }
986 }
987 
989 {
990  static float menuLastFocusSoundTime;
991  if (cvar("menu_sounds") < 2) return;
992  if (time - menuLastFocusSoundTime <= 0.25) return;
994  menuLastFocusSoundTime = time;
995 }
996 
997 void m_play_click_sound(string soundfile)
998 {
999  if (!cvar("menu_sounds")) return;
1000  localsound(soundfile);
1001 }
float K_ALT
Definition: keycodes.qc:20
float K_ESCAPE
Definition: keycodes.qc:9
const vector eY
Definition: vector.qh:45
string getWrappedLine_remaining
Definition: util.qh:108
#define NEW(cname,...)
Definition: oo.qh:105
void RegisterSLCategories()
Definition: serverlist.qc:44
string draw_currentSkin
Definition: util.qh:45
const int S_SHIFT
Definition: hud.qh:127
float K_MOUSE3
Definition: keycodes.qc:131
entity() spawn
#define static_init()
Definition: static.qh:33
#define static_init_precache()
Definition: static.qh:43
const float FILE_READ
Definition: csprogsdefs.qc:231
float K_SHIFT
Definition: keycodes.qc:22
const int S_ALT
Definition: hud.qh:129
#define FOREACH_ENTITY_ORDERED(cond, body)
Definition: iter.qh:138
#define strcpy(this, s)
Definition: string.qh:49
#define argv_end_index
Definition: dpextensions.qh:30
void Container_setFocus(entity me, entity other)
Definition: container.qc:289
ERASEABLE void IL_ENDFRAME()
#define argv_start_index
Definition: dpextensions.qh:27
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
float height
Definition: jumppads.qh:12
float K_CTRL
Definition: keycodes.qc:21
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
#define LOG_TRACEF(...)
Definition: log.qh:82
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define tokenize_console
Definition: dpextensions.qh:24
float K_MOUSE1
Definition: keycodes.qc:129
vector v
Definition: ent_cs.qc:116
string prvm_language
Definition: i18n.qh:8
const vector eX
Definition: vector.qh:44
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
#define LOG_TRACE(...)
Definition: log.qh:81
void TabButton_Click(entity button, entity tab)
#define LABEL(id)
Definition: compiler.qh:36
#define strfree(this)
Definition: string.qh:56
void execute_next_frame()
Definition: util.qc:1612
best
Definition: all.qh:77
const int S_CTRL
Definition: hud.qh:128
string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition: util.qc:880
float time
Definition: csprogsdefs.qc:16
#define static_init_late()
Definition: static.qh:38