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