]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/menu/menu.qc
restart menu when having turned "" to "en" to make menu.dat.en.po apply
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / menu.qc
1 ///////////////////////////////////////////////
2 // Menu Source File
3 ///////////////////////
4 // This file belongs to dpmod/darkplaces
5 // AK contains all menu functions (especially the required ones)
6 ///////////////////////////////////////////////
7
8 float mouseButtonsPressed;
9 vector menuMousePos;
10 float menuShiftState;
11 float menuPrevTime;
12 float menuAlpha;
13 float menuLogoAlpha;
14 float prevMenuAlpha;
15 float menuInitialized;
16 float menuNotTheFirstFrame;
17 float menuMouseMode;
18
19 void SUB_Null() { }
20
21 void m_sync()
22 {
23         updateCompression();
24         updateConwidths();
25
26         loadAllCvars(main);
27 }
28
29 void m_init()
30 {
31         cvar_set("_menu_alpha", "0");
32         prvm_language = cvar_string("prvm_language");
33         if(prvm_language == "")
34         {
35                 prvm_language = "en";
36                 cvar_set("prvm_language", prvm_language);
37                 localcmd("\nmenu_restart; togglemenu\n");
38         }
39         prvm_language = strzone(prvm_language);
40         cvar_set("_menu_prvm_language", prvm_language);
41
42         check_unacceptable_compiler_bugs();
43
44 #ifdef WATERMARK
45         print(sprintf(_("^4MQC Build information: ^1%s\n"), WATERMARK()));
46 #endif
47
48         // list all game dirs (TEST)
49         if(cvar("developer"))
50         {
51                 float i;
52                 string s;
53                 for(i = 0; ; ++i)
54                 {
55                         s = getgamedirinfo(i, GETGAMEDIRINFO_NAME);
56                         if not(s)
57                                 break;
58                         dprint(s, ": ", getgamedirinfo(i, GETGAMEDIRINFO_DESCRIPTION));
59                 }
60         }
61
62         // needs to be done so early because of the constants they create
63         RegisterWeapons();
64         RegisterGametypes();
65
66         float ddsload = cvar("r_texture_dds_load");
67         float texcomp = cvar("gl_texturecompression");
68         updateCompression();
69         if(ddsload != cvar("r_texture_dds_load") || texcomp != cvar("gl_texturecompression"))
70                 localcmd("\nr_restart\n");
71         initConwidths();
72 }
73
74 float MENU_ASPECT = 1.25; // 1280x1024
75 float MENU_MINHEIGHT = 600;
76 float conwidth_s, conheight_s, realconwidth, realconheight, screenconwidth, screenconheight;
77 void draw_reset_cropped()
78 {
79         draw_reset(screenconwidth, screenconheight, 0.5 * (realconwidth - screenconwidth), 0.5 * (realconheight - screenconheight));
80 }
81 void draw_reset_full()
82 {
83         draw_reset(realconwidth, realconheight, 0, 0);
84 }
85 void UpdateConWidthHeight()
86 {
87         conwidth_s = conwidth;
88         conheight_s = conheight;
89         realconwidth = cvar("vid_conwidth");
90         realconheight = cvar("vid_conheight");
91         if(realconwidth / realconheight > MENU_ASPECT)
92         {
93                 // widescreen
94                 conwidth = realconheight * MENU_ASPECT;
95                 conheight = realconheight;
96         }
97         else
98         {
99                 // squarescreen
100                 conwidth = realconwidth;
101                 conheight = realconwidth / MENU_ASPECT;
102         }
103         screenconwidth = conwidth;
104         screenconheight = conheight;
105         if(conwidth < MENU_MINHEIGHT * MENU_ASPECT)
106         {
107                 conheight *= MENU_MINHEIGHT * MENU_ASPECT / conwidth;
108                 conwidth = MENU_MINHEIGHT * MENU_ASPECT;
109         }
110         if(conheight < MENU_MINHEIGHT)
111         {
112                 conwidth *= MENU_MINHEIGHT / conheight;
113                 conheight = MENU_MINHEIGHT;
114         }
115         if(main)
116         {
117                 if(conwidth_s != conwidth || conheight_s != conheight)
118                 {
119                         draw_reset_cropped();
120                         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
121                 }
122         }
123 }
124
125 void m_init_delayed()
126 {
127         float fh, glob, n, i;
128         string s;
129
130         conwidth = conheight = -1;
131         UpdateConWidthHeight();
132         draw_reset_cropped();
133
134         menuInitialized = 0;
135         if(!preMenuInit())
136                 return;
137         menuInitialized = 1;
138
139         fh = -1;
140         if(cvar_string("menu_skin") != "")
141         {
142                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
143                 fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ);
144         }
145         if(fh < 0)
146         if(cvar_defstring("menu_skin") != "")
147         {
148                 cvar_set("menu_skin", cvar_defstring("menu_skin"));
149                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
150                 fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ);
151         }
152         if(fh < 0)
153         {
154                 draw_currentSkin = "gfx/menu/default";
155                 fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ);
156         }
157         if(fh < 0)
158         {
159                 error("cannot load any menu skin\n");
160         }
161         draw_currentSkin = strzone(draw_currentSkin);
162         while((s = fgets(fh)))
163         {
164                 // these two are handled by skinlist.qc
165                 if(substring(s, 0, 6) == "title ")
166                         continue;
167                 if(substring(s, 0, 7) == "author ")
168                         continue;
169                 n = tokenize_console(s);
170                 if(n >= 2)
171                         Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
172         }
173         fclose(fh);
174
175         glob = search_begin(strcat(draw_currentSkin, "/*.tga"), TRUE, TRUE);
176         if(glob >= 0)
177         {
178                 n = search_getsize(glob);
179                 for(i = 0; i < n; ++i)
180                         precache_pic(search_getfilename(glob, i));
181                 search_end(glob);
182         }
183
184         draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
185
186         loadTooltips();
187         anim = spawnAnimHost();
188         main = spawnMainWindow(); main.configureMainWindow(main);
189         unloadTooltips();
190
191         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
192         main.focused = 1;
193         menuShiftState = 0;
194         menuMousePos = '0.5 0.5 0';
195
196         m_sync();
197
198         if(Menu_Active)
199                 m_display(); // delayed menu display
200 }
201
202 void m_keyup (float key, float ascii)
203 {
204         if(!menuInitialized)
205                 return;
206         if(!Menu_Active)
207                 return;
208         draw_reset_cropped();
209         main.keyUp(main, key, ascii, menuShiftState);
210         if(key >= K_MOUSE1 && key <= K_MOUSE3)
211         {
212                 --mouseButtonsPressed;
213                 if(!mouseButtonsPressed)
214                         main.mouseRelease(main, menuMousePos);
215                 if(mouseButtonsPressed < 0)
216                 {
217                         mouseButtonsPressed = 0;
218                         dprint("Warning: released an already released button\n");
219                 }
220         }
221         if(key == K_ALT) menuShiftState -= (menuShiftState & S_ALT);
222         if(key == K_CTRL) menuShiftState -= (menuShiftState & S_CTRL);
223         if(key == K_SHIFT) menuShiftState -= (menuShiftState & S_SHIFT);
224 }
225
226 void m_keydown(float key, float ascii)
227 {
228         if(!menuInitialized)
229                 return;
230         if(!Menu_Active)
231                 return;
232         if(keyGrabber)
233         {
234                 entity e;
235                 e = keyGrabber;
236                 keyGrabber = NULL;
237                 e.keyGrabbed(e, key, ascii);
238         }
239         else
240         {
241                 draw_reset_cropped();
242                 if(key >= K_MOUSE1 && key <= K_MOUSE3)
243                         if(!mouseButtonsPressed)
244                                 main.mousePress(main, menuMousePos);
245                 if(!main.keyDown(main, key, ascii, menuShiftState))
246                         if(key == K_ESCAPE)
247                                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
248                                         m_hide(); // disable menu on unhandled ESC
249         }
250         if(key >= K_MOUSE1 && key <= K_MOUSE3)
251         {
252                 ++mouseButtonsPressed;
253                 if(mouseButtonsPressed > 10)
254                 {
255                         mouseButtonsPressed = 10;
256                         dprint("Warning: pressed an already pressed button\n");
257                 }
258         }
259         if(key == K_ALT) menuShiftState |= S_ALT;
260         if(key == K_CTRL) menuShiftState |= S_CTRL;
261         if(key == K_SHIFT) menuShiftState |= S_SHIFT;
262 }
263
264 float SCALEMODE_CROP = 0;
265 float SCALEMODE_LETTERBOX = 1;
266 float SCALEMODE_WIDTH = 2;
267 float SCALEMODE_HEIGHT = 3;
268 float SCALEMODE_STRETCH = 4;
269 void draw_Picture_Aligned(vector algn, float scalemode, string img, float a)
270 {
271         vector sz, org, isz, isz_w, isz_h;
272         float width_is_larger;
273
274         sz = draw_PictureSize(img);
275         width_is_larger = (sz_x * draw_scale_y >= sz_y * draw_scale_x);
276         isz_w = '1 0 0' + '0 1 0' * ((sz_y / sz_x) * (draw_scale_x / draw_scale_y));
277         isz_h = '0 1 0' + '1 0 0' * ((sz_x / sz_y) * (draw_scale_y / draw_scale_x));
278
279         switch(scalemode)
280         {
281                 default:
282                 case SCALEMODE_CROP:
283                         isz = (width_is_larger ? isz_h : isz_w);
284                         break;
285                 case SCALEMODE_LETTERBOX:
286                         isz = (width_is_larger ? isz_w : isz_h);
287                         break;
288                 case SCALEMODE_WIDTH:
289                         isz = isz_w;
290                         break;
291                 case SCALEMODE_HEIGHT:
292                         isz = isz_h;
293                         break;
294                 case SCALEMODE_STRETCH:
295                         isz = '1 1 0';
296                         break;
297         }
298
299         org = eX * (algn_x * (1 - isz_x)) + eY * (algn_y * (1 - isz_y));
300         draw_Picture(org, img, isz, '1 1 1', a);
301 }
302
303 void drawBackground(string img, float a, string algn, float force1)
304 {
305         if(main.mainNexposee.ModalController_state == 0)
306                 return;
307
308         vector v;
309         float i, l;
310         string c;
311         float scalemode;
312
313         v_z = 0;
314
315         scalemode = SCALEMODE_CROP;
316
317         for(i = 0; i < strlen(algn); ++i)
318         {
319                 c = substring(algn, i, 1);
320                 switch(c)
321                 {
322                         case "c": scalemode = SCALEMODE_CROP; goto nopic;
323                         case "l": scalemode = SCALEMODE_LETTERBOX; goto nopic;
324                         case "h": scalemode = SCALEMODE_HEIGHT; goto nopic;
325                         case "w": scalemode = SCALEMODE_WIDTH; goto nopic;
326                         case "s": scalemode = SCALEMODE_STRETCH; goto nopic;
327                         case "1": case "4": case "7": v_x = 0.0; break;
328                         case "2": case "5": case "8": v_x = 0.5; break;
329                         case "3": case "6": case "9": v_x = 1.0; break;
330                         default: v_x = random(); break;
331                 }
332                 switch(c)
333                 {
334                         case "7": case "8": case "9": v_y = 0.0; break;
335                         case "4": case "5": case "6": v_y = 0.5; break;
336                         case "1": case "2": case "3": v_y = 1.0; break;
337                         default: v_y = random(); break;
338                 }
339                 if(l == 0)
340                         draw_Picture_Aligned(v, scalemode, img, a);
341                 else if(force1)
342                         // force all secondary layers to use alpha 1. Prevents ugly issues
343                         // with overlap. It's a flag because it cannot be used for the
344                         // ingame background
345                         draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l+1)), 1);
346                 else
347                         draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l+1)), a);
348                 ++l;
349 :nopic
350         }
351 }
352
353 float menu_tooltips;
354 float menu_tooltips_old;
355 vector menuTooltipAveragedMousePos;
356 entity menuTooltipItem;
357 vector menuTooltipOrigin;
358 vector menuTooltipSize;
359 float menuTooltipAlpha;
360 string menuTooltipText;
361 float menuTooltipState; // 0: static, 1: fading in, 2: fading out
362 float m_testmousetooltipbox(vector pos)
363 {
364         if(pos_x >= menuTooltipOrigin_x && pos_x < menuTooltipOrigin_x + menuTooltipSize_x)
365         if(pos_y >= menuTooltipOrigin_y && pos_y < menuTooltipOrigin_y + menuTooltipSize_y)
366                 return FALSE;
367         return TRUE;
368 }
369 float m_testtooltipbox(vector tooltippos)
370 {
371         if(tooltippos_x < 0)
372                 return FALSE;
373         if(tooltippos_y < 0)
374                 return FALSE;
375         if(tooltippos_x + menuTooltipSize_x > 1)
376                 return FALSE;
377         if(tooltippos_y + menuTooltipSize_y > 1)
378                 return FALSE;
379         menuTooltipOrigin = tooltippos;
380         return TRUE;
381 }
382 float m_allocatetooltipbox(vector pos)
383 {
384         vector avoidplus, avoidminus;
385         vector v;
386
387         avoidplus_x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
388         avoidplus_y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
389         avoidplus_z = 0;
390
391         avoidminus_x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize_x;
392         avoidminus_y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize_y;
393         avoidminus_z = 0;
394
395         // bottom right
396         v = pos + avoidplus;
397         if(m_testtooltipbox(v))
398                 return TRUE;
399         
400         // bottom center
401         v_x = pos_x - menuTooltipSize_x * 0.5;
402         if(m_testtooltipbox(v))
403                 return TRUE;
404
405         // bottom left
406         v_x = pos_x - avoidminus_x;
407         if(m_testtooltipbox(v))
408                 return TRUE;
409
410         // top left
411         v_y = pos_y - avoidminus_y;
412         if(m_testtooltipbox(v))
413                 return TRUE;
414
415         // top center
416         v_x = pos_x - menuTooltipSize_x * 0.5;
417         if(m_testtooltipbox(v))
418                 return TRUE;
419         
420         // top right
421         v_x = pos_x + avoidplus_x;
422         if(m_testtooltipbox(v))
423                 return TRUE;
424         
425         return FALSE;
426 }
427 entity m_findtooltipitem(entity root, vector pos)
428 {
429         entity it;
430         entity best;
431
432         best = world;
433         it = root;
434
435         while(it.instanceOfContainer)
436         {
437                 while(it.instanceOfNexposee && it.focusedChild)
438                 {
439                         it = it.focusedChild;
440                         pos = globalToBox(pos, it.Container_origin, it.Container_size);
441                 }
442                 if(it.instanceOfNexposee)
443                 {
444                         it = it.itemFromPoint(it, pos);
445                         if(it.tooltip)
446                                 best = it;
447                         else if(menu_tooltips == 2 && (it.cvarName || it.onClickCommand))
448                                 best = it;
449                         it = world;
450                 }
451                 else if(it.instanceOfModalController)
452                         it = it.focusedChild;
453                 else
454                         it = it.itemFromPoint(it, pos);
455                 if(!it)
456                         break;
457                 if(it.tooltip)
458                         best = it;
459                 else if(menu_tooltips == 2 && (it.cvarName || it.onClickCommand))
460                         best = it;
461                 pos = globalToBox(pos, it.Container_origin, it.Container_size);
462         }
463
464         return best;
465 }
466 string gettooltip()
467 {
468         if (menu_tooltips == 2)
469         {
470                 string s;
471                 if (menuTooltipItem.cvarName)
472                 {
473                         if (getCvarsMulti(menuTooltipItem))
474                                 s = strcat("[", menuTooltipItem.cvarName, " ", getCvarsMulti(menuTooltipItem), "]");
475                         else
476                                 s = strcat("[", menuTooltipItem.cvarName, "]");
477                 }
478                 else if (menuTooltipItem.onClickCommand)
479                         s = strcat("<", menuTooltipItem.onClickCommand, ">");
480                 else
481                         return menuTooltipItem.tooltip;
482                 if (menuTooltipItem.tooltip)
483                         return strcat(menuTooltipItem.tooltip, " ", s);
484                 return s;
485         }
486         return menuTooltipItem.tooltip;
487 }
488 void m_tooltip(vector pos)
489 {
490         float f, i, w;
491         entity it;
492         vector fontsize, p;
493         string s;
494
495         menu_tooltips = cvar("menu_tooltips");
496         if (!menu_tooltips)
497         {
498                 // don't return immediately, fade out the active tooltip first
499                 if (menuTooltipItem == world)
500                         return;
501                 it = world;
502                 menu_tooltips_old = menu_tooltips;
503         }
504         else
505         {
506                 f = bound(0, frametime * 2, 1);
507                 menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f;
508                 f = vlen(pos - menuTooltipAveragedMousePos);
509                 if(f < 0.01)
510                         it = m_findtooltipitem(main, pos);
511                 else
512                         it = world;
513         }
514         fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
515
516         // float menuTooltipState; // 0: static, 1: fading in, 2: fading out
517         if(it != menuTooltipItem)
518         {
519                 switch(menuTooltipState)
520                 {
521                         case 0:
522                                 if(menuTooltipItem)
523                                 {
524                                         // another item: fade out first
525                                         menuTooltipState = 2;
526                                 }
527                                 else
528                                 {
529                                         // new item: fade in
530                                         menuTooltipState = 1;
531                                         menuTooltipItem = it;
532
533                                         menuTooltipOrigin_x = -1; // unallocated
534
535                                         if (menuTooltipText)
536                                                 strunzone(menuTooltipText);
537                                         menuTooltipText = strzone(gettooltip());
538
539                                         i = 0;
540                                         w = 0;
541                                         getWrappedLine_remaining = menuTooltipText;
542                                         while(getWrappedLine_remaining)
543                                         {
544                                                 s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
545                                                 ++i;
546                                                 f = draw_TextWidth(s, FALSE, fontsize);
547                                                 if(f > w)
548                                                         w = f;
549                                         }
550                                         menuTooltipSize_x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
551                                         menuTooltipSize_y = i * fontsize_y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
552                                         menuTooltipSize_z = 0;
553                                 }
554                                 break;
555                         case 1:
556                                 // changing item while fading in: fade out first
557                                 menuTooltipState = 2;
558                                 break;
559                         case 2:
560                                 // changing item while fading out: can't
561                                 break;
562                 }
563         }
564         else if(menuTooltipState == 2) // re-fade in?
565                 menuTooltipState = 1;
566
567         if(menuTooltipItem)
568                 if(!m_testmousetooltipbox(pos))
569                         menuTooltipState = 2; // fade out if mouse touches it
570
571         switch(menuTooltipState)
572         {
573                 case 1:
574                         menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1);
575                         if(menuTooltipAlpha == 1)
576                                 menuTooltipState = 0;
577                         break;
578                 case 2:
579                         menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1);
580                         if(menuTooltipAlpha == 0)
581                         {
582                                 menuTooltipState = 0;
583                                 menuTooltipItem = world;
584                         }
585                         break;
586         }
587
588         if(menuTooltipItem == world)
589         {
590                 if (menuTooltipText)
591                 {
592                         strunzone(menuTooltipText);
593                         menuTooltipText = string_null;
594                 }
595                 return;
596         }
597         else
598         {
599                 if(menu_tooltips != menu_tooltips_old)
600                 {
601                         if (menu_tooltips != 0 && menu_tooltips_old != 0)
602                                 menuTooltipItem = world; // reload tooltip next frame
603                         menu_tooltips_old = menu_tooltips;
604                 }
605                 else if(menuTooltipOrigin_x < 0) // unallocated?
606                         m_allocatetooltipbox(pos);
607
608                 if(menuTooltipOrigin_x >= 0)
609                 {
610                         // draw the tooltip!
611                         p = SKINBORDER_TOOLTIP;
612                         p_x *= 1 / conwidth;
613                         p_y *= 1 / conheight;
614                         draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
615                         p = menuTooltipOrigin;
616                         p_x += SKINMARGIN_TOOLTIP_x / conwidth;
617                         p_y += SKINMARGIN_TOOLTIP_y / conheight;
618                         getWrappedLine_remaining = menuTooltipText;
619                         while(getWrappedLine_remaining)
620                         {
621                                 s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
622                                 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, FALSE);
623                                 p_y += fontsize_y;
624                         }
625                 }
626         }
627 }
628
629 void m_draw()
630 {
631         float t;
632         float realFrametime;
633
634         execute_next_frame();
635
636         menuMouseMode = cvar("menu_mouse_absolute");
637
638         if (anim)
639                 anim.tickAll(anim);
640
641         if(main)
642                 UpdateConWidthHeight();
643
644         if(!menuInitialized)
645         {
646                 // TODO draw an info image about this situation
647                 m_init_delayed();
648                 return;
649         }
650         if(!menuNotTheFirstFrame)
651         {
652                 menuNotTheFirstFrame = 1;
653                 if(Menu_Active)
654                 if(!cvar("menu_video_played"))
655                 {
656                         localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.ogg\n");
657                         menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME
658                 }
659                 // ALWAYS set this cvar; if we start but menu is not active, this means we want no background music!
660                 localcmd("set menu_video_played 1\n");
661         }
662
663         t = gettime();
664         realFrametime = frametime = min(0.2, t - menuPrevTime);
665         menuPrevTime = t;
666         time += frametime;
667
668         t = cvar("menu_slowmo");
669         if(t)
670         {
671                 frametime *= t;
672                 realFrametime *= t;
673         }
674         else
675                 t = 1;
676
677         if(Menu_Active)
678         {
679                 if(getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED))
680                         setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU);
681                 else
682                         m_hide();
683         }
684
685         if(cvar("cl_capturevideo"))
686                 frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
687
688         gamestatus = 0;
689         if(isserver())
690                 gamestatus = gamestatus | GAME_ISSERVER;
691         if(clientstate() == CS_CONNECTED)
692                 gamestatus = gamestatus | GAME_CONNECTED;
693         if(cvar("developer"))
694                 gamestatus = gamestatus | GAME_DEVELOPER;
695
696         prevMenuAlpha = menuAlpha;
697         if(Menu_Active)
698         {
699                 if(menuAlpha == 0 && menuLogoAlpha < 2)
700                 {
701                         menuLogoAlpha = menuLogoAlpha + frametime * 2;
702                 }
703                 else
704                 {
705                         menuAlpha = min(1, menuAlpha + frametime * 5);
706                         menuLogoAlpha = 2;
707                 }
708         }
709         else
710         {
711                 menuAlpha = max(0, menuAlpha - frametime * 5);
712                 menuLogoAlpha = 2;
713         }
714
715         draw_reset_cropped();
716
717         if(!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
718         {
719                 if(menuLogoAlpha > 0)
720                 {
721                         draw_reset_full();
722                         draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
723                         drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, TRUE);
724                         draw_reset_cropped();
725                         if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
726                         {
727                                 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
728                                 draw_drawMousePointer(menuMousePos);
729                                 draw_alpha = 1;
730                         }
731                 }
732         }
733         else if(SKINALPHA_BACKGROUND_INGAME)
734         {
735                 if(menuAlpha > 0)
736                 {
737                         draw_reset_full();
738                         drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, FALSE);
739                         draw_reset_cropped();
740                 }
741         }
742
743         if(menuAlpha != prevMenuAlpha)
744                 cvar_set("_menu_alpha", ftos(menuAlpha));
745
746         draw_reset_cropped();
747         preMenuDraw();
748         draw_reset_cropped();
749
750         if(menuAlpha <= 0)
751         {
752                 if(prevMenuAlpha > 0)
753                         main.initializeDialog(main, main.firstChild);
754                 draw_reset_cropped();
755                 postMenuDraw();
756                 return;
757         }
758
759         draw_alpha *= menuAlpha;
760
761         if(menuMouseMode)
762         {
763                 vector newMouse;
764                 newMouse = globalToBox(getmousepos(), draw_shift, draw_scale);
765                 if(newMouse != '0 0 0')
766                         if(newMouse != menuMousePos)
767                         {
768                                 menuMousePos = newMouse;
769                                 if(mouseButtonsPressed)
770                                         main.mouseDrag(main, menuMousePos);
771                                 else
772                                         main.mouseMove(main, menuMousePos);
773                         }
774         }
775         else
776         {
777                 if(frametime > 0)
778                 {
779                         vector dMouse, minpos, maxpos;
780                         dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
781                         if(dMouse != '0 0 0')
782                         {
783                                 minpos = globalToBox('0 0 0', draw_shift, draw_scale);
784                                 maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale);
785                                 dMouse = globalToBoxSize(dMouse, draw_scale);
786                                 menuMousePos += dMouse * cvar("menu_mouse_speed");
787                                 menuMousePos_x = bound(minpos_x, menuMousePos_x, maxpos_x);
788                                 menuMousePos_y = bound(minpos_y, menuMousePos_y, maxpos_y);
789                                 if(mouseButtonsPressed)
790                                         main.mouseDrag(main, menuMousePos);
791                                 else
792                                         main.mouseMove(main, menuMousePos);
793                         }
794                 }
795         }
796         main.draw(main);
797
798         m_tooltip(menuMousePos);
799
800         draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
801
802         draw_drawMousePointer(menuMousePos);
803
804         draw_reset_cropped();
805         postMenuDraw();
806
807         frametime = 0;
808 }
809
810 void m_display()
811 {
812         Menu_Active = true;
813         setkeydest(KEY_MENU);
814         setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU));
815
816         if(!menuInitialized)
817                 return;
818
819         if(mouseButtonsPressed)
820                 main.mouseRelease(main, menuMousePos);
821         mouseButtonsPressed = 0;
822
823         main.focusEnter(main);
824         main.showNotify(main);
825 }
826
827 void m_hide()
828 {
829         Menu_Active = false;
830         setkeydest(KEY_GAME);
831         setmousetarget(MT_CLIENT);
832
833         if(!menuInitialized)
834                 return;
835
836         main.focusLeave(main);
837         main.hideNotify(main);
838 }
839
840 void m_toggle(float mode)
841 {
842         if(Menu_Active)
843         {
844                 if (mode == 1)
845                         return;
846                 m_hide();
847         }
848         else
849         {
850                 if (mode == 0)
851                         return;
852                 m_display();
853         }
854 }
855
856 void Shutdown()
857 {
858         entity e;
859
860         m_hide();
861         for(e = NULL; (e = nextent(e)) != NULL; )
862         {
863                 if(e.classname != "vtbl")
864                         if(e.destroy)
865                                 e.destroy(e);
866         }
867 }
868
869 void m_focus_item_chain(entity outermost, entity innermost)
870 {
871         if(innermost.parent != outermost)
872                 m_focus_item_chain(outermost, innermost.parent);
873         innermost.parent.setFocus(innermost.parent, innermost);
874 }
875
876 void m_activate_window(entity wnd)
877 {
878         entity par;
879         par = wnd.parent;
880         if(par)
881                 m_activate_window(par);
882
883         if(par.instanceOfModalController)
884         {
885                 if(wnd.tabSelectingButton)
886                         // tabs
887                         TabButton_Click(wnd.tabSelectingButton, wnd);
888                 else
889                         // root
890                         par.initializeDialog(par, wnd);
891         }
892         else if(par.instanceOfNexposee)
893         {
894                 // nexposee (sorry for violating abstraction here)
895                 par.selectedChild = wnd;
896                 par.animationState = 1;
897                 Container_setFocus(par, NULL);
898         }
899         else if(par.instanceOfContainer)
900         {
901                 // other containers
902                 if(par.focused)
903                         par.setFocus(par, wnd);
904         }
905 }
906
907 void m_setpointerfocus(entity wnd)
908 {
909         if(wnd.instanceOfContainer)
910         {
911                 entity focus = wnd.preferredFocusedGrandChild(wnd);
912                 if(focus)
913                 {
914                         menuMousePos = focus.origin + 0.5 * focus.size;
915                         menuMousePos_x *= 1 / conwidth;
916                         menuMousePos_y *= 1 / conheight;
917                         if(wnd.focused) // why does this never happen?
918                                 m_focus_item_chain(wnd, focus);
919                 }
920         }
921 }
922
923 void m_goto(string itemname)
924 {
925         entity e;
926         if(!menuInitialized)
927                 return;
928         if(itemname == "") // this can be called by GameCommand
929         {
930                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))
931                         m_hide();
932                 else
933                 {
934                         m_activate_window(main.mainNexposee);
935                         m_display();
936                 }
937         }
938         else
939         {
940                 for(e = NULL; (e = find(e, name, itemname)); )
941                         if(e.classname != "vtbl")
942                                 break;
943                 if(e)
944                 {
945                         m_hide();
946                         m_activate_window(e);
947                         m_setpointerfocus(e);
948                         m_display();
949                 }
950         }
951 }
952
953 void m_goto_skin_selector()
954 {
955         if(!menuInitialized)
956                 return;
957         // TODO add code to switch back to the skin selector (no idea how to do it now)
958         m_goto("skinselector");
959 }
960
961 void m_goto_language_selector()
962 {
963         if(!menuInitialized)
964                 return;
965         // TODO add code to switch back to the language selector (no idea how to do it now)
966         m_goto("languageselector");
967 }
968
969 void m_goto_video_settings()
970 {
971         if(!menuInitialized)
972                 return;
973         // TODO add code to switch back to the video settings (no idea how to do it now)
974         m_goto("videosettings");
975 }