]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/hud.qc
client: pass compilation units test
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / hud.qc
1 #include "hud.qh"
2
3 #include "hud_config.qh"
4 #include "../mapvoting.qh"
5 #include "../scoreboard.qh"
6 #include "../teamradar.qh"
7 #include <common/t_items.qh>
8 #include <common/deathtypes/all.qh>
9 #include <common/items/all.qc>
10 #include <common/mapinfo.qh>
11 #include <common/vehicles/all.qh>
12 #include <common/mutators/mutator/waypoints/all.qh>
13 #include <common/stats.qh>
14 #include <lib/csqcmodel/cl_player.qh>
15 // TODO: remove
16 #include <server/mutators/mutator/gamemode_ctf.qc>
17
18
19 /*
20 ==================
21 Misc HUD functions
22 ==================
23 */
24
25 vector HUD_Get_Num_Color (float x, float maxvalue)
26 {
27         float blinkingamt;
28         vector color;
29         if(x >= maxvalue) {
30                 color.x = sin(2*M_PI*time);
31                 color.y = 1;
32                 color.z = sin(2*M_PI*time);
33         }
34         else if(x > maxvalue * 0.75) {
35                 color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
36                 color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
37                 color.z = 0;
38         }
39         else if(x > maxvalue * 0.5) {
40                 color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
41                 color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
42                 color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0
43         }
44         else if(x > maxvalue * 0.25) {
45                 color.x = 1;
46                 color.y = 1;
47                 color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
48         }
49         else if(x > maxvalue * 0.1) {
50                 color.x = 1;
51                 color.y = (x-20)*90/27/100; // green value between 0 -> 1
52                 color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
53         }
54         else {
55                 color.x = 1;
56                 color.y = 0;
57                 color.z = 0;
58         }
59
60         blinkingamt = (1 - x/maxvalue/0.25);
61         if(blinkingamt > 0)
62         {
63                 color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time);
64                 color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time);
65                 color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time);
66         }
67         return color;
68 }
69
70 float HUD_GetRowCount(int item_count, vector size, float item_aspect)
71 {
72         float aspect = size_y / size_x;
73         return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
74 }
75
76 vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect)
77 {
78         float columns, rows;
79         float ratio, best_ratio = 0;
80         float best_columns = 1, best_rows = 1;
81         bool vertical = (psize.x / psize.y >= item_aspect);
82         if(vertical)
83         {
84                 psize = eX * psize.y + eY * psize.x;
85                 item_aspect = 1 / item_aspect;
86         }
87
88         rows = ceil(sqrt(item_count));
89         columns = ceil(item_count/rows);
90         while(columns >= 1)
91         {
92                 ratio = (psize.x/columns) / (psize.y/rows);
93                 if(ratio > item_aspect)
94                         ratio = item_aspect * item_aspect / ratio;
95
96                 if(ratio <= best_ratio)
97                         break; // ratio starts decreasing by now, skip next configurations
98
99                 best_columns = columns;
100                 best_rows = rows;
101                 best_ratio = ratio;
102
103                 if(columns == 1)
104                         break;
105
106                 --columns;
107                 rows = ceil(item_count/columns);
108         }
109
110         if(vertical)
111                 return eX * best_rows + eY * best_columns;
112         else
113                 return eX * best_columns + eY * best_rows;
114 }
115
116 // return the string of the onscreen race timer
117 string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname)
118 {
119         string col;
120         string timestr;
121         string cpname;
122         string lapstr;
123         lapstr = "";
124
125         if(theirtime == 0) // goal hit
126         {
127                 if(mytime > 0)
128                 {
129                         timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
130                         col = "^1";
131                 }
132                 else if(mytime == 0)
133                 {
134                         timestr = "+0.0";
135                         col = "^3";
136                 }
137                 else
138                 {
139                         timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
140                         col = "^2";
141                 }
142
143                 if(lapdelta > 0)
144                 {
145                         lapstr = sprintf(_(" (-%dL)"), lapdelta);
146                         col = "^2";
147                 }
148                 else if(lapdelta < 0)
149                 {
150                         lapstr = sprintf(_(" (+%dL)"), -lapdelta);
151                         col = "^1";
152                 }
153         }
154         else if(theirtime > 0) // anticipation
155         {
156                 if(mytime >= theirtime)
157                         timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS));
158                 else
159                         timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime));
160                 col = "^3";
161         }
162         else
163         {
164                 col = "^7";
165                 timestr = "";
166         }
167
168         if(cp == 254)
169                 cpname = _("Start line");
170         else if(cp == 255)
171                 cpname = _("Finish line");
172         else if(cp)
173                 cpname = sprintf(_("Intermediate %d"), cp);
174         else
175                 cpname = _("Finish line");
176
177         if(theirtime < 0)
178                 return strcat(col, cpname);
179         else if(theirname == "")
180                 return strcat(col, sprintf("%s (%s)", cpname, timestr));
181         else
182                 return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr)));
183 }
184
185 // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
186 int race_CheckName(string net_name)
187 {
188         int i;
189         for (i=RANKINGS_CNT-1;i>=0;--i)
190                 if(grecordholder[i] == net_name)
191                         return i+1;
192         return 0;
193 }
194
195 /*
196 ==================
197 HUD panels
198 ==================
199 */
200
201 //basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
202 void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag)
203 {
204         if(!length_ratio || !theAlpha)
205                 return;
206         if(length_ratio > 1)
207                 length_ratio = 1;
208         if (baralign == 3)
209         {
210                 if(length_ratio < -1)
211                         length_ratio = -1;
212         }
213         else if(length_ratio < 0)
214                 return;
215
216         vector square;
217         vector width, height;
218         if(vertical) {
219                 pic = strcat(hud_skin_path, "/", pic, "_vertical");
220                 if(precache_pic(pic) == "") {
221                         pic = "gfx/hud/default/progressbar_vertical";
222                 }
223
224         if (baralign == 1) // bottom align
225                         theOrigin.y += (1 - length_ratio) * theSize.y;
226         else if (baralign == 2) // center align
227             theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y;
228         else if (baralign == 3) // center align, positive values down, negative up
229                 {
230                         theSize.y *= 0.5;
231                         if (length_ratio > 0)
232                                 theOrigin.y += theSize.y;
233                         else
234                         {
235                                 theOrigin.y += (1 + length_ratio) * theSize.y;
236                                 length_ratio = -length_ratio;
237                         }
238                 }
239                 theSize.y *= length_ratio;
240
241                 vector bH;
242                 width = eX * theSize.x;
243                 height = eY * theSize.y;
244                 if(theSize.y <= theSize.x * 2)
245                 {
246                         // button not high enough
247                         // draw just upper and lower part then
248                         square = eY * theSize.y * 0.5;
249                         bH = eY * (0.25 * theSize.y / (theSize.x * 2));
250                         drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag);
251                         drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag);
252                 }
253                 else
254                 {
255                         square = eY * theSize.x;
256                         drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, drawflag);
257                         drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, drawflag);
258                         drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag);
259                 }
260         } else {
261                 pic = strcat(hud_skin_path, "/", pic);
262                 if(precache_pic(pic) == "") {
263                         pic = "gfx/hud/default/progressbar";
264                 }
265
266                 if (baralign == 1) // right align
267                         theOrigin.x += (1 - length_ratio) * theSize.x;
268         else if (baralign == 2) // center align
269             theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x;
270         else if (baralign == 3) // center align, positive values on the right, negative on the left
271                 {
272                         theSize.x *= 0.5;
273                         if (length_ratio > 0)
274                                 theOrigin.x += theSize.x;
275                         else
276                         {
277                                 theOrigin.x += (1 + length_ratio) * theSize.x;
278                                 length_ratio = -length_ratio;
279                         }
280                 }
281                 theSize.x *= length_ratio;
282
283                 vector bW;
284                 width = eX * theSize.x;
285                 height = eY * theSize.y;
286                 if(theSize.x <= theSize.y * 2)
287                 {
288                         // button not wide enough
289                         // draw just left and right part then
290                         square = eX * theSize.x * 0.5;
291                         bW = eX * (0.25 * theSize.x / (theSize.y * 2));
292                         drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag);
293                         drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag);
294                 }
295                 else
296                 {
297                         square = eX * theSize.y;
298                         drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, drawflag);
299                         drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, drawflag);
300                         drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
301                 }
302         }
303 }
304
305 void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag)
306 {
307         if(!theAlpha)
308                 return;
309
310         string pic;
311         pic = strcat(hud_skin_path, "/num_leading");
312         if(precache_pic(pic) == "") {
313                 pic = "gfx/hud/default/num_leading";
314         }
315
316         drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag);
317         if(mySize.x/mySize.y > 2)
318                 drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag);
319         drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag);
320 }
321
322 void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha, float fadelerp)
323 {
324         vector newPos = '0 0 0', newSize = '0 0 0';
325         vector picpos, numpos;
326
327         if (vertical)
328         {
329                 if(mySize.y/mySize.x > 2)
330                 {
331                         newSize.y = 2 * mySize.x;
332                         newSize.x = mySize.x;
333
334                         newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
335                         newPos.x = myPos.x;
336                 }
337                 else
338                 {
339                         newSize.x = 1/2 * mySize.y;
340                         newSize.y = mySize.y;
341
342                         newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
343                         newPos.y = myPos.y;
344                 }
345
346                 if(icon_right_align)
347                 {
348                         numpos = newPos;
349                         picpos = newPos + eY * newSize.x;
350                 }
351                 else
352                 {
353                         picpos = newPos;
354                         numpos = newPos + eY * newSize.x;
355                 }
356
357                 newSize.y /= 2;
358                 drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
359                 // make number smaller than icon, it looks better
360                 // reduce only y to draw numbers with different number of digits with the same y size
361                 numpos.y += newSize.y * ((1 - 0.7) / 2);
362                 newSize.y *= 0.7;
363                 drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
364                 return;
365         }
366
367         if(mySize.x/mySize.y > 3)
368         {
369                 newSize.x = 3 * mySize.y;
370                 newSize.y = mySize.y;
371
372                 newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
373                 newPos.y = myPos.y;
374         }
375         else
376         {
377                 newSize.y = 1/3 * mySize.x;
378                 newSize.x = mySize.x;
379
380                 newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
381                 newPos.x = myPos.x;
382         }
383
384         if(icon_right_align) // right align
385         {
386                 numpos = newPos;
387                 picpos = newPos + eX * 2 * newSize.y;
388         }
389         else // left align
390         {
391                 numpos = newPos + eX * newSize.y;
392                 picpos = newPos;
393         }
394
395         // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
396         // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
397         drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
398         drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
399 }
400
401 void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha)
402 {
403         DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0);
404 }
405
406 #include "all.inc"
407
408 /*
409 ==================
410 Main HUD system
411 ==================
412 */
413
414 void CSQC_BUMBLE_GUN_HUD();
415
416 void HUD_Vehicle()
417 {
418         if(autocvar__hud_configure) return;
419         if(intermission == 2) return;
420
421         if(hud == HUD_BUMBLEBEE_GUN)
422                 CSQC_BUMBLE_GUN_HUD();
423         else {
424                 Vehicle info = Vehicles_from(hud);
425                 info.vr_hud(info);
426         }
427 }
428
429 bool HUD_Panel_CheckFlags(int showflags)
430 {
431         if ( HUD_Minigame_Showpanels() )
432                 return showflags & PANEL_SHOW_MINIGAME;
433         if(intermission == 2)
434                 return showflags & PANEL_SHOW_MAPVOTE;
435         return showflags & PANEL_SHOW_MAINGAME;
436 }
437
438 void HUD_Panel_Draw(entity panent)
439 {
440         panel = panent;
441         if(autocvar__hud_configure)
442         {
443                 if(panel.panel_configflags & PANEL_CONFIG_MAIN)
444                         panel.panel_draw();
445         }
446         else if(HUD_Panel_CheckFlags(panel.panel_showflags))
447                 panel.panel_draw();
448 }
449
450 void HUD_Reset()
451 {
452         // reset gametype specific icons
453         if(gametype == MAPINFO_TYPE_CTF)
454                 HUD_Mod_CTF_Reset();
455 }
456
457 void HUD_Main()
458 {
459         int i;
460         // global hud theAlpha fade
461         if(menu_enabled == 1)
462                 hud_fade_alpha = 1;
463         else
464                 hud_fade_alpha = (1 - autocvar__menu_alpha);
465
466         if(scoreboard_fade_alpha)
467                 hud_fade_alpha = (1 - scoreboard_fade_alpha);
468
469         HUD_Configure_Frame();
470
471         // panels that we want to be active together with the scoreboard
472         // they must fade only when the menu does
473         if(scoreboard_fade_alpha == 1)
474         {
475                 HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
476                 return;
477         }
478
479         if(!autocvar__hud_configure && !hud_fade_alpha)
480         {
481                 hud_fade_alpha = 1;
482                 HUD_Panel_Draw(HUD_PANEL(VOTE));
483                 hud_fade_alpha = 0;
484                 return;
485         }
486
487         // Drawing stuff
488         if (hud_skin_prev != autocvar_hud_skin)
489         {
490                 if (hud_skin_path)
491                         strunzone(hud_skin_path);
492                 hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
493                 if (hud_skin_prev)
494                         strunzone(hud_skin_prev);
495                 hud_skin_prev = strzone(autocvar_hud_skin);
496         }
497
498         // draw the dock
499         if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
500         {
501                 int f;
502                 vector color;
503                 float hud_dock_color_team = autocvar_hud_dock_color_team;
504                 if((teamplay) && hud_dock_color_team) {
505                         if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
506                                 color = '1 0 0' * hud_dock_color_team;
507                         else
508                                 color = myteamcolors * hud_dock_color_team;
509                 }
510                 else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
511                         color = '1 0 0' * hud_dock_color_team;
512                 }
513                 else
514                 {
515                         string hud_dock_color = autocvar_hud_dock_color;
516                         if(hud_dock_color == "shirt") {
517                                 f = stof(getplayerkeyvalue(current_player, "colors"));
518                                 color = colormapPaletteColor(floor(f / 16), 0);
519                         }
520                         else if(hud_dock_color == "pants") {
521                                 f = stof(getplayerkeyvalue(current_player, "colors"));
522                                 color = colormapPaletteColor(f % 16, 1);
523                         }
524                         else
525                                 color = stov(hud_dock_color);
526                 }
527
528                 string pic;
529                 pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
530                 if(precache_pic(pic) == "") {
531                         pic = strcat(hud_skin_path, "/dock_medium");
532                         if(precache_pic(pic) == "") {
533                                 pic = "gfx/hud/default/dock_medium";
534                         }
535                 }
536                 drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock...
537         }
538
539         // cache the panel order into the panel_order array
540         if(autocvar__hud_panelorder != hud_panelorder_prev) {
541                 for(i = 0; i < hud_panels_COUNT; ++i)
542                         panel_order[i] = -1;
543                 string s = "";
544                 int p_num;
545                 bool warning = false;
546                 int argc = tokenize_console(autocvar__hud_panelorder);
547                 if (argc > hud_panels_COUNT)
548                         warning = true;
549                 //first detect wrong/missing panel numbers
550                 for(i = 0; i < hud_panels_COUNT; ++i) {
551                         p_num = stoi(argv(i));
552                         if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number?
553                                 if (panel_order[p_num] == -1) //found for the first time?
554                                         s = strcat(s, ftos(p_num), " ");
555                                 panel_order[p_num] = 1; //mark as found
556                         }
557                         else
558                                 warning = true;
559                 }
560                 for(i = 0; i < hud_panels_COUNT; ++i) {
561                         if (panel_order[i] == -1) {
562                                 warning = true;
563                                 s = strcat(s, ftos(i), " "); //add missing panel number
564                         }
565                 }
566                 if (warning)
567                         LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
568
569                 cvar_set("_hud_panelorder", s);
570                 if(hud_panelorder_prev)
571                         strunzone(hud_panelorder_prev);
572                 hud_panelorder_prev = strzone(s);
573
574                 //now properly set panel_order
575                 tokenize_console(s);
576                 for(i = 0; i < hud_panels_COUNT; ++i) {
577                         panel_order[i] = stof(argv(i));
578                 }
579         }
580
581         hud_draw_maximized = 0;
582         // draw panels in the order specified by panel_order array
583         for(i = hud_panels_COUNT - 1; i >= 0; --i)
584                 HUD_Panel_Draw(hud_panels_from(panel_order[i]));
585
586         HUD_Vehicle();
587
588         hud_draw_maximized = 1; // panels that may be maximized must check this var
589         // draw maximized panels on top
590         if(hud_panel_radar_maximized)
591                 HUD_Panel_Draw(HUD_PANEL(RADAR));
592         if(autocvar__con_chat_maximized)
593                 HUD_Panel_Draw(HUD_PANEL(CHAT));
594         if(hud_panel_quickmenu)
595                 HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
596
597         if (scoreboard_active || intermission == 2)
598                 HUD_Reset();
599
600         HUD_Configure_PostDraw();
601
602         hud_configure_prev = autocvar__hud_configure;
603 }