]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/hud.qc
Nades: move definitions alongside nade mutator
[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 "t_items.qh"
8 #include "../common/buffs/all.qh"
9 #include "../common/deathtypes/all.qh"
10 #include "../common/items/all.qc"
11 #include "../common/mapinfo.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 HUD_Vehicle()
415 {
416         if(autocvar__hud_configure) return;
417         if(intermission == 2) return;
418
419         if(hud == HUD_BUMBLEBEE_GUN)
420                 CSQC_BUMBLE_GUN_HUD();
421         else {
422                 Vehicle info = get_vehicleinfo(hud);
423                 info.vr_hud(info);
424         }
425 }
426
427 bool HUD_Panel_CheckFlags(int showflags)
428 {
429         if ( HUD_Minigame_Showpanels() )
430                 return showflags & PANEL_SHOW_MINIGAME;
431         if(intermission == 2)
432                 return showflags & PANEL_SHOW_MAPVOTE;
433         return showflags & PANEL_SHOW_MAINGAME;
434 }
435
436 void HUD_Panel_Draw(entity panent)
437 {
438         panel = panent;
439         if(autocvar__hud_configure)
440         {
441                 if(panel.panel_configflags & PANEL_CONFIG_MAIN)
442                         panel.panel_draw();
443         }
444         else if(HUD_Panel_CheckFlags(panel.panel_showflags))
445                 panel.panel_draw();
446 }
447
448 void HUD_Reset()
449 {
450         // reset gametype specific icons
451         if(gametype == MAPINFO_TYPE_CTF)
452                 HUD_Mod_CTF_Reset();
453 }
454
455 void HUD_Main()
456 {
457         int i;
458         // global hud theAlpha fade
459         if(menu_enabled == 1)
460                 hud_fade_alpha = 1;
461         else
462                 hud_fade_alpha = (1 - autocvar__menu_alpha);
463
464         if(scoreboard_fade_alpha)
465                 hud_fade_alpha = (1 - scoreboard_fade_alpha);
466
467         HUD_Configure_Frame();
468
469         // panels that we want to be active together with the scoreboard
470         // they must fade only when the menu does
471         if(scoreboard_fade_alpha == 1)
472         {
473                 HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
474                 return;
475         }
476
477         if(!autocvar__hud_configure && !hud_fade_alpha)
478         {
479                 hud_fade_alpha = 1;
480                 HUD_Panel_Draw(HUD_PANEL(VOTE));
481                 hud_fade_alpha = 0;
482                 return;
483         }
484
485         // Drawing stuff
486         if (hud_skin_prev != autocvar_hud_skin)
487         {
488                 if (hud_skin_path)
489                         strunzone(hud_skin_path);
490                 hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
491                 if (hud_skin_prev)
492                         strunzone(hud_skin_prev);
493                 hud_skin_prev = strzone(autocvar_hud_skin);
494         }
495
496         // draw the dock
497         if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
498         {
499                 int f;
500                 vector color;
501                 float hud_dock_color_team = autocvar_hud_dock_color_team;
502                 if((teamplay) && hud_dock_color_team) {
503                         if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
504                                 color = '1 0 0' * hud_dock_color_team;
505                         else
506                                 color = myteamcolors * hud_dock_color_team;
507                 }
508                 else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
509                         color = '1 0 0' * hud_dock_color_team;
510                 }
511                 else
512                 {
513                         string hud_dock_color = autocvar_hud_dock_color;
514                         if(hud_dock_color == "shirt") {
515                                 f = stof(getplayerkeyvalue(current_player, "colors"));
516                                 color = colormapPaletteColor(floor(f / 16), 0);
517                         }
518                         else if(hud_dock_color == "pants") {
519                                 f = stof(getplayerkeyvalue(current_player, "colors"));
520                                 color = colormapPaletteColor(f % 16, 1);
521                         }
522                         else
523                                 color = stov(hud_dock_color);
524                 }
525
526                 string pic;
527                 pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
528                 if(precache_pic(pic) == "") {
529                         pic = strcat(hud_skin_path, "/dock_medium");
530                         if(precache_pic(pic) == "") {
531                                 pic = "gfx/hud/default/dock_medium";
532                         }
533                 }
534                 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...
535         }
536
537         // cache the panel order into the panel_order array
538         if(autocvar__hud_panelorder != hud_panelorder_prev) {
539                 for(i = 0; i < hud_panels_COUNT; ++i)
540                         panel_order[i] = -1;
541                 string s = "";
542                 int p_num;
543                 bool warning = false;
544                 int argc = tokenize_console(autocvar__hud_panelorder);
545                 if (argc > hud_panels_COUNT)
546                         warning = true;
547                 //first detect wrong/missing panel numbers
548                 for(i = 0; i < hud_panels_COUNT; ++i) {
549                         p_num = stoi(argv(i));
550                         if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number?
551                                 if (panel_order[p_num] == -1) //found for the first time?
552                                         s = strcat(s, ftos(p_num), " ");
553                                 panel_order[p_num] = 1; //mark as found
554                         }
555                         else
556                                 warning = true;
557                 }
558                 for(i = 0; i < hud_panels_COUNT; ++i) {
559                         if (panel_order[i] == -1) {
560                                 warning = true;
561                                 s = strcat(s, ftos(i), " "); //add missing panel number
562                         }
563                 }
564                 if (warning)
565                         LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
566
567                 cvar_set("_hud_panelorder", s);
568                 if(hud_panelorder_prev)
569                         strunzone(hud_panelorder_prev);
570                 hud_panelorder_prev = strzone(s);
571
572                 //now properly set panel_order
573                 tokenize_console(s);
574                 for(i = 0; i < hud_panels_COUNT; ++i) {
575                         panel_order[i] = stof(argv(i));
576                 }
577         }
578
579         hud_draw_maximized = 0;
580         // draw panels in the order specified by panel_order array
581         for(i = hud_panels_COUNT - 1; i >= 0; --i)
582                 HUD_Panel_Draw(hud_panels_from(panel_order[i]));
583
584         HUD_Vehicle();
585
586         hud_draw_maximized = 1; // panels that may be maximized must check this var
587         // draw maximized panels on top
588         if(hud_panel_radar_maximized)
589                 HUD_Panel_Draw(HUD_PANEL(RADAR));
590         if(autocvar__con_chat_maximized)
591                 HUD_Panel_Draw(HUD_PANEL(CHAT));
592         if(hud_panel_quickmenu)
593                 HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
594
595         if (scoreboard_active || intermission == 2)
596                 HUD_Reset();
597
598         HUD_Configure_PostDraw();
599
600         hud_configure_prev = autocvar__hud_configure;
601 }