1 #include "engine.h"
2 
3 int uimillis = -1;
4 
5 VAR(IDF_READONLY, guilayoutpass, 1, 0, -1);
6 VAR(0, guicursortype, 0, 0, 2);
7 bool guiactionon = false;
8 int mouseaction[2] = {0};
9 
10 static float firstx, firsty;
11 
12 enum {FIELDCOMMIT, FIELDABORT, FIELDEDIT, FIELDSHOW, FIELDKEY};
13 static int fieldmode = FIELDSHOW;
14 static bool fieldsactive = false;
15 
16 FVAR(IDF_PERSIST, guiscale, FVAR_NONZERO, 0.00055f, VAR_MAX);
17 VAR(IDF_PERSIST, guiskinsize, 0, 48, VAR_MAX); // 0 = texture size, otherwise = size in pixels for skin scaling
18 VAR(IDF_PERSIST, guislidersize, 1, 58, VAR_MAX);
19 VAR(IDF_PERSIST, guisepsize, 1, 6, VAR_MAX);
20 VAR(IDF_PERSIST, guispacesize, 1, 48, VAR_MAX);
21 VAR(IDF_PERSIST, guitooltipwidth, -1, -1, VAR_MAX);
22 VAR(IDF_PERSIST, guistatuswidth, -1, -1, VAR_MAX);
23 
24 VAR(IDF_PERSIST, guishadow, 0, 2, 8);
25 VAR(IDF_PERSIST, guiclicktab, 0, 1, 1);
26 VAR(IDF_PERSIST, guitabborder, 0, 1, 2);
27 VAR(IDF_PERSIST, guitextblend, 1, 255, 255);
28 VAR(IDF_PERSIST, guitextfade, 1, 200, 255);
29 VAR(IDF_PERSIST, guiscaletime, 0, 250, VAR_MAX);
30 
31 VAR(IDF_PERSIST, guiskinned, 0, 3, 3); // 0 = no backgrounds, 1 = drawn backgrounds, 2 = skinned backgrounds, 3 = skinned with overlay border
32 
33 VAR(IDF_PERSIST|IDF_HEX, guibgcolour, -1, 0x000000, 0xFFFFFF);
34 FVAR(IDF_PERSIST, guibgblend, 0, 0.8f, 1);
35 VAR(IDF_PERSIST|IDF_HEX, guibordercolour, -1, 0x000000, 0xFFFFFF);
36 FVAR(IDF_PERSIST, guiborderblend, 0, 0.6f, 1);
37 
38 VAR(IDF_PERSIST|IDF_HEX, guihovercolour, -1, 0xF0A0A0, 0xFFFFFF);
39 FVAR(IDF_PERSIST, guihoverscale, 0, 0.3f, 1);
40 FVAR(IDF_PERSIST, guihoverblend, 0, 0.9f, 1);
41 
42 VAR(IDF_PERSIST, guistatusline, 0, 1, 1);
43 VAR(IDF_PERSIST, guitooltips, 0, 1, 1);
44 VAR(IDF_PERSIST, guitooltiptime, 0, 500, VAR_MAX);
45 VAR(IDF_PERSIST, guitooltipfade, 0, 500, VAR_MAX);
46 VAR(IDF_PERSIST|IDF_HEX, guitooltipcolour, -1, 0x000000, 0xFFFFFF);
47 FVAR(IDF_PERSIST, guitooltipblend, 0, 0.8f, 1);
48 VAR(IDF_PERSIST|IDF_HEX, guitooltipbordercolour, -1, 0x808080, 0xFFFFFF);
49 FVAR(IDF_PERSIST, guitooltipborderblend, 0, 0.6f, 1);
50 VAR(IDF_PERSIST, guitooltipborderskin, 0, 1, 1);
51 
52 VAR(IDF_PERSIST|IDF_HEX, guifieldbgcolour, -1, 0x202020, 0xFFFFFF);
53 FVAR(IDF_PERSIST, guifieldbgblend, 0, 0.3f, 1);
54 VAR(IDF_PERSIST|IDF_HEX, guifieldbordercolour, -1, 0xA0A0A0, 0xFFFFFF);
55 FVAR(IDF_PERSIST, guifieldborderblend, 0, 0.6f, 1);
56 
57 VAR(IDF_PERSIST|IDF_HEX, guifieldhitcolour, -1, 0xF04040, 0xFFFFFF);
58 FVAR(IDF_PERSIST, guifieldhitblend, 0, 0.8f, 1);
59 VAR(IDF_PERSIST|IDF_HEX, guifieldactivecolour, -1, 0xF04040, 0xFFFFFF);
60 FVAR(IDF_PERSIST, guifieldactiveblend, 0, 0.9f, 1);
61 
62 VAR(IDF_PERSIST|IDF_HEX, guislidercolour, -1, 0x000000, 0xFFFFFF);
63 FVAR(IDF_PERSIST, guisliderblend, 0, 0.3f, 1);
64 VAR(IDF_PERSIST|IDF_HEX, guisliderbordercolour, -1, 0xC0C0C0, 0xFFFFFF);
65 FVAR(IDF_PERSIST, guisliderborderblend, 0, 0.6f, 1);
66 VAR(IDF_PERSIST, guisliderborderskin, 0, 2, 2);
67 VAR(IDF_PERSIST|IDF_HEX, guislidermarkcolour, -1, 0x808080, 0xFFFFFF);
68 FVAR(IDF_PERSIST, guislidermarkblend, 0, 0.5f, 1);
69 VAR(IDF_PERSIST|IDF_HEX, guislidermarkbordercolour, -1, 0xC0C0C0, 0xFFFFFF);
70 FVAR(IDF_PERSIST, guislidermarkborderblend, 0, 0.6f, 1);
71 VAR(IDF_PERSIST, guislidermarkborderskin, 0, 0, 2);
72 VAR(IDF_PERSIST|IDF_HEX, guislideractivecolour, -1, 0xF04040, 0xFFFFFF);
73 FVAR(IDF_PERSIST, guislideractiveblend, 0, 0.9f, 1);
74 
75 VAR(IDF_PERSIST, guiactiveskin, 0, 1, 1);
76 VAR(IDF_PERSIST|IDF_HEX, guiactivecolour, -1, 0xF02020, 0xFFFFFF);
77 
78 VAR(IDF_PERSIST|IDF_HEX, guicheckboxcolour, -1, 0x20F020, 0xFFFFFF);
79 VAR(IDF_PERSIST|IDF_HEX, guicheckboxtwocolour, -1, 0xF020F0, 0xFFFFFF);
80 VAR(IDF_PERSIST|IDF_HEX, guiradioboxcolour, -1, 0xF02020, 0xFFFFFF);
81 
82 static bool needsinput = false, hastitle = true, hasbgfx = true, tooltipforce = false;
83 static char *statusstr = NULL, *tooltipstr = NULL, *tooltip = NULL;
84 static int lasttooltip = 0, statuswidth = 0, tooltipwidth = 0;
85 
86 #include "textedit.h"
87 struct gui : guient
88 {
89     struct list { int parent, w, h, springs, curspring, mouse[2]; };
90 
91     int nextlist;
92     static vector<list> lists;
93     static float hitx, hity;
94     static int curdepth, curlist, xsize, ysize, curx, cury, fontdepth, mergelist, mergedepth;
95     static bool hitfx, skinfx, cursorfx;
96 
resetgui97     static void reset()
98     {
99         if(statusstr) DELETEA(statusstr);
100         if(tooltipstr) DELETEA(tooltipstr);
101         lists.shrink(0);
102         statuswidth = tooltipwidth = 0;
103         mergelist = mergedepth = -1;
104         tooltipforce = false;
105     }
106 
107     static int ty, tx, tpos, *tcurrent, tcolor; //tracking tab size and position since uses different layout method...
108 
setcursortypegui109     int setcursortype(int type) { return guicursortype = type; }
getcursortypegui110     int getcursortype() { return guicursortype; }
allowcursorfxgui111     bool allowcursorfx(bool on) { return hitfx = on; }
allowhitfxgui112     bool allowhitfx(bool on) { return cursorfx = on; }
allowskinfxgui113     bool allowskinfx(bool on) { return skinfx = on; }
visibletabgui114     bool visibletab() { return !tcurrent || tpos == *tcurrent; }
visiblegui115     bool visible() { return !guilayoutpass && visibletab(); }
116 
skingui117     void skin(int x1, int y1, int x2, int y2, int c1 = -1, float b1 = -1.f, int c2 = -1, float b2 = -1.f, bool skinborder = false)
118     {
119         int colour1 = c1 >= 0 ? c1 : (guibgcolour >= 0 ? guibgcolour : (c2 >= 0 ? c2 : 0x000000)),
120             colour2 = c2 >= 0 ? c2 : (guibordercolour >= 0 ? guibordercolour : 0x808080);
121         float blend1 = b1 >= 0 ? b1 : guibgblend, blend2 = b2 >= 0 ? b2 : guiborderblend;
122         switch(guiskinned)
123         {
124             case 2: case 3:
125             {
126                 loopk(guiskinned == 3 || skinborder ? 2 : 1)
127                 {
128                     int colour = colour1;
129                     float blend = blend1;
130                     Texture *t = NULL;
131                     switch(k)
132                     {
133                         case 1:
134                             colour = colour2;
135                             blend = blend2;
136                             if(!skinbordertex) skinbordertex = textureload(guiskinbordertex, 0, true, false);
137                             t = skinbordertex;
138                             break;
139                         case 0: default:
140                             if(!skintex) skintex = textureload(guiskintex, 0, true, false);
141                             t = skintex;
142                             break;
143                     }
144                     drawskin(t, x1, y1, x2, y2, colour, blend, guiskinsize);
145                 }
146                 break;
147             }
148             case 1:
149             {
150                 if(colour1 >= 0)
151                 {
152                     hudnotextureshader->set();
153                     gle::color(vec::hexcolor(colour1), blend1);
154                     gle::defvertex(2);
155                     gle::begin(GL_TRIANGLE_STRIP);
156                     gle::attribf(x1, y1);
157                     gle::attribf(x2, y1);
158                     gle::attribf(x1, y2);
159                     gle::attribf(x2, y2);
160                     xtraverts += gle::end();
161                     hudshader->set();
162                 }
163                 if(skinborder && colour2 >= 0)
164                 {
165                     hudnotextureshader->set();
166                     gle::color(vec::hexcolor(colour2), blend2);
167                     gle::defvertex(2);
168                     gle::begin(GL_LINE_LOOP);
169                     gle::attribf(x1, y1);
170                     gle::attribf(x2, y1);
171                     gle::attribf(x2, y2);
172                     gle::attribf(x1, y2);
173                     xtraverts += gle::end();
174                     hudshader->set();
175                 }
176                 break;
177             }
178             case 0: default: break;
179         }
180     }
181 
182     //tab is always at top of page
tabgui183     void tab(const char *name, int color, bool front)
184     {
185         if(curdepth != 0) return;
186         tpos++;
187         if(front && tcurrent && *tcurrent != tpos) *tcurrent = tpos;
188         if(!hastitle)
189         {
190             if(guilayoutpass)
191             {
192                 ty = max(ty, ysize);
193                 ysize = 0;
194             }
195             else cury = -ysize;
196             return;
197         }
198         if(color) tcolor = color;
199         if(!name) name = intstr(tpos);
200         gui::pushfont("super");
201         int width = 0, height = 0;
202         text_bounds(name, width, height, 0, 0, -1, TEXT_NO_INDENT);
203         if(guilayoutpass)
204         {
205             ty = max(ty, ysize);
206             ysize = 0;
207         }
208         else
209         {
210             cury = -ysize;
211             int x1 = curx+tx, x2 = x1+width+guispacesize, y1 = cury-guispacesize-height, y2 = cury-guispacesize*3/4, alpha = guitextblend, border = -1;
212             if(!visibletab())
213             {
214                 if(tcurrent && !passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2)
215                 {
216                     if(!guiclicktab || mouseaction[0]&GUI_UP) *tcurrent = tpos; // switch tab
217                     tcolor = guiactivecolour;
218                     alpha = max(alpha, guitextfade);
219                     if(guitabborder) border = tcolor;
220                 }
221                 else
222                 {
223                     tcolor = vec::hexcolor(tcolor).mul(0.25f).tohexcolor();
224                     if(guitabborder == 2) border = tcolor;
225                 }
226             }
227             else if(guitabborder == 2) border = guibordercolour;
228             if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, border >= 0 ? border : guibordercolour, guiborderblend, border >= 0);
229             text_(name, x1+guispacesize/2, y1+guispacesize/8, tcolor, alpha);
230         }
231         tx += width+guispacesize*3/2;
232         gui::popfont();
233     }
234 
uibuttonsgui235     void uibuttons()
236     {
237         gui::pushfont("super");
238         tx += FONTH+guispacesize*2; // acts like a tab
239         if(!guilayoutpass)
240         {
241             cury = -ysize;
242             int x1 = curx+(xsize-FONTH+guispacesize/4), x2 = x1+FONTH+guispacesize/4, y1 = cury-guispacesize-FONTH, y2 = cury-guispacesize*3/4;
243             //int x1 = curx+(xsize-FONTH), x2 = x1+FONTH*3/2, y1 = cury-FONTH*225/100, y2 = cury-FONTH*3/4;
244             #define uibtn(a,b) \
245             { \
246                 int border = -1; \
247                 bool hit = false; \
248                 if(!passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx<x2 && hity<y2) \
249                 { \
250                     if(mouseaction[0]&GUI_UP) { b; } \
251                     hit = true; \
252                     if(guitabborder) border = guiactivecolour; \
253                 } \
254                 else if(guitabborder == 2) border = vec::hexcolor(guibordercolour).mul(0.25f).tohexcolor(); \
255                 if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, border >= 0 ? border : guibordercolour, guiborderblend, border >= 0); \
256                 x1 += guispacesize/8; \
257                 y1 += guispacesize/8; \
258                 icon_(a, false, x1, y1, FONTH, hit, 0xFFFFFF); \
259                 y1 += FONTH*3/2; \
260             }
261             if(!exittex) exittex = textureload(guiexittex, 3, true, false); \
262             uibtn(exittex, cleargui(1));
263         }
264         gui::popfont();
265     }
266 
ishorizontalgui267     bool ishorizontal() const { return curdepth&1; }
isverticalgui268     bool isvertical() const { return !ishorizontal(); }
269 
pushlistgui270     void pushlist(bool merge)
271     {
272         if(guilayoutpass)
273         {
274             if(curlist >= 0)
275             {
276                 lists[curlist].w = xsize;
277                 lists[curlist].h = ysize;
278             }
279             list &l = lists.add();
280             l.parent = curlist;
281             l.springs = 0;
282             curlist = lists.length()-1;
283             l.mouse[0] = l.mouse[1] = xsize = ysize = 0;
284         }
285         else
286         {
287             curlist = nextlist++;
288             if(curlist >= lists.length()) // should never get here unless script code doesn't use same amount of lists in layout and render passes
289             {
290                 list &l = lists.add();
291                 l.parent = curlist;
292                 l.springs = 0;
293                 l.w = l.h = l.mouse[0] = l.mouse[1] = 0;
294             }
295             list &l = lists[curlist];
296             l.curspring = 0;
297             if(l.springs > 0)
298             {
299                 if(ishorizontal()) xsize = l.w;
300                 else ysize = l.h;
301             }
302             else
303             {
304                 xsize = l.w;
305                 ysize = l.h;
306             }
307         }
308         curdepth++;
309         if(!guilayoutpass && visible() && ishit(xsize, ysize)) loopi(2) lists[curlist].mouse[i] = mouseaction[i]|GUI_ROLLOVER;
310         if(merge)
311         {
312             mergelist = curlist;
313             mergedepth = curdepth;
314         }
315     }
316 
poplistgui317     int poplist()
318     {
319         if(!lists.inrange(curlist)) return 0;
320         list &l = lists[curlist];
321         if(guilayoutpass)
322         {
323             l.w = xsize;
324             l.h = ysize;
325         }
326         curlist = l.parent;
327         curdepth--;
328         if(mergelist >= 0 && curdepth < mergedepth) mergelist = mergedepth = -1;
329         if(lists.inrange(curlist))
330         {
331             int w = xsize, h = ysize;
332             if(ishorizontal()) cury -= h;
333             else curx -= w;
334             list &p = lists[curlist];
335             xsize = p.w;
336             ysize = p.h;
337             if(!guilayoutpass && p.springs > 0)
338             {
339                 list &s = lists[p.parent];
340                 if(ishorizontal()) xsize = s.w;
341                 else ysize = s.h;
342             }
343             return layout(w, h);
344         }
345         return 0;
346     }
347 
setstatusgui348     void setstatus(const char *fmt, int width, ...)
349     {
350         if(statusstr) DELETEA(statusstr);
351         defvformatstring(str, width, fmt);
352         statusstr = newstring(str);
353         if(width) statuswidth = width;
354     }
355 
settooltipgui356     void settooltip(const char *fmt, int width, ...)
357     {
358         if(tooltipforce) return; // overridden in code
359         if(tooltipstr) DELETEA(tooltipstr);
360         defvformatstring(str, width, fmt);
361         tooltipstr = newstring(str);
362         if(width) tooltipwidth = width;
363     }
364 
pushfontgui365     void pushfont(const char *font) { ::pushfont(font); fontdepth++; }
popfontgui366     void popfont() { if(fontdepth) { ::popfont(); fontdepth--; } }
367 
textgui368     int text(const char *text, int color, const char *icon, int icolour, int wrap, bool faded, const char *oicon, int ocolor)
369     {
370         return button_(text, color, icon, icolour, false, wrap, faded, oicon, ocolor);
371     }
buttongui372     int button(const char *text, int color, const char *icon, int icolour, int wrap, bool faded, const char *oicon, int ocolor)
373     {
374         return button_(text, color, icon, icolour, true, wrap, faded, oicon, ocolor);
375     }
376 
separatorgui377     void separator(int size, int space, int colour, int border) { line_(size > 0 ? size : guisepsize, space > 0 ? space : 0, colour, border); }
378 
379     //use to set min size (useful when you have progress bars)
strutgui380     void strut(float size) { layout(isvertical() ? int(size*FONTW) : 0, isvertical() ? 0 : int(size*FONTH)); }
381     //add space between list items
spacegui382     void space(float size) { layout(isvertical() ? 0 : int(size*FONTW), isvertical() ? int(size*FONTH) : 0); }
383 
springgui384     void spring(int weight)
385     {
386         if(curlist < 0) return;
387         list &l = lists[curlist];
388         if(guilayoutpass) { if(l.parent >= 0) l.springs += weight; return; }
389         int nextspring = min(l.curspring + weight, l.springs);
390         if(nextspring <= l.curspring) return;
391         if(ishorizontal())
392         {
393             int w = xsize - l.w;
394             layout((w*nextspring)/l.springs - (w*l.curspring)/l.springs, 0);
395         }
396         else
397         {
398             int h = ysize - l.h;
399             layout(0, (h*nextspring)/l.springs - (h*l.curspring)/l.springs);
400         }
401         l.curspring = nextspring;
402     }
403 
layoutgui404     int layout(int w, int h)
405     {
406         if(guilayoutpass)
407         {
408             if(ishorizontal())
409             {
410                 xsize += w;
411                 ysize = max(ysize, h);
412             }
413             else
414             {
415                 xsize = max(xsize, w);
416                 ysize += h;
417             }
418         }
419         else
420         {
421             bool hit = ishit(w, h);
422             if(ishorizontal()) curx += w;
423             else cury += h;
424             if(hit && visible()) return mouseaction[0]|GUI_ROLLOVER;
425         }
426         return 0;
427     }
428 
ishitgui429     bool ishit(int w, int h, int x = curx, int y = cury)
430     {
431         if(passthrough || fieldmode == FIELDKEY) return false;
432         if(mergelist >= 0 && curdepth >= mergedepth && lists[mergelist].mouse[0]) return true;
433         if(ishorizontal()) h = ysize;
434         else w = xsize;
435         return hitx >= x && hity >= y && hitx < x+w && hity < y+h;
436     }
437 
imagegui438     int image(Texture *t, float scale, bool overlaid, int icolour, Texture *o, int ocolour)
439     {
440         if(scale == 0) scale = 1;
441         int size = (int)(scale*2*FONTH)-guishadow;
442         if(visible()) icon_(t, overlaid, curx, cury, size, ishit(size+guishadow, size+guishadow), icolour, o, ocolour);
443         return layout(size+guishadow, size+guishadow);
444     }
445 
texturegui446     int texture(VSlot &vslot, float scale, bool overlaid)
447     {
448         if(scale == 0) scale = 1;
449         int size = (int)(scale*2*FONTH)-guishadow;
450         if(visible()) previewslot(vslot, overlaid, curx, cury, size, ishit(size+guishadow, size+guishadow));
451         return layout(size+guishadow, size+guishadow);
452     }
453 
playerpreviewgui454     int playerpreview(int model, int color, int team, int weap, const char *vanity, float sizescale, bool overlaid, float scale, float blend)
455     {
456         if(sizescale == 0) sizescale = 1;
457         int size = (int)(sizescale*2*FONTH)-guishadow;
458         if(visible())
459         {
460             bool hit = ishit(size+guishadow, size+guishadow);
461             float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0;
462             if(overlaid)
463             {
464                 xpad = xs/32;
465                 ypad = ys/32;
466                 xi += xpad;
467                 yi += ypad;
468                 xs -= 2*xpad;
469                 ys -= 2*ypad;
470             }
471             int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))),
472                 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y))));
473             glDisable(GL_BLEND);
474             modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid);
475             game::renderplayerpreview(model, color, team, weap, vanity, scale, blend);
476             modelpreview::end();
477             hudshader->set();
478             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
479             glEnable(GL_BLEND);
480             if(overlaid)
481             {
482                 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false);
483                 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1));
484                 glBindTexture(GL_TEXTURE_2D, overlaytex->id);
485                 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0);
486             }
487             if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
488         }
489         return layout(size+guishadow, size+guishadow);
490     }
491 
modelpreviewgui492     int modelpreview(const char *name, int anim, float sizescale, bool overlaid, float scale, float blend)
493     {
494         if(sizescale == 0) sizescale = 1;
495         int size = (int)(sizescale*2*FONTH)-guishadow;
496         if(visible())
497         {
498             bool hit = ishit(size+guishadow, size+guishadow);
499             float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0;
500             if(overlaid)
501             {
502                 xpad = xs/32;
503                 ypad = ys/32;
504                 xi += xpad;
505                 yi += ypad;
506                 xs -= 2*xpad;
507                 ys -= 2*ypad;
508             }
509             int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))),
510                 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y))));
511             glDisable(GL_BLEND);
512             modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid);
513             model *m = loadmodel(name);
514             if(m)
515             {
516                 entitylight light;
517                 light.color = vec(1, 1, 1);
518                 light.dir = vec(0, -1, 2).normalize();
519                 vec center, radius;
520                 m->boundbox(center, radius);
521                 float yaw;
522                 vec o = calcmodelpreviewpos(radius, yaw).sub(center);
523                 rendermodel(&light, name, anim|ANIM_NOTRANS, o, yaw, 0, 0, 0, NULL, NULL, 0, 0, blend, scale);
524             }
525             modelpreview::end();
526             hudshader->set();
527             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
528             glEnable(GL_BLEND);
529             if(overlaid)
530             {
531                 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false);
532                 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1));
533                 glBindTexture(GL_TEXTURE_2D, overlaytex->id);
534                 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0);
535             }
536             if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
537         }
538         return layout(size+guishadow, size+guishadow);
539     }
540 
prefabpreviewgui541     int prefabpreview(const char *prefab, const vec &color, float sizescale, bool overlaid)
542     {
543         if(sizescale==0) sizescale = 1;
544         int size = (int)(sizescale*2*FONTH)-guishadow;
545         if(visible())
546         {
547             bool hit = ishit(size+guishadow, size+guishadow);
548             float xs = size, ys = size, xi = curx, yi = cury, xpad = 0, ypad = 0;
549             if(overlaid)
550             {
551                 xpad = xs/32;
552                 ypad = ys/32;
553                 xi += xpad;
554                 yi += ypad;
555                 xs -= 2*xpad;
556                 ys -= 2*ypad;
557             }
558             int x1 = int(floor(screenw*(xi*uiscale.x+uiorigin.x))), y1 = int(floor(screenh*(1 - ((yi+ys)*uiscale.y+uiorigin.y)))),
559                 x2 = int(ceil(screenw*((xi+xs)*uiscale.x+uiorigin.x))), y2 = int(ceil(screenh*(1 - (yi*uiscale.y+uiorigin.y))));
560             glDisable(GL_BLEND);
561             modelpreview::start(x1, y1, x2-x1, y2-y1, overlaid);
562             previewprefab(prefab, color);
563             modelpreview::end();
564             hudshader->set();
565             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
566             glEnable(GL_BLEND);
567             if(overlaid)
568             {
569                 if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false);
570                 gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1));
571                 glBindTexture(GL_TEXTURE_2D, overlaytex->id);
572                 rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0);
573             }
574             if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
575         }
576         return layout(size+guishadow, size+guishadow);
577     }
578 
slicegui579     int slice(Texture *t, float scale, float start, float end, const char *text)
580     {
581         if(scale == 0) scale = 1;
582         int size = (int)(scale*2*FONTH);
583         if(t!=notexture && visible()) slice_(t, curx, cury, size, start, end, text);
584         return layout(size, size);
585     }
586 
progressgui587     void progress(float percent, float scale)
588     {
589         if(scale == 0) scale = 1;
590         int size = (int)(scale*2*FONTH);
591         slice_(textureload(hud::progringtex, 3, true, false), curx, cury, size, (SDL_GetTicks()%1000)/1000.f, 0.1f);
592         string s; if(percent > 0) formatstring(s, "\fg%d%%", int(percent*100)); else formatstring(s, "\fg...");
593         slice_(textureload(hud::progresstex, 3, true, false), curx, cury, size, 0, percent, s);
594         layout(size, size);
595     }
596 
slidergui597     int slider(int &val, int vmin, int vmax, int colour, const char *label, bool reverse, bool scroll, int style, int scolour)
598     {
599         int x = curx, y = cury;
600         float percent = (val-vmin)/float(max(vmax-vmin, 1));
601         bool hit = false;
602         int space = slider_(guislidersize, percent, ishorizontal() ? FONTW*3 : FONTH, hit, style, scolour);
603         if(visible())
604         {
605             if(hit)
606             {
607                 if(!label) label = intstr(val);
608                 settooltip("\f[%d]%s", -1, colour, label);
609                 tooltipforce = true;
610                 if(mouseaction[0]&GUI_PRESSED)
611                 {
612                     int vnew = vmax-vmin+1;
613                     if(ishorizontal()) vnew = int((vnew*(reverse ? hity-y-guislidersize/2 : y+ysize-guislidersize/2-hity))/(ysize-guislidersize));
614                     else vnew = int((vnew*(reverse ? x+xsize-guislidersize/2-hitx : hitx-x-guislidersize/2))/(xsize-guislidersize));
615                     vnew += vmin;
616                     vnew = clamp(vnew, vmin, vmax);
617                     if(vnew != val) val = vnew;
618                 }
619                 else if(mouseaction[1]&GUI_UP)
620                 {
621                     int vval = val+(reverse == !(mouseaction[1]&GUI_ALT) ? -1 : 1),
622                         vnew = clamp(vval, vmin, vmax);
623                     if(vnew != val) val = vnew;
624                 }
625             }
626             else if(scroll && lists[curlist].mouse[1]&GUI_UP)
627             {
628                 int vval = val+(reverse == !(lists[curlist].mouse[1]&GUI_ALT) ? -1 : 1),
629                     vnew = clamp(vval, vmin, vmax);
630                 if(vnew != val) val = vnew;
631             }
632         }
633         return space;
634     }
635 
fieldgui636     char *field(const char *name, int color, int length, int height, const char *initval, int initmode, bool focus, const char *parent, const char *prompt, bool immediate)
637     {
638         return field_(name, color, length, height, initval, initmode, FIELDEDIT, focus, parent, prompt, immediate);
639     }
640 
keyfieldgui641     char *keyfield(const char *name, int color, int length, int height, const char *initval, int initmode, bool focus, const char *parent, const char *prompt, bool immediate)
642     {
643         return field_(name, color, length, height, initval, initmode, FIELDKEY, focus, parent, prompt, immediate);
644     }
645 
field_gui646     char *field_(const char *name, int color, int length, int height, const char *initval, int initmode, int fieldtype = FIELDEDIT, bool focus = false, const char *parent = NULL, const char *prompt = NULL, bool immediate = false)
647     {
648         editor *e = useeditor(name, initmode, false, initval, parent); // generate a new editor if necessary
649         if(guilayoutpass)
650         {
651             if(initval && (e->lines.empty() || (e->mode == EDITORFOCUSED && (e != currentfocus() || fieldmode == FIELDSHOW) && strcmp(e->lines[0].text, initval))))
652                 e->clear(initval);
653             e->linewrap = (length < 0);
654             e->maxx = e->linewrap ? -1 : length;
655             e->maxy = (height <= 0) ? 1 : -1;
656             e->pixelwidth = (abs(length)+1)*FONTW;
657             if(e->linewrap && e->maxy == 1)
658             {
659                 int temp = 0;
660                 text_bounds(e->lines[0].text, temp, e->pixelheight, 0, 0, e->pixelwidth, TEXT_NO_INDENT); // only single line editors can have variable height
661                 int ph = e->pixelheight%FONTH;
662                 if(ph) e->pixelheight += FONTH-ph;
663             }
664             else e->pixelheight = max(height, 1)*FONTH;
665         }
666         int hpad = FONTH/4, wpad = FONTW, h = e->pixelheight+hpad, w = e->pixelwidth+wpad;
667 
668         bool wasvertical = isvertical();
669         if(wasvertical && e->maxy != 1) pushlist(false);
670 
671         char *result = NULL;
672         if(visible())
673         {
674             e->rendered = true;
675             if(focus && e->unfocus) focus = false;
676             bool hit = ishit(w, h) && e->mode != EDITORREADONLY, clrs = fieldtype == FIELDKEY,
677                  editing = (fieldmode != FIELDSHOW) && e == currentfocus() && e->mode != EDITORREADONLY;
678             if(mouseaction[0]&GUI_UP && mergedepth >= 0 && hit) mouseaction[0] &= ~GUI_UP;
679             if(mouseaction[0]&GUI_DOWN) //mouse request focus
680             {
681                 if(hit)
682                 {
683                     focus = true;
684                     if(mouseaction[0]&GUI_ALT) clrs = true;
685                     if(e->unfocus) e->unfocus = false;
686                 }
687                 else if(editing) fieldmode = e->mode != EDITORREADONLY && e->mode != EDITORFOREVER ? FIELDCOMMIT : FIELDSHOW;
688             }
689             if(focus)
690             {
691                 if(clrs) e->clear();
692                 useeditor(e->name, initmode, true, initval, parent);
693                 e->mark(false);
694                 if(fieldmode != FIELDCOMMIT && fieldmode != FIELDABORT) fieldmode = fieldtype;
695             }
696             if(hit && editing && (mouseaction[0]&GUI_PRESSED) && fieldtype == FIELDEDIT)
697                 e->hit(int(floor(hitx-(curx+wpad/2))), int(floor(hity-(cury+hpad/2))), (mouseaction[0]&GUI_DRAGGED)!=0); //mouse request position
698             if(editing && (fieldmode == FIELDCOMMIT || fieldmode == FIELDABORT)) // commit field if user pressed enter
699             {
700                 if(fieldmode == FIELDCOMMIT) result = e->currentline().text;
701                 e->active = (e->mode != EDITORFOCUSED);
702                 fieldmode = FIELDSHOW;
703             }
704             else
705             {
706                 if(editing && immediate) result = e->currentline().text;
707                 fieldsactive = true;
708             }
709             skin(curx, cury, curx+w, cury+h, guifieldbgcolour, guifieldbgblend, editing ? guifieldactivecolour : (hit ? guifieldhitcolour : guifieldbordercolour), editing ? guifieldactiveblend : (hit ? guifieldhitblend : guifieldborderblend), true);
710             e->draw(curx+wpad/2, cury+hpad/2, color, editing, prompt);
711             if(hit && !guicursortype) guicursortype = 2;
712         }
713         else if(e->unfocus) e->unfocus = false;
714         layout(w, h);
715         if(e->maxy != 1)
716         {
717             int slines = e->limitscrolly();
718             if(slines > 0)
719             {
720                 int oldpos = e->scrolly == editor::SCROLLEND ? slines : e->scrolly, newpos = oldpos;
721                 slider(newpos, 0, slines, color, NULL, true, true, 0, -1);
722                 if(oldpos != newpos)
723                 {
724                     e->cy = newpos;
725                     e->scrolly = e->mode == EDITORREADONLY && newpos >= slines ? editor::SCROLLEND : newpos;
726                 }
727             }
728             if(wasvertical) poplist();
729         }
730         return result;
731     }
732 
rect_gui733     void rect_(float x, float y, float w, float h, bool lines = false)
734     {
735         gle::defvertex(2);
736         gle::begin(lines ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
737         gle::attribf(x, y);
738         gle::attribf(x + w, y);
739         if(lines) gle::attribf(x + w, y + h);
740         gle::attribf(x, y + h);
741         if(!lines) gle::attribf(x + w, y + h);
742         xtraverts += gle::end();
743     }
744 
rect_gui745     void rect_(float x, float y, float w, float h, int usetc)
746     {
747         gle::defvertex(2);
748         gle::deftexcoord0();
749         gle::begin(GL_TRIANGLE_STRIP);
750         static const vec2 tc[5] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1), vec2(0, 0) };
751         gle::attribf(x, y); gle::attrib(tc[usetc]);
752         gle::attribf(x + w, y); gle::attrib(tc[usetc+1]);
753         gle::attribf(x, y + h); gle::attrib(tc[usetc+3]);
754         gle::attribf(x + w, y + h); gle::attrib(tc[usetc+2]);
755         xtraverts += gle::end();
756     }
757 
text_gui758     void text_(const char *text, int x, int y, int color, int alpha, bool hit = false, int wrap = -1)
759     {
760         int flags = TEXT_NO_INDENT;
761         if(hit)
762         {
763             if(guiactiveskin && skinfx)
764             {
765                 flags |= TEXT_SKIN;
766                 color = guiactivecolour;
767             }
768             else if(hitfx)
769             {
770                 color = guiactivecolour;
771                 if(cursorfx && !guicursortype) guicursortype = 1;
772             }
773         }
774         draw_textf("%s", x, y, 0, 0, color>>16, (color>>8)&0xFF, color&0xFF, alpha, flags, -1, wrap > 0 ? wrap : -1, 1, text);
775     }
776 
fillgui777     void fill(int color, int inheritw, int inherith)
778     {
779         if(!visible()) return;
780         hudnotextureshader->set();
781         gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80);
782         int w = xsize, h = ysize;
783         if(inheritw>0)
784         {
785             int parentw = curlist, parentdepth = 0;
786             for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++)
787                 parentw = lists[parentw].parent;
788             list &p = lists[parentw];
789             w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w;
790         }
791         if(inherith>0)
792         {
793             int parenth = curlist, parentdepth = 0;
794             for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++)
795                 parenth = lists[parenth].parent;
796             list &p = lists[parenth];
797             h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h;
798         }
799         rect_(curx, cury, w, h, false);
800         hudshader->set();
801     }
802 
outlinegui803     void outline(int color, int inheritw, int inherith, int offsetx, int offsety)
804     {
805         if(!visible()) return;
806         hudnotextureshader->set();
807         gle::colorub(color>>16, (color>>8)&0xFF, color&0xFF, 0x80);
808         int w = xsize, h = ysize;
809         if(inheritw>0)
810         {
811             int parentw = curlist, parentdepth = 0;
812             for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++)
813                 parentw = lists[parentw].parent;
814             list &p = lists[parentw];
815             w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w;
816         }
817         if(inherith>0)
818         {
819             int parenth = curlist, parentdepth = 0;
820             for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++)
821                 parenth = lists[parenth].parent;
822             list &p = lists[parenth];
823             h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h;
824         }
825         rect_(curx+offsetx, cury+offsety, w-offsetx*2, h-offsety*2, true);
826         hudshader->set();
827     }
828 
backgroundgui829     void background(int colour1, float blend1, int colour2, float blend2, bool skinborder, int inheritw, int inherith)
830     {
831         if(!visible()) return;
832         int w = xsize, h = ysize;
833         if(inheritw>0)
834         {
835             int parentw = curlist, parentdepth = 0;
836             for(;parentdepth < inheritw && lists[parentw].parent>=0; parentdepth++)
837                 parentw = lists[parentw].parent;
838             list &p = lists[parentw];
839             w = p.springs > 0 && (curdepth-parentdepth)&1 ? lists[p.parent].w : p.w;
840         }
841         if(inherith>0)
842         {
843             int parenth = curlist, parentdepth = 0;
844             for(;parentdepth < inherith && lists[parenth].parent>=0; parentdepth++)
845                 parenth = lists[parenth].parent;
846             list &p = lists[parenth];
847             h = p.springs > 0 && !((curdepth-parentdepth)&1) ? lists[p.parent].h : p.h;
848         }
849         skin(curx, cury, curx+w, cury+h, colour1, blend1, colour2, blend2, skinborder);
850     }
851 
icon_gui852     void icon_(Texture *t, bool overlaid, int x, int y, int size, bool hit, int icolour = 0xFFFFFF, Texture *o = NULL, int ocolor = 0xFFFFFF)
853     {
854         static const vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
855         float xs = 0, ys = 0;
856         int textureid = -1;
857         if(t)
858         {
859             float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio
860             xs = t->xs*scale;
861             ys = t->ys*scale;
862             x += int((size-xs)/2);
863             y += int((size-ys)/2);
864             textureid = t->id;
865         }
866         else
867         {
868             if(lightmapping && lmprogtex)
869             {
870                 float scale = float(size)/256; //scale and preserve aspect ratio
871                 xs = 256*scale; ys = 256*scale;
872                 x += int((size-xs)/2);
873                 y += int((size-ys)/2);
874                 textureid = lmprogtex;
875             }
876             else
877             {
878                 defformatstring(texname, "%s", mapname);
879                 if((t = textureload(texname, 3, true, false)) == notexture) t = textureload(emblemtex, 3, true, false);
880                 float scale = float(size)/max(t->xs, t->ys); //scale and preserve aspect ratio
881                 xs = t->xs*scale; ys = t->ys*scale;
882                 x += int((size-xs)/2);
883                 y += int((size-ys)/2);
884                 textureid = t->id;
885             }
886         }
887         float xi = x, yi = y, xpad = 0, ypad = 0;
888         if(overlaid)
889         {
890             xpad = xs/32;
891             ypad = ys/32;
892             xi += xpad;
893             yi += ypad;
894             xs -= 2*xpad;
895             ys -= 2*ypad;
896         }
897         gle::defvertex(2);
898         gle::deftexcoord0();
899         if(hit && hitfx)
900         {
901             float offx = xs*guihoverscale, offy = ys*guihoverscale;
902             if(!hovertex) hovertex = textureload(guihovertex, 3, true, false);
903             glBindTexture(GL_TEXTURE_2D, hovertex->id);
904             gle::color(vec::hexcolor(guihovercolour), guihoverblend);
905             gle::begin(GL_TRIANGLE_STRIP);
906             gle::attribf(xi-offx,    yi-offy);    gle::attrib(tc[0]);
907             gle::attribf(xi+xs+offx, yi-offy);    gle::attrib(tc[1]);
908             gle::attribf(xi-offx,    yi+ys+offy); gle::attrib(tc[3]);
909             gle::attribf(xi+xs+offx, yi+ys+offy); gle::attrib(tc[2]);
910             xtraverts += gle::end();
911         }
912         glBindTexture(GL_TEXTURE_2D, textureid);
913         gle::color(vec::hexcolor(icolour), 1.f);
914         gle::begin(GL_TRIANGLE_STRIP);
915         gle::attribf(xi,    yi);    gle::attrib(tc[0]);
916         gle::attribf(xi+xs, yi);    gle::attrib(tc[1]);
917         gle::attribf(xi,    yi+ys); gle::attrib(tc[3]);
918         gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]);
919         xtraverts += gle::end();
920         if(o)
921         {
922             glBindTexture(GL_TEXTURE_2D, o->id);
923             gle::color(vec::hexcolor(ocolor), 1.f);
924             gle::begin(GL_TRIANGLE_STRIP);
925             gle::attribf(xi,    yi);    gle::attrib(tc[0]);
926             gle::attribf(xi+xs, yi);    gle::attrib(tc[1]);
927             gle::attribf(xi,    yi+ys); gle::attrib(tc[3]);
928             gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]);
929             xtraverts += gle::end();
930         }
931         if(overlaid)
932         {
933             if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false);
934             gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1), 1.f);
935             glBindTexture(GL_TEXTURE_2D, overlaytex->id);
936             rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0);
937         }
938         if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
939     }
940 
previewslotgui941     void previewslot(VSlot &vslot, bool overlaid, int x, int y, int size, bool hit)
942     {
943         Slot &slot = *vslot.slot;
944         if(slot.sts.empty()) return;
945         VSlot *layer = NULL;
946         Texture *t = NULL, *glowtex = NULL, *layertex = NULL;
947         if(slot.loaded)
948         {
949             t = slot.sts[0].t;
950             if(t == notexture) return;
951             Slot &slot = *vslot.slot;
952             if(slot.texmask&(1<<TEX_GLOW)) { loopvj(slot.sts) if(slot.sts[j].type==TEX_GLOW) { glowtex = slot.sts[j].t; break; } }
953             if(vslot.layer)
954             {
955                 layer = &lookupvslot(vslot.layer);
956                 if(!layer->slot->sts.empty()) layertex = layer->slot->sts[0].t;
957             }
958         }
959         else if(slot.thumbnail && slot.thumbnail != notexture) t = slot.thumbnail;
960         else return;
961         float xt = min(1.0f, t->xs/(float)t->ys), yt = min(1.0f, t->ys/(float)t->xs), xs = size, ys = size;
962         float xi = x, yi = y, xpad = 0, ypad = 0;
963         if(overlaid)
964         {
965             xpad = xs/32;
966             ypad = ys/32;
967             xi += xpad;
968             yi += ypad;
969             xs -= 2*xpad;
970             ys -= 2*ypad;
971         }
972         SETSHADER(hudrgb);
973         gle::defvertex(2);
974         gle::deftexcoord0();
975         const vec &color = hit && hitfx && !overlaid ? vec(1.25f, 1.25f, 1.25f) : vec(1, 1, 1);
976         vec2 tc[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
977         int xoff = vslot.offset.x, yoff = vslot.offset.y;
978         if(vslot.rotation)
979         {
980             if((vslot.rotation&5) == 1) { swap(xoff, yoff); loopk(4) swap(tc[k].x, tc[k].y); }
981             if(vslot.rotation >= 2 && vslot.rotation <= 4) { xoff *= -1; loopk(4) tc[k].x *= -1; }
982             if(vslot.rotation <= 2 || vslot.rotation == 5) { yoff *= -1; loopk(4) tc[k].y *= -1; }
983         }
984         loopk(4) { tc[k].x = tc[k].x/xt - xoff/t->xs; tc[k].y = tc[k].y/yt - yoff/t->ys; }
985         if(slot.loaded) gle::color(vec(color).mul(vslot.getcolorscale()));
986         else gle::color(color);
987         glBindTexture(GL_TEXTURE_2D, t->id);
988         gle::begin(GL_TRIANGLE_STRIP);
989         gle::attribf(xi,    yi);    gle::attrib(tc[0]);
990         gle::attribf(xi+xs, yi);    gle::attrib(tc[1]);
991         gle::attribf(xi,    yi+ys); gle::attrib(tc[3]);
992         gle::attribf(xi+xs, yi+ys); gle::attrib(tc[2]);
993         gle::end();
994         if(glowtex)
995         {
996             glBlendFunc(GL_SRC_ALPHA, GL_ONE);
997             glBindTexture(GL_TEXTURE_2D, glowtex->id);
998             vec glowcolor = vslot.getglowcolor();
999             if(hit || overlaid) gle::color(vec(glowcolor).mul(color));
1000             else gle::color(glowcolor);
1001             gle::begin(GL_TRIANGLE_STRIP);
1002             gle::attribf(x,    y);    gle::attrib(tc[0]);
1003             gle::attribf(x+xs, y);    gle::attrib(tc[1]);
1004             gle::attribf(x,    y+ys); gle::attrib(tc[3]);
1005             gle::attribf(x+xs, y+ys); gle::attrib(tc[2]);
1006             gle::end();
1007             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1008         }
1009         if(layertex)
1010         {
1011             glBindTexture(GL_TEXTURE_2D, layertex->id);
1012             gle::color(vec(color).mul(layer->getcolorscale()));
1013             gle::begin(GL_TRIANGLE_STRIP);
1014             gle::attribf(x+xs/2, y+ys/2); gle::attrib(tc[0]);
1015             gle::attribf(x+xs,   y+ys/2); gle::attrib(tc[1]);
1016             gle::attribf(x+xs/2, y+ys);   gle::attrib(tc[3]);
1017             gle::attribf(x+xs,   y+ys);   gle::attrib(tc[2]);
1018             gle::end();
1019         }
1020 
1021         hudshader->set();
1022         if(overlaid)
1023         {
1024             if(!overlaytex) overlaytex = textureload(guioverlaytex, 3, true, false);
1025             gle::color(hit && hitfx ? vec::hexcolor(guiactivecolour) : vec(1, 1, 1));
1026             glBindTexture(GL_TEXTURE_2D, overlaytex->id);
1027             rect_(xi - xpad, yi - ypad, xs + 2*xpad, ys + 2*ypad, 0);
1028         }
1029         if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
1030     }
1031 
slice_gui1032     void slice_(Texture *t, int x, int y, int size, float start = 0, float end = 1, const char *text = NULL)
1033     {
1034         float scale = float(size)/max(t->xs, t->ys), xs = t->xs*scale, ys = t->ys*scale, fade = 1;
1035         if(start == end) { end = 1; fade = 0.5f; }
1036         glBindTexture(GL_TEXTURE_2D, t->id);
1037         gle::colorf(1, 1, 1, fade);
1038         int s = max(xs,ys)/2;
1039         drawslice(start, end, x+s/2, y+s/2, s);
1040         if(text && *text)
1041         {
1042             int w = int(ceil(text_widthf(text, 0, 0, TEXT_NO_INDENT)));
1043             text_(text, x+s/2-w/2, y+s/2-FONTH/2, 0xFFFFFF, guitextblend);
1044         }
1045     }
1046 
slider_gui1047     int slider_(int size, float percent, int space, bool &hit, int style, int colour)
1048     {
1049         space = max(max(space, FONTW), size);
1050         if(visible())
1051         {
1052             int x = ishorizontal() ? curx+space/2-size/2 : curx, w = ishorizontal() ? size : xsize,
1053                 y = ishorizontal() ? cury : cury+space/2-size/2, h = ishorizontal() ? ysize : size;
1054             hit = ishit(w, h, x, y);
1055             skin(x, y, x+w, y+h, guislidercolour, guisliderblend, hit ? guislideractivecolour : guisliderbordercolour, hit ? guislideractiveblend : guisliderborderblend, guisliderborderskin >= (hit ? 1 : 2));
1056             if(percent >= 0 && percent <= 1)
1057             {
1058                 int px = x+size/8, py = y+size/8, pw = size*3/4, ph = size*3/4;
1059                 switch(style)
1060                 {
1061                     case 1:
1062                         if(ishorizontal()) ph = (h-size/4)*percent;
1063                         else pw = (w-size/4)*percent;
1064                         break;
1065                     case 0: default:
1066                         if(ishorizontal()) py += (h-size)*percent;
1067                         else px += (w-size)*percent;
1068                         break;
1069                 }
1070                 skin(px, py, px+pw, py+ph, colour >= 0 ? colour : guislidermarkcolour, guislidermarkblend, hit ? guislideractivecolour : guislidermarkbordercolour, hit ? guislideractiveblend : guislidermarkborderblend, guislidermarkborderskin >= (hit ? 1 : 2));
1071             }
1072             if(hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
1073         }
1074         layout(ishorizontal() ? space : 0, ishorizontal() ? 0 : space);
1075         return space;
1076     }
1077 
line_gui1078     int line_(int size, int space = 0, int colour = -1, int border = -1)
1079     {
1080         space = max(max(space, FONTW), size);
1081         if(visible())
1082         {
1083             int colour1 = colour >= 0 ? colour : (guibgcolour >= 0 ? guibgcolour : (guibordercolour >= 0 ? guibordercolour : 0x000000)),
1084                 colour2 = border >= 0 ? border : (guibordercolour >= 0 ? guibordercolour : 0x808080),
1085                 x1 = ishorizontal() ? curx+space/2-size/2 : curx, x2 = x1+(ishorizontal() ? size : xsize),
1086                 y1 = ishorizontal() ? cury : cury+space/2-size/2, y2 = y1+(ishorizontal() ? ysize : size);
1087             if(colour1 >= 0)
1088             {
1089                 hudnotextureshader->set();
1090                 gle::color(vec::hexcolor(colour1), guibgblend);
1091                 gle::defvertex(2);
1092                 gle::begin(GL_TRIANGLE_STRIP);
1093                 gle::attribf(x1, y1);
1094                 gle::attribf(x2, y1);
1095                 gle::attribf(x1, y2);
1096                 gle::attribf(x2, y2);
1097                 xtraverts += gle::end();
1098                 hudshader->set();
1099             }
1100             if(colour2 >= 0)
1101             {
1102                 hudnotextureshader->set();
1103                 gle::color(vec::hexcolor(colour2), guiborderblend);
1104                 gle::defvertex(2);
1105                 gle::begin(GL_LINE_LOOP);
1106                 gle::attribf(x1, y1);
1107                 gle::attribf(x2, y1);
1108                 gle::attribf(x2, y2);
1109                 gle::attribf(x1, y2);
1110                 xtraverts += gle::end();
1111                 hudshader->set();
1112             }
1113         }
1114         layout(ishorizontal() ? space : 0, ishorizontal() ? 0 : space);
1115         return space;
1116     }
1117 
button_gui1118     int button_(const char *text, int color, const char *icon, int icolour, bool clickable, int wrap = -1, bool faded = true, const char *oicon = NULL, int ocolor = 0xFFFFFF)
1119     {
1120         int w = 0, h = 0;
1121         if((icon && *icon) || (oicon && *oicon))
1122         {
1123             w += FONTH;
1124             if(text && *text) w += 8;
1125         }
1126         if(text && *text)
1127         {
1128             int tw = 0, th = 0;
1129             text_bounds(text, tw, th, 0, 0, wrap > 0 ? wrap : -1, TEXT_NO_INDENT);
1130             w += tw;
1131             h += th;
1132         }
1133         else h = FONTH;
1134 
1135         if(visible())
1136         {
1137             bool hit = ishit(w, h);
1138             int x = curx;
1139             if((icon && *icon) || (oicon && *oicon))
1140             {
1141                 Texture *ttex = icon && *icon ? textureload(strstr(icon, "textures/") ? icon : makerelpath("textures", icon), 3, true, false) : NULL,
1142                     *otex = oicon && *oicon ? textureload(strstr(oicon, "textures/") ? oicon : makerelpath("textures", oicon), 3, true, false) : NULL;
1143                 if(otex == notexture) otex = NULL;
1144                 icon_(ttex, false, x, cury, FONTH, clickable && hit, icolour, otex, ocolor);
1145                 x += FONTH;
1146                 if(text && *text) x += 8;
1147             }
1148             if(text && *text) text_(text, x, cury, color, (hit && hitfx) || !faded || !clickable ? guitextblend : guitextfade, hit && clickable, wrap > 0 ? wrap : -1);
1149             if(clickable && hit && hitfx && cursorfx && !guicursortype) guicursortype = 1;
1150         }
1151         return layout(w, h);
1152     }
1153 
1154     static Texture *skintex, *skinbordertex, *overlaytex, *exittex, *hovertex;
1155 
1156     vec uiorigin, uiscale;
1157     guicb *cb;
1158 
1159     static float basescale, maxscale;
1160     static bool passthrough;
1161 
adjustscalegui1162     void adjustscale()
1163     {
1164         int w = xsize+FONTW*8, h = ysize+FONTH*8;
1165         float aspect = forceaspect ? 1.0f/forceaspect : float(screenh)/float(screenw), fit = 1.0f;
1166         if(w*aspect*basescale>1.0f) fit = 1.0f/(w*aspect*basescale);
1167         if(h*basescale*fit>maxscale) fit *= maxscale/(h*basescale*fit);
1168         uiscale = vec(aspect*uiscale.x*fit, uiscale.y*fit, 1);
1169         uiorigin = vec(0.5f - ((w-xsize)/2 - (FONTW*4))*uiscale.x, 0.5f + (0.5f*h-(FONTH*4))*uiscale.y, 0);
1170     }
1171 
startgui1172     void start(int starttime, int *tab, bool allowinput, bool wantstitle, bool wantsbgfx)
1173     {
1174         fontdepth = 0;
1175         gui::pushfont("default");
1176         basescale = guiscale;
1177         if(guilayoutpass)
1178             uiscale.x = uiscale.y = uiscale.z = guiscaletime ? min(basescale*(totalmillis-starttime)/float(guiscaletime), basescale) : basescale;
1179         needsinput = allowinput;
1180         hastitle = wantstitle;
1181         hasbgfx = wantsbgfx;
1182         passthrough = !allowinput;
1183         curdepth = curlist = mergedepth = mergelist = -1;
1184         tpos = ty = 0;
1185         tx = -FONTW;
1186         tcurrent = tab;
1187         tcolor = 0xFFFFFF;
1188         pushlist(false);
1189         if(guilayoutpass) nextlist = curlist;
1190         else
1191         {
1192             if(tcurrent && !*tcurrent) tcurrent = NULL;
1193             cury = -ysize;
1194             curx = -xsize/2;
1195 
1196             hudmatrix.ortho(0, 1, 1, 0, -1, 1);
1197             hudmatrix.translate(uiorigin);
1198             hudmatrix.scale(uiscale);
1199 
1200             resethudmatrix();
1201             hudshader->set();
1202 
1203             if(hasbgfx)
1204             {
1205                 int x1 = curx-FONTW, y1 = cury-FONTH/2, x2 = x1+xsize+FONTW*2, y2 = y1+ysize+FONTH;
1206                 skin(x1, y1, x2, y2, guibgcolour, guibgblend, guibordercolour, guiborderblend);
1207             }
1208         }
1209     }
1210 
endgui1211     void end()
1212     {
1213         if(guilayoutpass)
1214         {
1215             if(needsinput) uibuttons();
1216             xsize = max(tx, xsize);
1217             ysize = max(ty, ysize);
1218             ysize = max(ysize, FONTH);
1219 
1220             if(tcurrent) *tcurrent = max(1, min(*tcurrent, tpos));
1221             adjustscale();
1222 
1223             if(!passthrough && fieldmode != FIELDKEY)
1224             {
1225                 hitx = (cursorx - uiorigin.x)/uiscale.x;
1226                 hity = (cursory - uiorigin.y)/uiscale.y;
1227                 if((mouseaction[0]&GUI_PRESSED) && (fabs(hitx-firstx) > 2 || fabs(hity - firsty) > 2)) mouseaction[0] |= GUI_DRAGGED;
1228             }
1229         }
1230         else
1231         {
1232             if(guistatusline && statusstr && *statusstr)
1233             {
1234                 gui::pushfont("reduced");
1235                 int width = 0, height = 0, tw = min(statuswidth ? statuswidth : (guistatuswidth ? guistatuswidth : -1), int(screenw*(1/uiscale.y))-FONTH*4);
1236                 text_bounds(statusstr, width, height, 0, 0, tw, TEXT_CENTERED|TEXT_NO_INDENT);
1237                 int w = width+FONTW*2, h = height+FONTH/2, x1 = -w/2, y1 = guispacesize, x2 = x1+w, y2 = y1+h;
1238                 if(hasbgfx) skin(x1, y1, x2, y2, guibgcolour, guibgblend, guibordercolour, guiborderblend);
1239                 draw_text(statusstr, x1+FONTW, y1+FONTH/4, 255, 255, 255, 255, TEXT_CENTERED|TEXT_NO_INDENT, -1, tw);
1240                 gui::popfont();
1241             }
1242             if(needsinput) uibuttons();
1243             if(!passthrough && fieldmode != FIELDKEY && (guitooltips || tooltipforce) && tooltipstr && *tooltipstr)
1244             {
1245                 if(!tooltip || !lasttooltip || strcmp(tooltip, tooltipstr))
1246                 {
1247                     if(tooltip) DELETEA(tooltip);
1248                     tooltip = newstring(tooltipstr);
1249                     lasttooltip = totalmillis ? totalmillis : 1;
1250                 }
1251                 if(tooltipforce || totalmillis-lasttooltip >= guitooltiptime)
1252                 {
1253                     gui::pushfont("little");
1254                     int width, height, tw = min(tooltipwidth ? tooltipwidth : (guitooltipwidth ? guitooltipwidth : -1), int(screenw*(1/uiscale.y))-FONTH*4);
1255                     text_bounds(tooltipstr, width, height, 0, 0, tw, TEXT_NO_INDENT);
1256                     int w = width+FONTW*2, h = FONTH/2+height, x1 = hitx, y1 = hity-height-FONTH/2, x2 = x1+w, y2 = y1+h,
1257                         offset = totalmillis-lasttooltip-guitooltiptime;
1258                     float blend = tooltipforce ? 1.f : (offset > 0 ? (offset < guitooltipfade ? offset/float(guitooltipfade) : 1.f) : 0.f);
1259                     skin(x1, y1, x2, y2, guitooltipcolour, guitooltipblend*blend, guitooltipbordercolour, guitooltipborderblend*blend, guitooltipborderskin!=0);
1260                     draw_text(tooltip, x1+FONTW, y1+FONTH/4, 255, 255, 255, int(255*blend), TEXT_NO_INDENT, -1, tw);
1261                     gui::popfont();
1262                 }
1263             }
1264             else
1265             {
1266                 if(tooltip) DELETEA(tooltip);
1267                 lasttooltip = 0;
1268             }
1269         }
1270         poplist();
1271         while(fontdepth) gui::popfont();
1272     }
1273 };
1274 
1275 Texture *gui::skintex = NULL, *gui::skinbordertex, *gui::overlaytex = NULL, *gui::exittex = NULL, *gui::hovertex = NULL;
1276 TVARN(IDF_PERSIST|IDF_PRELOAD, guiskintex, "textures/guiskin", gui::skintex, 0);
1277 TVARN(IDF_PERSIST|IDF_PRELOAD, guiskinbordertex, "textures/guiskinborder", gui::skinbordertex, 0);
1278 TVARN(IDF_PERSIST|IDF_PRELOAD, guioverlaytex, "textures/guioverlay", gui::overlaytex, 0);
1279 TVARN(IDF_PERSIST|IDF_PRELOAD, guiexittex, "textures/guiexit", gui::exittex, 0);
1280 TVARN(IDF_PERSIST|IDF_PRELOAD, guihovertex, "textures/guihover", gui::hovertex, 0);
1281 
1282 vector<gui::list> gui::lists;
1283 float gui::basescale, gui::maxscale = 1, gui::hitx, gui::hity;
1284 bool gui::passthrough, gui::hitfx = true, gui::cursorfx = true, gui::skinfx = true;
1285 int gui::curdepth, gui::fontdepth, gui::curlist, gui::xsize, gui::ysize, gui::curx, gui::cury, gui::mergelist, gui::mergedepth;
1286 int gui::ty, gui::tx, gui::tpos, *gui::tcurrent, gui::tcolor;
1287 static vector<gui> guis;
1288 
1289 namespace UI
1290 {
1291     bool isopen = false;
1292 
textinput(const char * str,int len)1293     bool textinput(const char *str, int len)
1294     {
1295         editor *e = currentfocus();
1296         if(fieldmode == FIELDKEY || fieldmode == FIELDSHOW || !e || e->mode == EDITORREADONLY) return false;
1297 
1298         e->input(str, len);
1299         return true;
1300     }
1301 
keypress(int code,bool isdown)1302     bool keypress(int code, bool isdown)
1303     {
1304         editor *e = currentfocus();
1305         if(fieldmode == FIELDKEY && e && e->mode != EDITORREADONLY)
1306         {
1307             switch(code)
1308             {
1309                 case SDLK_ESCAPE:
1310                     if(isdown)
1311                     {
1312                         fieldmode = FIELDCOMMIT;
1313                         e->unfocus = true;
1314                     }
1315                     return true;
1316             }
1317             const char *keyname = getkeyname(code);
1318             if(keyname && isdown)
1319             {
1320                 if(e->lines.length()!=1 || !e->lines[0].empty()) e->insert(" ");
1321                 e->insert(keyname);
1322             }
1323             return true;
1324         }
1325 
1326         if(code < 0) switch(code)
1327         { // fall-through-o-rama
1328             case -5: mouseaction[1] |= GUI_ALT;
1329             case -4: mouseaction[1] |= isdown ? GUI_DOWN : GUI_UP;
1330                 if(active()) return true;
1331                 break;
1332             case -3: mouseaction[0] |= GUI_ALT;
1333             case -1: mouseaction[0] |= (guiactionon = isdown) ? GUI_DOWN : GUI_UP;
1334                 if(isdown)
1335                 {
1336                     firstx = gui::hitx;
1337                     firsty = gui::hity;
1338                 }
1339                 if(active()) return true;
1340                 break;
1341             case -2:
1342                 if(isdown) cleargui(1);
1343                 if(active()) return true;
1344                 break;
1345         }
1346 
1347         if(fieldmode == FIELDSHOW || !e || e->mode == EDITORREADONLY) return false;
1348         switch(code)
1349         {
1350             case SDLK_ESCAPE: //cancel editing without commit
1351                 if(isdown)
1352                 {
1353                     fieldmode = FIELDABORT;
1354                     e->unfocus = true;
1355                 }
1356                 return e->mode != EDITORFOREVER;
1357             case SDLK_RETURN:
1358             case SDLK_TAB:
1359                 if(e->maxy != 1) break;
1360             case SDLK_KP_ENTER:
1361                 if(isdown) fieldmode = FIELDCOMMIT; //signal field commit (handled when drawing field)
1362                 return true;
1363             case SDLK_PAGEUP:
1364             case SDLK_PAGEDOWN:
1365             case -4:
1366             case -5:
1367                 if(e->parent && *e->parent)
1368                 { // pass input on to parent
1369                     editor *f = findeditor(e->parent);
1370                     if(f) e = f;
1371                 }
1372                 break;
1373         }
1374         if(isdown) e->key(code);
1375         return true;
1376     }
1377 
active(bool pass)1378     bool active(bool pass) { return guis.length() && (!pass || needsinput); }
limitscale(float scale)1379     void limitscale(float scale) {  gui::maxscale = scale; }
1380 
addcb(guicb * cb)1381     void addcb(guicb *cb)
1382     {
1383         gui &g = guis.add();
1384         g.cb = cb;
1385         g.adjustscale();
1386     }
1387 
update()1388     void update()
1389     {
1390         bool p = active(false);
1391         if(isopen != p) uimillis = (isopen = p) ? totalmillis : -totalmillis;
1392         setsvar("guirollovername", "", true);
1393         setsvar("guirolloveraction", "", true);
1394         setsvar("guirollovertype", "", true);
1395     }
1396 
render()1397     void render()
1398     {
1399         float oldtextscale = curtextscale;
1400         curtextscale = 1;
1401         if(guiactionon) mouseaction[0] |= GUI_PRESSED;
1402 
1403         gui::reset();
1404         guis.shrink(0);
1405 
1406         // call all places in the engine that may want to render a gui from here, they call addcb()
1407         if(progressing) progressmenu();
1408         else
1409         {
1410             texturemenu();
1411             hud::gamemenus();
1412             mainmenu();
1413         }
1414 
1415         readyeditors();
1416         bool wasfocused = (fieldmode!=FIELDSHOW);
1417         fieldsactive = false;
1418 
1419         needsinput = false;
1420         hastitle = hasbgfx = true;
1421 
1422         if(!guis.empty())
1423         {
1424             guicursortype = 0;
1425             guilayoutpass = 1;
1426             //loopv(guis) guis[i].cb->gui(guis[i], true);
1427             guis.last().cb->gui(guis.last(), true);
1428             guilayoutpass = guicursortype = 0;
1429 
1430             glEnable(GL_BLEND);
1431             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1432 
1433             //loopvrev(guis) guis[i].cb->gui(guis[i], false);
1434             guis.last().cb->gui(guis.last(), false);
1435 
1436             glDisable(GL_BLEND);
1437         }
1438 
1439         flusheditors();
1440         if(!fieldsactive) fieldmode = FIELDSHOW; //didn't draw any fields, so lose focus - mainly for menu closed
1441         if((fieldmode!=FIELDSHOW) != wasfocused)
1442         {
1443             textinput(fieldmode!=FIELDSHOW, TI_GUI);
1444             keyrepeat(fieldmode!=FIELDSHOW, KR_GUI);
1445         }
1446         loopi(2) mouseaction[i] = 0;
1447         curtextscale = oldtextscale;
1448     }
1449 
geteditor(const char * name,int mode,const char * init,const char * parent)1450     editor *geteditor(const char *name, int mode, const char *init, const char *parent)
1451     {
1452         return useeditor(name, mode, false, init, parent);
1453     }
1454 
editorline(editor * e,const char * str,int limit)1455     void editorline(editor *e, const char *str, int limit)
1456     {
1457         if(!e) return;
1458         if(e->lines.length() != 1 || !e->lines[0].empty()) e->lines.add();
1459         e->lines.last().set(str);
1460         if(limit >= 0 && e->lines.length() > limit)
1461         {
1462             int n = e->lines.length()-limit;
1463             e->removelines(0, n);
1464             e->cy = max(e->cy - n, 0);
1465             if(e->scrolly != editor::SCROLLEND) e->scrolly = max(e->scrolly - n, 0);
1466         }
1467         e->mark(false);
1468     }
1469 
editorclear(editor * e,const char * init)1470     void editorclear(editor *e, const char *init)
1471     {
1472         if(!e) return;
1473         e->clear(init);
1474     }
1475 
editoredit(editor * e,const char * init)1476     void editoredit(editor *e, const char *init)
1477     {
1478         if(!e) return;
1479         useeditor(e->name, e->mode, true, init, e->parent);
1480         e->clear(init);
1481         fieldmode = FIELDEDIT;
1482         e->unfocus = true;
1483     }
1484 };
1485