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