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