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