]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/hud.qc
Purge client/defs.qh
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / hud.qc
1 #include "hud.qh"
2
3 #include <client/items/items.qh>
4 #include <client/miscfunctions.qh>
5 #include <client/view.qh>
6 #include "panel/scoreboard.qh"
7 #include "hud_config.qh"
8 #include "../mapvoting.qh"
9 #include "../teamradar.qh"
10 #include <common/minigames/cl_minigames.qh>
11 #include <common/deathtypes/all.qh>
12 #include <common/ent_cs.qh>
13 #include <common/items/_mod.qh>
14 #include <common/mapinfo.qh>
15 #include <common/vehicles/all.qh>
16 #include <common/vehicles/vehicle/bumblebee.qh>
17 #include <common/mutators/mutator/waypoints/all.qh>
18 #include <common/stats.qh>
19 #include <lib/csqcmodel/cl_player.qh>
20 #include <lib/csqcmodel/cl_model.qh>
21 #include <common/gamemodes/_mod.qh>
22
23
24 /*
25 ==================
26 Misc HUD functions
27 ==================
28 */
29
30 void draw_cursor(vector pos, vector ofs, string img, vector col, float a)
31 {
32         ofs = vec2(ofs.x * SIZE_CURSOR.x, ofs.y * SIZE_CURSOR.y);
33         drawpic(pos - ofs, strcat(draw_currentSkin, img), SIZE_CURSOR, col, a, DRAWFLAG_NORMAL);
34 }
35
36 void draw_cursor_normal(vector pos, vector col, float a)
37 {
38         draw_cursor(pos, OFFSET_CURSOR, "/cursor", col, a);
39 }
40
41 void LoadMenuSkinValues()
42 {
43         int fh = -1;
44         if(cvar_string("menu_skin") != "")
45         {
46                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
47                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
48         }
49         if(fh < 0 && cvar_defstring("menu_skin") != "")
50         {
51                 cvar_set("menu_skin", cvar_defstring("menu_skin"));
52                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
53                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
54         }
55         if(fh < 0)
56         {
57                 draw_currentSkin = "gfx/menu/default";
58                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
59         }
60
61         draw_currentSkin = strzone(draw_currentSkin);
62
63         if(fh >= 0)
64         {
65                 string s;
66                 while((s = fgets(fh)))
67                 {
68                         int n = tokenize_console(s);
69                         if (n < 2)
70                                 continue;
71                         if(substring(argv(0), 0, 2) == "//")
72                                 continue;
73                         if(argv(0) == "SIZE_CURSOR")
74                                 SIZE_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
75                         else if(argv(0) == "OFFSET_CURSOR")
76                                 OFFSET_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
77                 }
78                 fclose(fh);
79         }
80 }
81
82 vector HUD_Get_Num_Color(float hp, float maxvalue, bool blink)
83 {
84         const vector COLOR100 = '0 1 0'; // green
85         const vector COLOR75 = '0.4 0.9 0'; // lightgreen
86         const vector COLOR50 = '1 1 1'; // white
87         const vector COLOR25 = '1 1 0.2'; // lightyellow
88         const vector COLOR10 = '1 0 0'; // red
89         vector color;
90
91         float hp_percent = hp / maxvalue * 100;
92         #define CASE_COLOR_BETWEEN(min, max) \
93                 if(hp_percent > min) \
94                         color = COLOR##min + (COLOR##max - COLOR##min) * ((hp_percent - min) / (max - min))
95
96         if(hp_percent > 100) color = COLOR100;
97         else CASE_COLOR_BETWEEN(75, 100);
98         else CASE_COLOR_BETWEEN(50, 75);
99         else CASE_COLOR_BETWEEN(25, 50);
100         else CASE_COLOR_BETWEEN(10, 25);
101         else color = COLOR10;
102
103         #undef CASE_COLOR_BETWEEN
104
105         if (blink)
106         {
107                 if(hp_percent >= 100)
108                 {
109                         float f = sin(2*M_PI*time);
110                         if (color.x == 0) color.x = f;
111                         if (color.y == 0) color.y = f;
112                         if (color.z == 0) color.z = f;
113                 }
114                 else if(hp_percent < 25)
115                 {
116                         float f = (1 - hp_percent / 25) * sin(2*M_PI*time);
117                         color -= color * f;
118                 }
119         }
120
121         return color;
122 }
123
124 float HUD_GetRowCount(int item_count, vector size, float item_aspect)
125 {
126         TC(int, item_count);
127         float aspect = size_y / size_x;
128         return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
129 }
130
131 vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect)
132 {
133         TC(int, item_count);
134         float columns, rows;
135         float ratio, best_ratio = 0;
136         float best_columns = 1, best_rows = 1;
137         bool vertical = (psize.x / psize.y >= item_aspect);
138         if(vertical)
139         {
140                 psize = eX * psize.y + eY * psize.x;
141                 item_aspect = 1 / item_aspect;
142         }
143
144         rows = ceil(sqrt(item_count));
145         columns = ceil(item_count/rows);
146         while(columns >= 1)
147         {
148                 ratio = (psize.x/columns) / (psize.y/rows);
149                 if(ratio > item_aspect)
150                         ratio = item_aspect * item_aspect / ratio;
151
152                 if(ratio <= best_ratio)
153                         break; // ratio starts decreasing by now, skip next configurations
154
155                 best_columns = columns;
156                 best_rows = rows;
157                 best_ratio = ratio;
158
159                 if(columns == 1)
160                         break;
161
162                 --columns;
163                 rows = ceil(item_count/columns);
164         }
165
166         return (vertical) ? vec2(best_rows, best_columns) : vec2(best_columns, best_rows);
167 }
168
169 /*
170 ==================
171 HUD panels
172 ==================
173 */
174
175 void HUD_Panel_LoadCvars()
176 {
177         // NOTE: in hud_configure mode cvars must be reloaded every frame
178         if (panel.update_time <= time)
179         {
180                 panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos")));
181                 panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size")));
182                 HUD_Panel_ScalePosSize();
183                 panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg"));
184                 panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color"));
185                 panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team"));
186                 panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha"));
187                 panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border"));
188                 panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding"));
189                 HUD_Panel_GetBg();
190                 if (panel.current_panel_bg != "0")
191                 {
192                         HUD_Panel_GetBgAlpha();
193                         HUD_Panel_GetBorder();
194                 }
195                 HUD_Panel_GetColorTeam();
196                 HUD_Panel_GetColor();
197                 HUD_Panel_GetFgAlpha();
198                 HUD_Panel_GetPadding();
199                 panel.current_panel_bg_alpha = panel_bg_alpha;
200                 panel.current_panel_fg_alpha = panel_fg_alpha;
201                 if (hud_configure_menu_open == 2 && panel == highlightedPanel)
202                         HUD_Panel_UpdatePosSize_ForMenu();
203                 else
204                 {
205                         panel_bg_alpha *= hud_fade_alpha * panel_fade_alpha;
206                         panel_fg_alpha *= hud_fade_alpha * panel_fade_alpha;
207                 }
208                 panel.current_panel_pos = panel_pos;
209                 panel.current_panel_size = panel_size;
210                 panel.current_panel_bg_border = panel_bg_border;
211                 panel.current_panel_bg_color = panel_bg_color;
212                 panel.current_panel_bg_color_team = panel_bg_color_team;
213                 panel.current_panel_bg_padding = panel_bg_padding;
214                 panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval;
215                 return;
216         }
217
218         panel_pos = panel.current_panel_pos;
219         panel_size = panel.current_panel_size;
220         panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha * panel_fade_alpha;
221         panel_bg_border = panel.current_panel_bg_border;
222         panel_bg_color = panel.current_panel_bg_color;
223         panel_bg_color_team = panel.current_panel_bg_color_team;
224         panel_bg_padding = panel.current_panel_bg_padding;
225         panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha * panel_fade_alpha;
226 }
227
228 //basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
229 void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag)
230 {
231         TC(bool, vertical); TC(int, drawflag);
232         if(!length_ratio || !theAlpha)
233                 return;
234         if(length_ratio > 1)
235                 length_ratio = 1;
236         if (baralign == 3)
237         {
238                 if(length_ratio < -1)
239                         length_ratio = -1;
240         }
241         else if(length_ratio < 0)
242                 return;
243
244         theOrigin = HUD_Shift(theOrigin);
245         theSize = HUD_Scale(theSize);
246
247         vector square;
248         vector width, height;
249         if(vertical) {
250                 pic = strcat(hud_skin_path, "/", pic, "_vertical");
251                 if(precache_pic(pic) == "") {
252                         pic = "gfx/hud/default/progressbar_vertical";
253                 }
254
255         if (baralign == 1) // bottom align
256                         theOrigin.y += (1 - length_ratio) * theSize.y;
257         else if (baralign == 2) // center align
258             theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y;
259         else if (baralign == 3) // center align, positive values down, negative up
260                 {
261                         theSize.y *= 0.5;
262                         if (length_ratio > 0)
263                                 theOrigin.y += theSize.y;
264                         else
265                         {
266                                 theOrigin.y += (1 + length_ratio) * theSize.y;
267                                 length_ratio = -length_ratio;
268                         }
269                 }
270                 theSize.y *= length_ratio;
271
272                 vector bH;
273                 width = eX * theSize.x;
274                 height = eY * theSize.y;
275                 if(theSize.y <= theSize.x * 2)
276                 {
277                         // button not high enough
278                         // draw just upper and lower part then
279                         square = eY * theSize.y * 0.5;
280                         bH = eY * (0.25 * theSize.y / (theSize.x * 2));
281                         drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag);
282                         drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag);
283                 }
284                 else
285                 {
286                         square = eY * theSize.x;
287                         drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, drawflag);
288                         drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, drawflag);
289                         drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag);
290                 }
291         } else {
292                 pic = strcat(hud_skin_path, "/", pic);
293                 if(precache_pic(pic) == "") {
294                         pic = "gfx/hud/default/progressbar";
295                 }
296
297                 if (baralign == 1) // right align
298                         theOrigin.x += (1 - length_ratio) * theSize.x;
299         else if (baralign == 2) // center align
300             theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x;
301         else if (baralign == 3) // center align, positive values on the right, negative on the left
302                 {
303                         theSize.x *= 0.5;
304                         if (length_ratio > 0)
305                                 theOrigin.x += theSize.x;
306                         else
307                         {
308                                 theOrigin.x += (1 + length_ratio) * theSize.x;
309                                 length_ratio = -length_ratio;
310                         }
311                 }
312                 theSize.x *= length_ratio;
313
314                 vector bW;
315                 width = eX * theSize.x;
316                 height = eY * theSize.y;
317                 if(theSize.x <= theSize.y * 2)
318                 {
319                         // button not wide enough
320                         // draw just left and right part then
321                         square = eX * theSize.x * 0.5;
322                         bW = eX * (0.25 * theSize.x / (theSize.y * 2));
323                         drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag);
324                         drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag);
325                 }
326                 else
327                 {
328                         square = eX * theSize.y;
329                         drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, drawflag);
330                         drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, drawflag);
331                         drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
332                 }
333         }
334 }
335
336 void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag)
337 {
338         TC(int, drawflag);
339         if(!theAlpha)
340                 return;
341
342         pos = HUD_Shift(pos);
343         mySize = HUD_Scale(mySize);
344
345         string pic;
346         pic = strcat(hud_skin_path, "/num_leading");
347         if(precache_pic(pic) == "") {
348                 pic = "gfx/hud/default/num_leading";
349         }
350
351         drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag);
352         if(mySize.x/mySize.y > 2)
353                 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);
354         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);
355 }
356
357 void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp)
358 {
359         TC(bool, vertical); TC(int, icon_right_align);
360         vector newPos = '0 0 0', newSize = '0 0 0';
361         vector picpos, numpos;
362
363         if (vertical)
364         {
365                 if(mySize.y/mySize.x > 2)
366                 {
367                         newSize.y = 2 * mySize.x;
368                         newSize.x = mySize.x;
369
370                         newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
371                         newPos.x = myPos.x;
372                 }
373                 else
374                 {
375                         newSize.x = 1/2 * mySize.y;
376                         newSize.y = mySize.y;
377
378                         newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
379                         newPos.y = myPos.y;
380                 }
381
382                 if(icon_right_align)
383                 {
384                         numpos = newPos;
385                         picpos = newPos + eY * newSize.x;
386                 }
387                 else
388                 {
389                         picpos = newPos;
390                         numpos = newPos + eY * newSize.x;
391                 }
392
393                 newSize.y /= 2;
394                 drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
395                 // make number smaller than icon, it looks better
396                 // reduce only y to draw numbers with different number of digits with the same y size
397                 numpos.y += newSize.y * ((1 - 0.7) / 2);
398                 newSize.y *= 0.7;
399                 drawstring_aspect(numpos, ftos(theTime), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
400                 return;
401         }
402
403         if(mySize.x/mySize.y > 3)
404         {
405                 newSize.x = 3 * mySize.y;
406                 newSize.y = mySize.y;
407
408                 newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
409                 newPos.y = myPos.y;
410         }
411         else
412         {
413                 newSize.y = 1/3 * mySize.x;
414                 newSize.x = mySize.x;
415
416                 newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
417                 newPos.x = myPos.x;
418         }
419
420         if(icon_right_align) // right align
421         {
422                 numpos = newPos;
423                 picpos = newPos + eX * 2 * newSize.y;
424         }
425         else // left align
426         {
427                 numpos = newPos + eX * newSize.y;
428                 picpos = newPos;
429         }
430
431         // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
432         // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
433         drawstring_aspect_expanding(numpos, ftos(theTime), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
434         drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
435 }
436
437 void DrawNumIcon(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha)
438 {
439         TC(bool, vertical); TC(int, icon_right_align);
440         DrawNumIcon_expanding(myPos, mySize, theTime, icon, vertical, icon_right_align, color, theAlpha, 0);
441 }
442
443 /*
444 ==================
445 Main HUD system
446 ==================
447 */
448
449 float lasthud;
450 float vh_notice_time;
451 void HUD_Vehicle()
452 {
453         if(autocvar__hud_configure) return;
454         if(intermission == 2) return;
455
456         if(hud == HUD_BUMBLEBEE_GUN)
457                 CSQC_BUMBLE_GUN_HUD();
458         else {
459                 Vehicle info = REGISTRY_GET(Vehicles, hud);
460                 info.vr_hud(info);
461         }
462
463         if(hud != HUD_NORMAL && lasthud == HUD_NORMAL)
464                 vh_notice_time = time + autocvar_cl_vehicles_notify_time;
465
466         lasthud = hud;
467 }
468
469 void HUD_Panel_Draw(entity panent)
470 {
471         panel = panent;
472         if (autocvar__hud_configure)
473         {
474                 if (!(panel.panel_configflags & PANEL_CONFIG_MAIN))
475                         return;
476                 panel_fade_alpha = 1;
477                 Hud_Panel_GetPanelEnabled();
478                 panel.panel_draw();
479                 return;
480         }
481
482         bool draw_allowed = false;
483         if (scoreboard_fade_alpha && panel.panel_showflags & PANEL_SHOW_WITH_SB)
484         {
485                 draw_allowed = true;
486         }
487         else if (active_minigame && HUD_MinigameMenu_IsOpened())
488         {
489                 if (panel.panel_showflags & PANEL_SHOW_MINIGAME)
490                         draw_allowed = true;
491         }
492         else if(intermission == 2)
493         {
494                 if(panel.panel_showflags & PANEL_SHOW_MAPVOTE)
495                         draw_allowed = true;
496         }
497         else if (panel.panel_showflags & PANEL_SHOW_MAINGAME)
498                 draw_allowed = true;
499
500         if (draw_allowed)
501         {
502                 if (panel.panel_showflags & PANEL_SHOW_WITH_SB)
503                 {
504                         if (scoreboard_fade_alpha && intermission == 2 && !(panel.panel_showflags & PANEL_SHOW_MAPVOTE))
505                                 panel_fade_alpha = scoreboard_fade_alpha;
506                         else
507                                 panel_fade_alpha = 1;
508                 }
509                 else
510                 {
511                         panel_fade_alpha = 1 - scoreboard_fade_alpha;
512                         if(!panel_fade_alpha)
513                                 return;
514                 }
515                 panel.panel_draw();
516         }
517 }
518
519 void HUD_Reset()
520 {
521         // reset gametype specific icons
522         if(gametype.m_modicons_reset)
523                 gametype.m_modicons_reset();
524 }
525
526 float autocvar_hud_dynamic_shake = 1;
527 float autocvar_hud_dynamic_shake_damage_max = 130;
528 float autocvar_hud_dynamic_shake_damage_min = 10;
529 float autocvar_hud_dynamic_shake_scale = 0.2;
530 float hud_dynamic_shake_x[10] = {0,    1, -0.7,  0.5, -0.3,  0.2, -0.1,  0.1,  0.0, 0};
531 float hud_dynamic_shake_y[10] = {0,  0.4,  0.8, -0.2, -0.6,  0.0,  0.3,  0.1, -0.1, 0};
532 bool Hud_Shake_Update()
533 {
534         if(time - hud_dynamic_shake_time < 0)
535                 return false;
536
537         float anim_speed = 17 + 9 * hud_dynamic_shake_factor;
538         float elapsed_time = (time - hud_dynamic_shake_time) * anim_speed;
539         int i = floor(elapsed_time);
540         if(i >= 9)
541                 return false;
542
543         float f = elapsed_time - i;
544         hud_dynamic_shake_realofs.x = (1 - f) * hud_dynamic_shake_x[i] + f * hud_dynamic_shake_x[i+1];
545         hud_dynamic_shake_realofs.y = (1 - f) * hud_dynamic_shake_y[i] + f * hud_dynamic_shake_y[i+1];
546         hud_dynamic_shake_realofs.z = 0;
547         hud_dynamic_shake_realofs *= hud_dynamic_shake_factor * autocvar_hud_dynamic_shake_scale;
548         hud_dynamic_shake_realofs.x = bound(-0.1, hud_dynamic_shake_realofs.x, 0.1) * vid_conwidth;
549         hud_dynamic_shake_realofs.y = bound(-0.1, hud_dynamic_shake_realofs.y, 0.1) * vid_conheight;
550         return true;
551 }
552
553 void Hud_Dynamic_Frame()
554 {
555         vector ofs = '0 0 0';
556         hud_scale_current = '1 1 0';
557         hud_shift_current = '0 0 0';
558
559         if (autocvar_hud_dynamic_follow)
560         {
561                 entity view = CSQCModel_server2csqc(player_localentnum - 1);
562                 calc_followmodel_ofs(view);
563                 ofs = -cl_followmodel_ofs * autocvar_hud_dynamic_follow_scale;
564                 ofs.x *= autocvar_hud_dynamic_follow_scale_xyz.z;
565                 ofs.y *= autocvar_hud_dynamic_follow_scale_xyz.x;
566                 ofs.z *= autocvar_hud_dynamic_follow_scale_xyz.y;
567
568                 if (fabs(ofs.x) < 0.001) ofs.x = 0;
569                 if (fabs(ofs.y) < 0.001) ofs.y = 0;
570                 if (fabs(ofs.z) < 0.001) ofs.z = 0;
571                 ofs.x = bound(-0.1, ofs.x, 0.1);
572                 ofs.y = bound(-0.1, ofs.y, 0.1);
573                 ofs.z = bound(-0.1, ofs.z, 0.1);
574
575                 hud_shift_current.x = ofs.y * vid_conwidth;
576                 hud_shift_current.y = ofs.z * vid_conheight;
577                 hud_shift_current.z = ofs.x;
578
579                 hud_scale_current.x = (1 + hud_shift_current.z);
580                 hud_scale_current.y = hud_scale_current.x;
581         }
582
583         if(autocvar_hud_dynamic_shake > 0)
584         {
585                 static float old_health = 0;
586                 float health = max(-1, STAT(HEALTH));
587                 if(hud_dynamic_shake_factor == -1) // don't allow the effect for this frame
588                 {
589                         hud_dynamic_shake_factor = 0;
590                         old_health = health;
591                 }
592                 else
593                 {
594                         float new_hud_dynamic_shake_factor = 0;
595                         if (old_health - health >= autocvar_hud_dynamic_shake_damage_min
596                                 && autocvar_hud_dynamic_shake_damage_max > autocvar_hud_dynamic_shake_damage_min
597                                 && old_health > 0 && !intermission)
598                         {
599                                 float m = max(autocvar_hud_dynamic_shake_damage_min, 1);
600                                 new_hud_dynamic_shake_factor = (old_health - health - m) / (autocvar_hud_dynamic_shake_damage_max - m);
601                                 if(new_hud_dynamic_shake_factor >= 1)
602                                         new_hud_dynamic_shake_factor = 1;
603                                 if(new_hud_dynamic_shake_factor >= hud_dynamic_shake_factor)
604                                 {
605                                         hud_dynamic_shake_factor = new_hud_dynamic_shake_factor;
606                                         hud_dynamic_shake_time = time;
607                                 }
608                         }
609                         old_health = health;
610                         if(hud_dynamic_shake_factor)
611                                 if(!Hud_Shake_Update())
612                                         hud_dynamic_shake_factor = 0;
613                 }
614
615                 if(hud_dynamic_shake_factor > 0)
616                 {
617                         hud_shift_current.x += hud_dynamic_shake_realofs.x;
618                         hud_shift_current.y += hud_dynamic_shake_realofs.y;
619                 }
620         }
621
622         hud_scale_center.x = 0.5 * vid_conwidth;
623         hud_scale_center.y = 0.5 * vid_conheight;
624
625         HUD_Scale_Disable();
626 }
627
628 bool HUD_WouldShowCursor()
629 {
630         if(autocvar__hud_configure)
631                 return true;
632         if(mv_active)
633                 return true;
634         //entity local_player = ((csqcplayer) ? csqcplayer : CSQCModel_server2csqc(player_localentnum - 1)); // TODO: doesn't use regular cursor handling
635         //if(local_player.viewloc && (local_player.viewloc.spawnflags & VIEWLOC_FREEAIM))
636                 //return true;
637         if(HUD_Radar_Clickable())
638                 return true;
639         if(HUD_MinigameMenu_IsOpened())
640                 return true;
641         if(QuickMenu_IsOpened())
642                 return true;
643         return false;
644 }
645
646 float prev_myteam;
647 void HUD_Main()
648 {
649         int i;
650         if(hud_configure_menu_open == 1)
651                 hud_fade_alpha = 1;
652         else
653                 hud_fade_alpha = 1 - autocvar__menu_alpha;
654
655         if(myteam != prev_myteam)
656         {
657                 myteamcolors = colormapPaletteColor(myteam, 1);
658                 FOREACH(hud_panels, true, it.update_time = time);
659                 prev_myteam = myteam;
660         }
661
662         HUD_Configure_Frame();
663
664         if(scoreboard_fade_alpha == 1)
665                 if(autocvar__menu_alpha == 1)
666                         return;
667
668         // Drawing stuff
669         if (hud_skin_prev != autocvar_hud_skin)
670         {
671                 strcpy(hud_skin_path, strcat("gfx/hud/", autocvar_hud_skin));
672                 strcpy(hud_skin_prev, autocvar_hud_skin);
673         }
674
675         // draw the dock
676         if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
677         {
678                 int f;
679                 vector color;
680                 float hud_dock_color_team = autocvar_hud_dock_color_team;
681                 if((teamplay) && hud_dock_color_team) {
682                         if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
683                                 color = '1 0 0' * hud_dock_color_team;
684                         else
685                                 color = myteamcolors * hud_dock_color_team;
686                 }
687                 else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
688                         color = '1 0 0' * hud_dock_color_team;
689                 }
690                 else
691                 {
692                         string hud_dock_color = autocvar_hud_dock_color;
693                         if(hud_dock_color == "shirt") {
694                                 f = entcs_GetClientColors(current_player);
695                                 color = colormapPaletteColor(floor(f / 16), 0);
696                         }
697                         else if(hud_dock_color == "pants") {
698                                 f = entcs_GetClientColors(current_player);
699                                 color = colormapPaletteColor(f % 16, 1);
700                         }
701                         else
702                                 color = stov(hud_dock_color);
703                 }
704
705                 string pic;
706                 pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
707                 if(precache_pic(pic) == "") {
708                         pic = strcat(hud_skin_path, "/dock_medium");
709                         if(precache_pic(pic) == "") {
710                                 pic = "gfx/hud/default/dock_medium";
711                         }
712                 }
713                 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...
714         }
715
716         // cache the panel order into the panel_order array
717         if(autocvar__hud_panelorder != hud_panelorder_prev) {
718                 for(i = 0; i < REGISTRY_COUNT(hud_panels); ++i)
719                         panel_order[i] = -1;
720                 string s = "";
721                 int p_num;
722                 bool warning = false;
723                 int argc = tokenize_console(autocvar__hud_panelorder);
724                 if (argc > REGISTRY_COUNT(hud_panels))
725                         warning = true;
726                 //first detect wrong/missing panel numbers
727                 for(i = 0; i < REGISTRY_COUNT(hud_panels); ++i) {
728                         p_num = stoi(argv(i));
729                         if (p_num >= 0 && p_num < REGISTRY_COUNT(hud_panels)) { //correct panel number?
730                                 if (panel_order[p_num] == -1) //found for the first time?
731                                         s = strcat(s, ftos(p_num), " ");
732                                 panel_order[p_num] = 1; //mark as found
733                         }
734                         else
735                                 warning = true;
736                 }
737                 for(i = 0; i < REGISTRY_COUNT(hud_panels); ++i) {
738                         if (panel_order[i] == -1) {
739                                 warning = true;
740                                 s = strcat(s, ftos(i), " "); //add missing panel number
741                         }
742                 }
743                 if (warning)
744                         LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder");
745
746                 cvar_set("_hud_panelorder", s);
747                 strcpy(hud_panelorder_prev, s);
748
749                 //now properly set panel_order
750                 tokenize_console(s);
751                 for(i = 0; i < REGISTRY_COUNT(hud_panels); ++i) {
752                         panel_order[i] = stof(argv(i));
753                 }
754         }
755
756         hud_draw_maximized = 0;
757         // draw panels in the order specified by panel_order array
758         for(i = REGISTRY_COUNT(hud_panels) - 1; i >= 0; --i)
759                 HUD_Panel_Draw(REGISTRY_GET(hud_panels, panel_order[i]));
760
761         HUD_Vehicle();
762
763         hud_draw_maximized = 1; // panels that may be maximized must check this var
764         // draw maximized panels on top
765         if(hud_panel_radar_maximized)
766                 HUD_Panel_Draw(HUD_PANEL(RADAR));
767         if(autocvar__con_chat_maximized)
768                 HUD_Panel_Draw(HUD_PANEL(CHAT));
769         if (QuickMenu_IsOpened())
770                 HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
771         HUD_Panel_Draw(HUD_PANEL(SCOREBOARD));
772
773         int cursor_active_prev = cursor_active;
774         cursor_active = HUD_WouldShowCursor();
775         if (cursor_active_prev != cursor_active && autocvar_hud_cursormode)
776         {
777                 setcursormode(cursor_active);
778                 // cursor inactive this frame, will be set to 1 the next frame
779                 if (cursor_active)
780                         cursor_active = -1;
781         }
782
783         if (intermission == 2)
784                 HUD_Reset();
785
786         HUD_Configure_PostDraw();
787
788         hud_configure_prev = autocvar__hud_configure;
789 }