]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/panel/strafehud.qc
add drawfill-only option to the strafehud to prevent weird progressbar styles
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / panel / strafehud.qc
1 // Author: Juhu
2
3 #include "strafehud.qh"
4
5 #include <client/autocvars.qh>
6 #include <client/miscfunctions.qh>
7 #include <common/animdecide.qh>
8 #include <common/ent_cs.qh>
9 #include <common/mapinfo.qh>
10 #include <common/physics/movetypes/movetypes.qh>
11 #include <common/physics/player.qh>
12 #include <lib/csqcmodel/cl_player.qh>
13
14 // StrafeHUD (#25)
15
16 void HUD_StrafeHUD_Export(int fh)
17 {
18     // allow saving cvars that aesthetically change the panel into hud skin files
19 }
20
21 bool fwd = true;
22 bool state_fwd = true;
23 bool state_fwd_prev = true;
24 float demo_angle = -37;
25 float demo_direction = 1;
26 float demo_time = 0;
27 float state_onground_time = 0;
28 float state_strafekeys_time = 0;
29 float state_direction_time = 0;
30 bool state_onground = false;
31 bool state_strafekeys = false;
32 bool turn = false;
33 float turnangle;
34
35 // provide basic panel cvars to old clients
36 // TODO remove them after a future release (0.8.2+)
37 noref string autocvar_hud_panel_strafehud_pos = "0.320000 0.570000";
38 noref string autocvar_hud_panel_strafehud_size = "0.360000 0.020000";
39 noref string autocvar_hud_panel_strafehud_bg = "0";
40 noref string autocvar_hud_panel_strafehud_bg_color = "";
41 noref string autocvar_hud_panel_strafehud_bg_color_team = "";
42 noref string autocvar_hud_panel_strafehud_bg_alpha = "0.7";
43 noref string autocvar_hud_panel_strafehud_bg_border = "";
44 noref string autocvar_hud_panel_strafehud_bg_padding = "";
45
46 void HUD_StrafeHUD()
47 {
48     entity strafeplayer;
49     bool islocal;
50
51     if(!autocvar__hud_configure)
52     {
53         if(!autocvar_hud_panel_strafehud) return;
54         if(spectatee_status == -1 && (autocvar_hud_panel_strafehud == 1 || autocvar_hud_panel_strafehud == 3)) return;
55         if(autocvar_hud_panel_strafehud == 3 && !(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return;
56     }
57
58     HUD_Panel_LoadCvars();
59
60     if(autocvar_hud_panel_strafehud_dynamichud)
61         HUD_Scale_Enable();
62     else
63         HUD_Scale_Disable();
64     HUD_Panel_DrawBg();
65     if(panel_bg_padding)
66     {
67         panel_pos  += '1 1 0' * panel_bg_padding;
68         panel_size -= '2 2 0' * panel_bg_padding;
69     }
70
71     if(spectatee_status > 0 || isdemo())
72     {
73         islocal = false;
74         strafeplayer = CSQCModel_server2csqc(player_localentnum - 1);
75     }
76     else
77     {
78         islocal = true;
79         strafeplayer = csqcplayer;
80     }
81
82     // draw strafehud
83     if(csqcplayer && strafeplayer)
84     {
85         // physics
86         bool   onground                      = islocal ? IS_ONGROUND(strafeplayer) : !(strafeplayer.anim_implicit_state & ANIMIMPLICITSTATE_INAIR);
87         bool   strafekeys;
88         bool   iswater                       = strafeplayer.waterlevel >= WATERLEVEL_SWIMMING;
89         float  speed                         = !autocvar__hud_configure ? vlen(vec2(csqcplayer.velocity)) : 1337; // use local csqcmodel entity for this even when spectating, flickers too much otherwise
90         float  maxspeed_crouch_mod           = IS_DUCKED(strafeplayer) && (!iswater || IS_ONGROUND(strafeplayer)) ? .5 : 1;
91         float  maxspeed_water_mod            = iswater ? .7 : 1; // FIXME: water physics are way more complex than this
92         float  maxspeed_phys                 = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
93         float  maxspeed                      = !autocvar__hud_configure ? maxspeed_phys * maxspeed_crouch_mod * maxspeed_water_mod : 320;
94         float  vel_angle                     = vectoangles(strafeplayer.velocity).y;
95         float  view_angle                    = view_angles.y + 180;
96         float  angle;
97         float  direction;
98         vector movement                      = PHYS_INPUT_MOVEVALUES(strafeplayer);
99         int    keys                          = STAT(PRESSED_KEYS);
100         int    keys_fwd;
101         float  wishangle                     = 0;
102         float  moveangle;
103
104         // HUD
105         int    mode                          = autocvar_hud_panel_strafehud_mode >= 0 && autocvar_hud_panel_strafehud_mode <= 1 ? autocvar_hud_panel_strafehud_mode : 0;
106         float  hudangle;
107         float  bar_offset;
108         float  bar_width;
109         vector currentangle_color            = autocvar_hud_panel_strafehud_warning_color;
110         float  currentangle_offset;
111         vector currentangle_size             = '0 0 0';
112         float  bestangle;
113         bool   bestangle_anywhere            = false;
114         float  bestangle_offset;
115         float  bestangle_width;
116         float  switch_bestangle_offset;
117         float  switch_bestangle_width;
118         float  accelzone_offset;
119         float  accelzone_width;
120         float  odd_accelzone_offset;
121         float  odd_accelzone_width;
122         float  overturn_offset;
123         float  overturn_width;
124         float  overturn_width_visible;
125         float  hidden_angle;
126         float  hidden_size;
127         vector direction_size_vertical       = '0 0 0';
128         vector direction_size_horizontal     = '0 0 0';
129         float  maxangle;
130         float  range_minangle;
131
132         // determine whether the player is pressing forwards or backwards keys
133         if(islocal) // if entity is local player
134         {
135             if(movement_x > 0)
136             {
137                 keys_fwd = 1;
138             }
139             else if(movement_x < 0)
140             {
141                 keys_fwd = -1;
142             }
143             else
144             {
145                 keys_fwd = 0;
146             }
147         }
148         else // alternatively determine direction by querying pressed keys
149         {
150             if((keys & KEY_FORWARD) && !(keys & KEY_BACKWARD))
151             {
152                 keys_fwd = 1;
153             }
154             else if(!(keys & KEY_FORWARD) && (keys & KEY_BACKWARD))
155             {
156                 keys_fwd = -1;
157             }
158             else
159             {
160                 keys_fwd = 0;
161             }
162         }
163
164         // determine player wishdir
165         if(islocal) // if entity is local player
166         {
167             if(movement_x == 0)
168             {
169                 if(movement_y < 0)
170                 {
171                     wishangle = -90;
172                 }
173                 else if(movement_y > 0)
174                 {
175                     wishangle = 90;
176                 }
177                 else
178                 {
179                     wishangle = 0;
180                 }
181             }
182             else
183             {
184                 if(movement_y == 0)
185                 {
186                     wishangle = 0;
187                 }
188                 else
189                 {
190                     wishangle = RAD2DEG * atan2(movement_y, movement_x);
191                     // wrap the wish angle if it exceeds Â±90°
192                     if(fabs(wishangle) > 90)
193                     {
194                         if(wishangle < 0) wishangle += 180;
195                         else wishangle -= 180;
196                         wishangle = -wishangle;
197                     }
198                 }
199             }
200         }
201         else // alternatively calculate wishdir by querying pressed keys
202         {
203             if(keys & KEY_FORWARD || keys & KEY_BACKWARD)
204             {
205                 wishangle = 45;
206             }
207             else
208             {
209                 wishangle = 90;
210             }
211             if(keys & KEY_LEFT)
212             {
213                 wishangle *= -1;
214             }
215             else if(!(keys & KEY_RIGHT))
216             {
217                 wishangle = 0; // wraps at 180°
218             }
219         }
220
221         strafekeys = fabs(wishangle) == 90;
222
223         // determine minimum required angle to display full strafe range
224         range_minangle = fabs(wishangle) % 90; // maximum range is 90 degree
225         if(range_minangle > 45) // minimum angle range is 45
226         {
227             range_minangle = 45 - fabs(wishangle) % 45;
228         }
229         range_minangle = 90 - range_minangle; // calculate value which is never >90 or <45
230         range_minangle *= 2; // multiply to accommodate for both sides of the hud
231
232         if(autocvar_hud_panel_strafehud_angle == 0)
233         {
234             if(autocvar__hud_configure)
235             {
236                 hudangle = 90;
237             }
238             else
239             {
240                 hudangle = range_minangle; // use minimum angle required if dynamically setting hud angle
241             }
242         }
243         else
244         {
245             hudangle = bound(0, fabs(autocvar_hud_panel_strafehud_angle), 360); // limit HUD range to 360 degrees, higher values don't make sense
246         }
247
248         // detect air strafe turning
249         if(onground != state_onground)
250         {
251             state_onground_time = time;
252         }
253         state_onground = onground;
254         if(strafekeys != state_strafekeys)
255         {
256             state_strafekeys_time = time;
257         }
258         state_strafekeys = strafekeys;
259         if((keys & KEY_FORWARD) || (keys & KEY_BACKWARD) || iswater || autocvar__hud_configure)
260         {
261             turn = false;
262         }
263         else if(onground)
264         {
265             if((time - state_onground_time) >= autocvar_hud_panel_strafehud_timeout_ground) // timeout for strafe jumping in general
266             {
267                 turn = false;
268             }
269         }
270         else // air strafe only
271         {
272             if(strafekeys)
273             {
274                 if(((time - state_onground_time) >= autocvar_hud_panel_strafehud_timeout_air) || (keys & KEY_JUMP)) // timeout for slick ramps
275                 {
276                     turn = true; // CPMA turning
277                     turnangle = wishangle;
278                 }
279             }
280             else if((time - state_strafekeys_time) >= autocvar_hud_panel_strafehud_timeout_strafe) // timeout for jumping with strafe keys only
281             {
282                 turn = false;
283             }
284         }
285         if(turn)
286         {
287             maxspeed = PHYS_MAXAIRSTRAFESPEED(strafeplayer); // no crouching here because it doesn't affect air strafing
288             wishangle = turnangle;
289         }
290
291         autocvar_hud_panel_strafehud_indicator_minspeed = autocvar_hud_panel_strafehud_indicator_minspeed < 0 ? maxspeed + .1 : autocvar_hud_panel_strafehud_indicator_minspeed;
292
293         // get current strafing angle ranging from -180° to +180°
294         if(!autocvar__hud_configure)
295         {
296             if(speed > 0)
297             {
298                 // calculate view angle relative to the players current velocity direction
299                 angle = vel_angle - view_angle;
300
301                 // if the angle goes above 180° or below -180° wrap it to the opposite side
302                 if (angle > 180) angle -= 360;
303                 else if(angle < -180) angle += 360;
304
305                 // shift the strafe angle by 180° for hud calculations
306                 if(angle < 0) angle += 180;
307                 else angle -= 180;
308
309                 // determine whether the player is strafing forwards or backwards
310                 // if the player isn't strafe turning use forwards/backwards keys to determine direction
311                 if(!strafekeys)
312                 {
313                     if(keys_fwd > 0)
314                     {
315                     state_fwd = true;
316                     }
317                     else if(keys_fwd < 0)
318                     {
319                         state_fwd = false;
320                     }
321                     else
322                     {
323                         state_fwd = fabs(angle) <= 90;
324                     }
325                 }
326                 // otherwise determine by examining the strafe angle
327                 else
328                 {
329                     if(wishangle < 0) // detect direction since the direction is not yet set
330                     {
331                         state_fwd = angle <= -wishangle;
332                     }
333                     else
334                     {
335                         state_fwd = angle >= -wishangle;
336                     }
337                 }
338
339                 if(state_fwd_prev != state_fwd)
340                 {
341                     state_direction_time = time;
342                 }
343                 state_fwd_prev = state_fwd;
344
345                 if((time - state_direction_time) >= autocvar_hud_panel_strafehud_timeout_direction) // timeout when changing between forwards and backwards strafe
346                 {
347                     fwd = state_fwd;
348                 }
349
350                 // shift the strafe angle by 180° when strafing backwards
351                 if(!fwd)
352                 {
353                     if(angle < 0) angle += 180;
354                     else angle -= 180;
355                 }
356
357                 // making the hud less flickery in case of rounding errors
358                 if(angle > 179.9 || angle < -179.9)
359                 {
360                     currentangle_color = autocvar_hud_panel_strafehud_alert_color;
361                     angle = 0;
362                 }
363                 if(angle < .1 && angle > -.1)
364                 {
365                     angle = 0;
366                 }
367             }
368             else
369             {
370                 angle = 0;
371             }
372         }
373         else // simulate turning for HUD setup
374         {
375             if(autocvar__hud_panel_strafehud_center)
376             {
377                 angle = demo_angle = 0;
378                 demo_time = 0;
379                 wishangle = 0;
380             }
381             else
382             {
383                 if(autocvar__hud_panel_strafehud_demo && ((time - demo_time) >= .025))
384                 {
385                     demo_time = time;
386                     demo_angle += demo_direction;
387                     if(fabs(demo_angle) >= 55)
388                     {
389                         demo_direction = -demo_direction;
390                     }
391                 }
392                 angle = demo_angle;
393                 wishangle = 45 * (demo_angle > 0 ? 1 : -1);
394             }
395         }
396
397         // invert the wish angle when strafing backwards
398         if(!fwd)
399         {
400             wishangle = -wishangle;
401         }
402
403         // flip angles if v_flipped is enabled
404         if(autocvar_v_flipped)
405         {
406             angle = -angle;
407             wishangle = -wishangle;
408         }
409
410         moveangle = angle + wishangle;
411
412         if(wishangle != 0)
413         {
414             direction = wishangle > 0 ? 1 : -1;
415         }
416         else
417         {
418             direction = moveangle > 0 ? 1 : moveangle < 0 ? -1 : 0;
419         }
420
421         // how much is hidden by the current hud angle
422         hidden_angle = 360 - hudangle;
423         hidden_size = hidden_angle / hudangle * panel_size.x;
424         // decelerating at this angle
425         maxangle = 90 - fabs(wishangle);
426         // best angle to strafe at
427         bestangle = (speed > maxspeed ? acos(maxspeed / speed) : 0) * RAD2DEG * (direction < 0 ? -1 : 1) - wishangle;
428         // various offsets and size calculations of hud indicator elements
429         // current angle
430         currentangle_size.x = panel_size.x * .005;
431         if(currentangle_size.x < 1) currentangle_size.x = 1;
432         if(mode == 0)
433         {
434             currentangle_offset = angle/hudangle * panel_size.x;
435         }
436         else
437         {
438             currentangle_offset = bound(-hudangle/2, angle, hudangle/2)/hudangle * panel_size.x + panel_size.x/2;
439         }
440         currentangle_size.y = panel_size.y * 1.5;
441         // best strafe acceleration angle
442         bestangle_offset        =  bestangle/hudangle * panel_size.x + panel_size.x/2;
443         switch_bestangle_offset = -bestangle/hudangle * panel_size.x + panel_size.x/2;
444         bestangle_width = panel_size.x * .01;
445         if(bestangle_width < 1) bestangle_width = 1;
446         switch_bestangle_width = bestangle_width;
447         // remove indicator width from offset
448         if(direction < 0)
449         {
450             bestangle_offset -= bestangle_width;
451         }
452         else
453         {
454             switch_bestangle_offset -= switch_bestangle_width;
455         }
456         // direction indicator
457         direction_size_vertical.x = panel_size.x * .0075;
458         if(direction_size_vertical.x < 1) direction_size_vertical.x = 1;
459         direction_size_vertical.y = panel_size.y;
460         direction_size_horizontal.x = direction_size_vertical.x * 3;
461         direction_size_horizontal.y = direction_size_vertical.x;
462         // overturn
463         overturn_width = 180/hudangle * panel_size.x;
464         overturn_width_visible = (hudangle/2 - maxangle) / hudangle * panel_size.x;
465
466         // if the strafe bar fills the whole hud panel
467         if(!(speed >= autocvar_hud_panel_strafehud_indicator_minspeed) || !(direction != 0))
468         {
469             // add a background to the strafe-o-meter
470             if(panel_size.x > 0 && panel_size.y > 0)
471             {
472                 if(!autocvar_hud_panel_strafehud_unstyled)
473                 {
474                     HUD_Panel_DrawProgressBar(panel_pos, panel_size, "progressbar", 1, 0, 0, autocvar_hud_panel_strafehud_bar_color, autocvar_hud_panel_strafehud_bar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
475                 }
476                 else
477                 {
478                     drawfill(panel_pos, panel_size, autocvar_hud_panel_strafehud_bar_color, autocvar_hud_panel_strafehud_bar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
479                 }
480             }
481         }
482
483         // mark the ideal strafe angle
484         if(speed >= autocvar_hud_panel_strafehud_indicator_minspeed) // only draw indicators if strafing is required to gain speed (or when whatever configured speed is reached)
485         {
486             if(direction != 0) // only draw acceleration zones if strafe direction can be determined
487             {
488                 if(direction < 0) // turning left
489                 {
490                     // calculate zone in which strafe acceleration happens
491                     accelzone_width = bestangle_offset;
492                     // calculate offset of overturn area
493                     overturn_offset = overturn_width_visible - overturn_width;
494                     // move/adjust acceleration zone
495                     accelzone_offset = overturn_width_visible;
496                     accelzone_width -= overturn_width_visible;
497                     // calculate zone in which strafe acceleration could also happen without changing wishdir
498                     odd_accelzone_width = accelzone_width + bestangle_width;
499                     odd_accelzone_offset = overturn_offset - odd_accelzone_width;
500                     // calculate the background of the strafe-o-meter
501                     bar_offset = bestangle_offset + bestangle_width;
502                     bar_width = 360/hudangle * panel_size.x - bestangle_width - accelzone_width - odd_accelzone_width - overturn_width;
503                 }
504                 else // turning right
505                 {
506                     // calculate zone in which strafe acceleration happens
507                     accelzone_offset = bestangle_offset + bestangle_width;
508                     accelzone_width = panel_size.x - accelzone_offset;
509                     // calculate offset of overturn area
510                     overturn_offset = panel_size.x - overturn_width_visible;
511                     // adjust acceleration zone
512                     accelzone_width -= overturn_width_visible;
513                     // calculate zone in which strafe acceleration could also happen without changing wishdir
514                     odd_accelzone_width = accelzone_width + bestangle_width;
515                     odd_accelzone_offset = overturn_offset + overturn_width;
516                     // calculate the background of the strafe-o-meter
517                     bar_offset = odd_accelzone_offset + odd_accelzone_width;
518                     bar_width = 360/hudangle * panel_size.x - bestangle_width - accelzone_width - odd_accelzone_width - overturn_width;
519                 }
520
521                 if(mode == 0)
522                 {
523                     bar_offset -= currentangle_offset;
524                     accelzone_offset -= currentangle_offset;
525                     odd_accelzone_offset -= currentangle_offset;
526                     overturn_offset -= currentangle_offset;
527                     bestangle_offset -= currentangle_offset;
528                     switch_bestangle_offset -= currentangle_offset;
529                 }
530
531                 if(!autocvar_hud_panel_strafehud_unstyled)
532                 {
533                     // draw acceleration zone
534                     HUD_Panel_DrawStrafeHUD_ProgressBar(accelzone_offset, accelzone_width, autocvar_hud_panel_strafehud_indicator_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
535
536                     // draw odd acceleration zone
537                     HUD_Panel_DrawStrafeHUD_ProgressBar(odd_accelzone_offset, odd_accelzone_width, autocvar_hud_panel_strafehud_indicator_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
538
539                     // draw overturn area
540                     HUD_Panel_DrawStrafeHUD_ProgressBar(overturn_offset, overturn_width, autocvar_hud_panel_strafehud_alert_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
541
542                     // draw the strafe bar background
543                     HUD_Panel_DrawStrafeHUD_ProgressBar(bar_offset, bar_width, autocvar_hud_panel_strafehud_bar_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
544                 }
545                 else
546                 {
547                     // draw acceleration zone
548                     HUD_Panel_DrawStrafeHUD_drawfill(accelzone_offset, accelzone_width, autocvar_hud_panel_strafehud_indicator_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
549
550                     // draw odd acceleration zone
551                     HUD_Panel_DrawStrafeHUD_drawfill(odd_accelzone_offset, odd_accelzone_width, autocvar_hud_panel_strafehud_indicator_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
552
553                     // draw overturn area
554                     HUD_Panel_DrawStrafeHUD_drawfill(overturn_offset, overturn_width, autocvar_hud_panel_strafehud_alert_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
555
556                     // draw the strafe bar background
557                     HUD_Panel_DrawStrafeHUD_drawfill(bar_offset, bar_width, autocvar_hud_panel_strafehud_bar_color, autocvar_hud_panel_strafehud_bar_alpha, hidden_size);
558                 }
559
560                 // draw the direction indicator caps at the sides of the hud
561                 // vertical line
562                 drawfill(panel_pos + eX * (direction < 0 ? -direction_size_vertical.x : panel_size.x), direction_size_vertical, autocvar_hud_panel_strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
563                 // top horizontal line
564                 drawfill(panel_pos + eX * (direction < 0 ? -direction_size_vertical.x : panel_size.x - direction_size_horizontal.x + direction_size_vertical.x) - eY * direction_size_horizontal.y, direction_size_horizontal, autocvar_hud_panel_strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
565                 // bottom horizontal line
566                 drawfill(panel_pos + eX * (direction < 0 ? -direction_size_vertical.x : panel_size.x - direction_size_horizontal.x + direction_size_vertical.x) + eY * panel_size.y, direction_size_horizontal, autocvar_hud_panel_strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
567
568                 // draw best angles for acceleration
569                 HUD_Panel_DrawStrafeHUD_drawfill(switch_bestangle_offset, switch_bestangle_width, autocvar_hud_panel_strafehud_indicator_switch_color, 1, hidden_size);
570                 HUD_Panel_DrawStrafeHUD_drawfill(bestangle_offset, bestangle_width, autocvar_hud_panel_strafehud_indicator_color, 1, hidden_size);
571             }
572             else
573             {
574                 // draw best angles for acceleration
575                 HUD_Panel_DrawStrafeHUD_drawfill(switch_bestangle_offset, switch_bestangle_width, autocvar_hud_panel_strafehud_indicator_switch_color, 1, hidden_size);
576                 HUD_Panel_DrawStrafeHUD_drawfill(bestangle_offset, bestangle_width, autocvar_hud_panel_strafehud_indicator_switch_color, 1, hidden_size);
577             }
578         }
579         else
580         {
581             bestangle_anywhere = true; // no indicators, moving forward should suffice to gain speed
582         }
583
584         // draw the actual strafe angle
585         if(!bestangle_anywhere) // player gains speed with strafing
586         {
587             if((direction > 0 && (angle >= bestangle || angle <= -(bestangle + wishangle*2))) ||
588                 (direction < 0 && (angle <= bestangle || angle >= -(bestangle + wishangle*2))))
589             currentangle_color = autocvar_hud_panel_strafehud_good_color;
590         }
591
592         if(fabs(moveangle) > 90) // player is overturning
593         {
594             currentangle_color = autocvar_hud_panel_strafehud_alert_color;
595         }
596
597         if(speed <= (maxspeed + .1) && currentangle_color != autocvar_hud_panel_strafehud_alert_color) // player gains speed without strafing
598         {
599             currentangle_color = autocvar_hud_panel_strafehud_good_color;
600         }
601
602         if(mode == 0)
603         {
604             drawfill(panel_pos - eY * ((currentangle_size.y - panel_size.y) / 2) + eX * (panel_size.x/2 - currentangle_size.x/2), currentangle_size, currentangle_color, autocvar_hud_panel_strafehud_angle_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
605         }
606         else
607         {
608             drawfill(panel_pos - eY * ((currentangle_size.y - panel_size.y) / 2) + eX * (currentangle_offset - currentangle_size.x/2), currentangle_size, currentangle_color, autocvar_hud_panel_strafehud_angle_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
609         }
610     }
611 }
612
613 // functions to make hud elements align perfectly in the hud area
614 void HUD_Panel_DrawStrafeHUD_ProgressBar(float offset, float width, vector color, float alpha, float hidden)
615 {
616     float mirror_offset, mirror_width;
617     vector size = panel_size;
618     vector mirror_size = panel_size;
619
620     if(offset < 0)
621     {
622         mirror_width = min(fabs(offset), width);
623         mirror_offset = panel_size.x + hidden - fabs(offset);
624         width += offset;
625         offset = 0;
626     }
627     else
628     {
629         mirror_width = min(offset + width - panel_size.x - hidden, width);
630         mirror_offset = max(offset - panel_size.x - hidden, 0);
631     }
632     if((offset + width) > panel_size.x)
633     {
634         width = panel_size.x - offset;
635     }
636     if(mirror_offset < 0)
637     {
638         mirror_width += mirror_offset;
639         mirror_offset = 0;
640     }
641     if((mirror_offset + mirror_width) > panel_size.x)
642     {
643         mirror_width = panel_size.x - mirror_offset;
644     }
645
646     size.x = width;
647     mirror_size.x = mirror_width;
648     if(mirror_size.x > 0 && mirror_size.y > 0) HUD_Panel_DrawProgressBar(panel_pos + eX * mirror_offset, mirror_size, "progressbar", 1, 0, 0, color, alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
649     if(size.x > 0 && size.y > 0) HUD_Panel_DrawProgressBar(panel_pos + eX * offset, size, "progressbar", 1, 0, 0, color, alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
650 }
651
652 void HUD_Panel_DrawStrafeHUD_drawfill(float offset, float width, vector color, float alpha, float hidden)
653 {
654     float mirror_offset, mirror_width;
655     vector size = panel_size;
656     vector mirror_size = panel_size;
657
658     if(offset < 0)
659     {
660         mirror_width = min(fabs(offset), width);
661         mirror_offset = panel_size.x + hidden - fabs(offset);
662         width += offset;
663         offset = 0;
664     }
665     else
666     {
667         mirror_width = min(offset + width - panel_size.x - hidden, width);
668         mirror_offset = max(offset - panel_size.x - hidden, 0);
669     }
670     if((offset + width) > panel_size.x)
671     {
672         width = panel_size.x - offset;
673     }
674     if(mirror_offset < 0)
675     {
676         mirror_width += mirror_offset;
677         mirror_offset = 0;
678     }
679     if((mirror_offset + mirror_width) > panel_size.x)
680     {
681         mirror_width = panel_size.x - mirror_offset;
682     }
683
684     size.x = width;
685     mirror_size.x = mirror_width;
686     if(mirror_width > 0) drawfill(panel_pos + eX * mirror_offset, mirror_size, color, alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
687     if(width > 0) drawfill(panel_pos + eX * offset, size, color, alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
688 }