]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/menu.qc
fix a tooltip related warning message spam
[divverent/nexuiz.git] / data / 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_init =
22 {
23         dprint_load();
24         check_unacceptable_compiler_bugs();
25 }
26
27 void UpdateConWidthHeight()
28 {
29         float conwidth_s, conheight_s;
30         conwidth_s = conwidth;
31         conheight_s = conheight;
32         conwidth = cvar("vid_conwidth");
33         conheight = cvar("vid_conheight");
34         if(conwidth < 800)
35         {
36                 conheight *= 800 / conwidth;
37                 conwidth = 800;
38         }
39         if(conheight < 600)
40         {
41                 conwidth *= 600 / conheight;
42                 conheight = 600;
43         }
44         if(main)
45         {
46                 if(conwidth_s != conwidth || conheight_s != conheight)
47                 {
48                         draw_reset();
49                         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
50                 }
51         }
52 }
53
54 void() m_init_delayed =
55 {
56         float fh, glob, n, i;
57         string s;
58
59         dprint_load();
60
61         menuInitialized = 0;
62         if(!preMenuInit())
63                 return;
64         menuInitialized = 1;
65         GameCommand_Init();
66
67         RegisterWeapons();
68
69         fh = -1;
70         if(cvar_string("menu_skin") != "")
71         {
72                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
73                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
74         }
75         if(fh < 0)
76         if(cvar_defstring("menu_skin") != "")
77         {
78                 draw_currentSkin = strcat("gfx/menu/", cvar_defstring("menu_skin"));
79                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
80         }
81         if(fh < 0)
82         {
83                 draw_currentSkin = "gfx/menu/default";
84                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
85         }
86         draw_currentSkin = strzone(draw_currentSkin);
87         while((s = fgets(fh)))
88         {
89                 // these two are handled by skinlist.qc
90                 if(substring(s, 0, 6) == "title ")
91                         continue;
92                 if(substring(s, 0, 7) == "author ")
93                         continue;
94                 n = tokenize_console(s);
95                 if(n >= 2)
96                         Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
97         }
98         fclose(fh);
99
100         glob = search_begin(strcat(draw_currentSkin, "/*.tga"), TRUE, TRUE);
101         if(glob >= 0)
102         {
103                 n = search_getsize(glob);
104                 for(i = 0; i < n; ++i)
105                         precache_pic(search_getfilename(glob, i));
106                 search_end(glob);
107         }
108
109         draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
110
111         conwidth = conheight = -1;
112         draw_reset();
113         UpdateConWidthHeight();
114
115         loadTooltips();
116         main = spawnMainWindow(); main.configureMainWindow(main);
117         unloadTooltips();
118
119         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
120         main.focused = 1;
121         menuShiftState = 0;
122         menuMousePos = '0.5 0.5 0';
123
124         if(Menu_Active)
125                 m_display(); // delayed menu display
126 };
127
128 void(float key, float ascii) m_keyup =
129 {
130         if(!menuInitialized)
131                 return;
132         if(!Menu_Active)
133                 return;
134         draw_reset();
135         main.keyUp(main, key, ascii, menuShiftState);
136         if(key >= K_MOUSE1 && key <= K_MOUSE3)
137         {
138                 --mouseButtonsPressed;
139                 if(!mouseButtonsPressed)
140                         main.mouseRelease(main, menuMousePos);
141                 if(mouseButtonsPressed < 0)
142                 {
143                         mouseButtonsPressed = 0;
144                         print("Warning: released an already released button\n");
145                 }
146         }
147         if(key == K_ALT) menuShiftState -= (menuShiftState & S_ALT);
148         if(key == K_CTRL) menuShiftState -= (menuShiftState & S_CTRL);
149         if(key == K_SHIFT) menuShiftState -= (menuShiftState & S_SHIFT);
150 };
151
152 void(float key, float ascii) m_keydown =
153 {
154         if(!menuInitialized)
155                 return;
156         if(!Menu_Active)
157                 return;
158         if(keyGrabber)
159         {
160                 entity e;
161                 e = keyGrabber;
162                 keyGrabber = NULL;
163                 e.keyGrabbed(e, key, ascii);
164         }
165         else
166         {
167                 draw_reset();
168                 if(key >= K_MOUSE1 && key <= K_MOUSE3)
169                         if(!mouseButtonsPressed)
170                                 main.mousePress(main, menuMousePos);
171                 if(!main.keyDown(main, key, ascii, menuShiftState))
172                         if(key == K_ESCAPE)
173                                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
174                                         m_hide(); // disable menu on unhandled ESC
175         }
176         if(key >= K_MOUSE1 && key <= K_MOUSE3)
177         {
178                 ++mouseButtonsPressed;
179                 if(mouseButtonsPressed > 10)
180                 {
181                         mouseButtonsPressed = 10;
182                         print("Warning: pressed an already pressed button\n");
183                 }
184         }
185         if(key == K_ALT) menuShiftState |= S_ALT;
186         if(key == K_CTRL) menuShiftState |= S_CTRL;
187         if(key == K_SHIFT) menuShiftState |= S_SHIFT;
188 };
189
190 void(string img, float a) drawBackground =
191 {
192         vector sz;
193         vector isz;
194         sz = draw_PictureSize(img);
195         // keep aspect of image
196         if(sz_x * draw_scale_y >= sz_y * draw_scale_x)
197         {
198                 // that is, sz_x/sz_y >= draw_scale_x/draw_scale_y
199                 // match up the height
200                 isz_y = 1;
201                 isz_x = isz_y * (sz_x / sz_y) * (draw_scale_y / draw_scale_x);
202         }
203         else
204         {
205                 // that is, sz_x/sz_y <= draw_scale_x/draw_scale_y
206                 // match up the width
207                 isz_x = 1;
208                 isz_y = isz_x * (sz_y / sz_x) * (draw_scale_x / draw_scale_y);
209         }
210         draw_Picture('0.5 0.5 0' - 0.5 * isz, img, isz, '1 1 1', a);
211 }
212
213
214 vector menuTooltipAveragedMousePos;
215 entity menuTooltipItem;
216 vector menuTooltipOrigin;
217 vector menuTooltipSize;
218 float menuTooltipAlpha;
219 float menuTooltipState; // 0: no tooltip, 1: fading in, 2: displaying, 3: fading out
220 float m_testmousetooltipbox(vector pos)
221 {
222         if(pos_x >= menuTooltipOrigin_x && pos_x < menuTooltipOrigin_x + menuTooltipSize_x)
223         if(pos_y >= menuTooltipOrigin_y && pos_y < menuTooltipOrigin_y + menuTooltipSize_y)
224                 return FALSE;
225         return TRUE;
226 }
227 float m_testtooltipbox(vector tooltippos)
228 {
229         if(tooltippos_x < 0)
230                 return FALSE;
231         if(tooltippos_y < 0)
232                 return FALSE;
233         if(tooltippos_x + menuTooltipSize_x > 1)
234                 return FALSE;
235         if(tooltippos_y + menuTooltipSize_y > 1)
236                 return FALSE;
237         /*
238         menuTooltipOrigin_x = rint(tooltippos_x * cvar("vid_width")) / cvar("vid_width");
239         menuTooltipOrigin_y = rint(tooltippos_y * cvar("vid_height")) / cvar("vid_height");
240         menuTooltipOrigin_z = 0;
241         */
242         menuTooltipOrigin = tooltippos;
243         return TRUE;
244 }
245 float m_allocatetooltipbox(vector pos)
246 {
247         vector avoidplus, avoidminus;
248         vector v;
249
250         avoidplus_x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x) / conwidth;
251         avoidplus_y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y) / conheight;
252         avoidplus_z = 0;
253
254         avoidminus_x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x) / conwidth + menuTooltipSize_x;
255         avoidminus_y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y) / conheight + menuTooltipSize_y;
256         avoidminus_z = 0;
257
258         // bottom right
259         v = pos + avoidplus;
260         if(m_testtooltipbox(v))
261                 return TRUE;
262         
263         // bottom center
264         v_x = pos_x - menuTooltipSize_x * 0.5;
265         if(m_testtooltipbox(v))
266                 return TRUE;
267
268         // bottom left
269         v_x = pos_x - avoidminus_x;
270         if(m_testtooltipbox(v))
271                 return TRUE;
272
273         // top left
274         v_y = pos_y - avoidminus_y;
275         if(m_testtooltipbox(v))
276                 return TRUE;
277
278         // top center
279         v_x = pos_x - menuTooltipSize_x * 0.5;
280         if(m_testtooltipbox(v))
281                 return TRUE;
282         
283         // top right
284         v_x = pos_x + avoidplus_x;
285         if(m_testtooltipbox(v))
286                 return TRUE;
287         
288         return FALSE;
289 }
290 entity m_findtooltipitem(entity root, vector pos)
291 {
292         entity it;
293         entity best;
294
295         best = world;
296         it = root;
297
298         while(it.instanceOfContainer)
299         {
300                 if(it.instanceOfNexposee)
301                 {
302                         if(it.focusedChild)
303                         {
304                                 it = it.focusedChild;
305                                 pos = globalToBox(pos, it.Container_origin, it.Container_size);
306                                 continue; // don't show dialog tooltips when the dialog is open
307                         }
308                         else
309                         {
310                                 it = it.itemFromPoint(it, pos);
311                                 if(it.tooltip)
312                                         best = it;
313                                 break;
314                         }
315                 }
316                 else if(it.instanceOfModalController)
317                         it = it.focusedChild;
318                 else
319                         it = it.itemFromPoint(it, pos);
320                 if(!it)
321                         break;
322                 if(it.tooltip)
323                         best = it;
324                 pos = globalToBox(pos, it.Container_origin, it.Container_size);
325         }
326
327         return best;
328 }
329 void m_tooltip(vector pos)
330 {
331         float f, i, w;
332         entity it;
333         vector fontsize, p;
334         string s;
335
336         fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
337
338         f = bound(0, frametime * 2, 1);
339         menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f;
340         f = vlen(pos - menuTooltipAveragedMousePos);
341
342         if(f < 0.01)
343                 it = m_findtooltipitem(main, pos);
344         else    
345                 it = world;
346
347         // float menuTooltipState; // 0: static, 1: fading in, 2: fading out
348         if(it != menuTooltipItem)
349         {
350                 switch(menuTooltipState)
351                 {
352                         case 0:
353                                 if(menuTooltipItem)
354                                 {
355                                         // another item: fade out first
356                                         menuTooltipState = 2;
357                                 }
358                                 else
359                                 {
360                                         // new item: fade in
361                                         menuTooltipState = 1;
362                                         menuTooltipItem = it;
363
364                                         menuTooltipOrigin_x = -1; // unallocated
365                                         i = 0;
366                                         w =  0;
367                                         getWrappedLine_remaining = it.tooltip;
368                                         while(getWrappedLine_remaining)
369                                         {
370                                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
371                                                 ++i;
372                                                 f = draw_TextWidth(s, FALSE);
373                                                 if(f > w)
374                                                         w = f;
375                                         }
376                                         menuTooltipSize_x = w * fontsize_x + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
377                                         menuTooltipSize_y = i * fontsize_y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
378                                         menuTooltipSize_z = 0;
379                                 }
380                                 break;
381                         case 1:
382                                 // changing item while fading in: fade out first
383                                 menuTooltipState = 2;
384                                 break;
385                         case 2:
386                                 // changing item while fading out: can't
387                                 break;
388                 }
389         }
390         else if(menuTooltipState == 2) // re-fade in?
391                 menuTooltipState = 1;
392
393         if(menuTooltipItem)
394                 if(!m_testmousetooltipbox(pos))
395                         menuTooltipState = 2; // fade out if mouse touches it
396
397         switch(menuTooltipState)
398         {
399                 case 1:
400                         menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1);
401                         if(menuTooltipAlpha == 1)
402                                 menuTooltipState = 0;
403                         break;
404                 case 2:
405                         menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1);
406                         if(menuTooltipAlpha == 0)
407                         {
408                                 menuTooltipState = 0;
409                                 menuTooltipItem = world;
410                         }
411                         break;
412         }
413
414         if(menuTooltipItem)
415         {
416                 if(menuTooltipOrigin_x < 0) // unallocated?
417                         m_allocatetooltipbox(pos);
418
419                 if(menuTooltipOrigin_x >= 0)
420                 {
421                         // draw the tooltip!
422                         p = SKINBORDER_TOOLTIP;
423                         p_x *= 1 / conwidth;
424                         p_y *= 1 / conheight;
425                         draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
426                         p = menuTooltipOrigin;
427                         p_x += SKINMARGIN_TOOLTIP_x / conwidth;
428                         p_y += SKINMARGIN_TOOLTIP_y / conheight;
429                         getWrappedLine_remaining = menuTooltipItem.tooltip;
430                         while(getWrappedLine_remaining)
431                         {
432                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
433                                 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, FALSE);
434                                 p_y += fontsize_y;
435                         }
436                 }
437         }
438 }
439
440 void() m_draw =
441 {
442         float t;
443         float realFrametime;
444
445         menuMouseMode = cvar("menu_mouse_absolute");
446
447         if(main)
448                 UpdateConWidthHeight();
449
450         if(!menuInitialized)
451         {
452                 // TODO draw an info image about this situation
453                 m_init_delayed();
454                 return;
455         }
456         if(!menuNotTheFirstFrame)
457         {
458                 menuNotTheFirstFrame = 1;
459                 if(Menu_Active)
460                 if(!cvar("menu_video_played"))
461                 {
462                         localcmd("set menu_video_played 1; cd loop $menu_cdtrack; play sound/announcer/male/welcome.ogg\n");
463                         menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading
464                 }
465         }
466
467         t = gettime();
468         realFrametime = frametime = min(0.2, t - menuPrevTime);
469         menuPrevTime = t;
470         time += frametime;
471
472         t = cvar("menu_slowmo");
473         if(t)
474         {
475                 frametime *= t;
476                 realFrametime *= t;
477         }
478         else
479                 t = 1;
480
481         if(Menu_Active)
482         {
483                 if(getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED))
484                         setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU);
485                 else
486                         m_hide();
487         }
488
489         if(cvar("cl_capturevideo"))
490                 frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
491
492         dprint_load();
493         gamestatus = 0;
494         if(isserver())
495                 gamestatus = gamestatus | GAME_ISSERVER;
496         if(clientstate() == CS_CONNECTED)
497                 gamestatus = gamestatus | GAME_CONNECTED;
498         if(cvar("developer"))
499                 gamestatus = gamestatus | GAME_DEVELOPER;
500
501         prevMenuAlpha = menuAlpha;
502         if(Menu_Active)
503         {
504                 if(menuAlpha == 0 && menuLogoAlpha < 2)
505                 {
506                         menuLogoAlpha = menuLogoAlpha + frametime * 2;
507                 }
508                 else
509                 {
510                         menuAlpha = min(1, menuAlpha + frametime * 5);
511                         menuLogoAlpha = 2;
512                 }
513         }
514         else
515         {
516                 menuAlpha = max(0, menuAlpha - frametime * 5);
517                 menuLogoAlpha = 2;
518         }
519
520         draw_reset();
521
522         if(!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
523         {
524                 if(menuLogoAlpha > 0)
525                 {
526                         drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1));
527                         if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
528                         {
529                                 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
530                                 draw_drawMousePointer(menuMousePos);
531                                 draw_alpha = 1;
532                         }
533                 }
534         }
535         else if(SKINALPHA_BACKGROUND_INGAME)
536         {
537                 if(menuAlpha > 0)
538                         drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME);
539         }
540
541         draw_reset();
542         preMenuDraw();
543         draw_reset();
544
545         if(menuAlpha <= 0)
546         {
547                 if(prevMenuAlpha > 0)
548                         main.initializeDialog(main, main.firstChild);
549                 draw_reset();
550                 postMenuDraw();
551                 return;
552         }
553
554         draw_alpha *= menuAlpha;
555
556         if(menuMouseMode)
557         {
558                 vector newMouse;
559                 newMouse = globalToBoxSize(getmousepos(), draw_scale);
560                 if(newMouse != '0 0 0')
561                         if(newMouse != menuMousePos)
562                         {
563                                 menuMousePos = newMouse;
564                                 if(mouseButtonsPressed)
565                                         main.mouseDrag(main, menuMousePos);
566                                 else
567                                         main.mouseMove(main, menuMousePos);
568                         }
569         }
570         else
571         {
572                 if(frametime > 0)
573                 {
574                         vector dMouse;
575                         dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
576                         if(dMouse != '0 0 0')
577                         {
578                                 dMouse = globalToBoxSize(dMouse, draw_scale);
579                                 menuMousePos += dMouse * cvar("menu_mouse_speed");
580                                 menuMousePos_x = bound(0, menuMousePos_x, 1);
581                                 menuMousePos_y = bound(0, menuMousePos_y, 1);
582                                 if(mouseButtonsPressed)
583                                         main.mouseDrag(main, menuMousePos);
584                                 else
585                                         main.mouseMove(main, menuMousePos);
586                         }
587                 }
588         }
589         main.draw(main);
590
591         m_tooltip(menuMousePos);
592
593         draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
594
595         draw_drawMousePointer(menuMousePos);
596
597         draw_reset();
598         postMenuDraw();
599
600         frametime = 0;
601 };
602
603 void() m_display =
604 {
605         Menu_Active = true;
606         setkeydest(KEY_MENU);
607         setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU));
608
609         if(!menuInitialized)
610                 return;
611
612         if(mouseButtonsPressed)
613                 main.mouseRelease(main, menuMousePos);
614         mouseButtonsPressed = 0;
615
616         main.focusEnter(main);
617         main.showNotify(main);
618 };
619
620 void() m_hide =
621 {
622         Menu_Active = false;
623         setkeydest(KEY_GAME);
624         setmousetarget(MT_CLIENT);
625
626         if(!menuInitialized)
627                 return;
628
629         main.focusLeave(main);
630         main.hideNotify(main);
631 };
632
633 void() m_toggle =
634 {
635         if(Menu_Active)
636                 m_hide();
637         else
638                 m_display();
639 };
640
641 void() m_shutdown =
642 {
643         entity e;
644
645         m_hide();
646         for(e = NULL; (e = nextent(e)) != NULL; )
647         {
648                 if(e.destroy)
649                         e.destroy(e);
650         }
651 };
652
653 void m_focus_item_chain(entity outermost, entity innermost)
654 {
655         if(innermost.parent != outermost)
656                 m_focus_item_chain(outermost, innermost.parent);
657         innermost.parent.setFocus(innermost.parent, innermost);
658 }
659
660 void m_activate_window(entity wnd)
661 {
662         entity par;
663         par = wnd.parent;
664         if(par)
665                 m_activate_window(par);
666
667         if(par.instanceOfModalController)
668         {
669                 if(wnd.tabSelectingButton)
670                         // tabs
671                         TabButton_Click(wnd.tabSelectingButton, wnd);
672                 else
673                         // root
674                         par.initializeDialog(par, wnd);
675         }
676         else if(par.instanceOfNexposee)
677         {
678                 // nexposee (sorry for violating abstraction here)
679                 par.selectedChild = wnd;
680                 par.animationState = 1;
681                 setFocusContainer(par, NULL);
682         }
683         else if(par.instanceOfContainer)
684         {
685                 // other containers
686                 if(par.focused)
687                         par.setFocus(par, wnd);
688         }
689 }
690
691 void m_setpointerfocus(entity wnd)
692 {
693         if(wnd.instanceOfContainer)
694         {
695                 entity focus = wnd.preferredFocusedGrandChild(wnd);
696                 if(focus)
697                 {
698                         menuMousePos = focus.origin + 0.5 * focus.size;
699                         menuMousePos_x *= 1 / conwidth;
700                         menuMousePos_y *= 1 / conheight;
701                         if(wnd.focused) // why does this never happen?
702                                 m_focus_item_chain(wnd, focus);
703                 }
704         }
705 }
706
707 void(string itemname) m_goto =
708 {
709         entity e;
710         if(!menuInitialized)
711                 return;
712         if(itemname == "") // this can be called by GameCommand
713         {
714                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))
715                         m_hide();
716                 else
717                 {
718                         m_activate_window(main.mainNexposee);
719                         m_display();
720                 }
721         }
722         else
723         {
724                 e = findstring(NULL, name, itemname);
725                 if(e)
726                 {
727                         m_hide();
728                         m_activate_window(e);
729                         m_setpointerfocus(e);
730                         m_display();
731                 }
732         }
733 }
734
735 void() m_goto_skin_selector =
736 {
737         if(!menuInitialized)
738                 return;
739         // TODO add code to switch back to the skin selector (no idea how to do it now)
740         m_goto("skinselector");
741 }