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