4 #include "strafehud.qh"
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>
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;
31 if(!autocvar__hud_configure)
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;
38 HUD_Panel_LoadCvars();
40 if (autocvar_hud_panel_strafehud_dynamichud)
47 panel_pos += '1 1 0' * panel_bg_padding;
48 panel_size -= '2 2 0' * panel_bg_padding;
51 if(spectatee_status > 0)
53 strafeplayer = CSQCModel_server2csqc(player_localentnum - 1);
57 strafeplayer = csqcplayer;
61 if(csqcplayer && strafeplayer)
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_bestangle_opposite_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;
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;
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 = 0;
101 bool strafehud_bestangle_anywhere = false;
102 float strafehud_bestangle_offset;
103 float strafehud_bestangle_opposite_offset;
104 vector strafehud_bestangle_size = '0 0 0';
105 float strafehud_accelzone_offset;
106 vector strafehud_accelzone_size = panel_size;
107 float strafehud_overturn_offset;
108 vector strafehud_overturn_size = panel_size;
109 float strafehud_mirrorangle;
110 float strafehud_mirror_overturn_offset = 0;
111 vector strafehud_mirror_overturn_size = panel_size;
112 vector strafehud_direction_size_vertical = '0 0 0';
113 vector strafehud_direction_size_horizontal = '0 0 0';
114 float strafehud_maxangle;
115 float strafehud_range_minangle;
117 // determine whether the player is strafing forwards or backwards
118 if(strafeplayer == csqcplayer) // if entity is local player
120 if(strafehud_movement_x > 0)
122 strafehud_fwd = true;
124 else if(strafehud_movement_x < 0)
126 strafehud_fwd = false;
129 else // alternatively determine direction by querying pressed keys
131 if((strafehud_keys & KEY_FORWARD) && !(strafehud_keys & KEY_BACKWARD))
133 strafehud_fwd = true;
135 else if(!(strafehud_keys & KEY_FORWARD) && (strafehud_keys & KEY_BACKWARD))
137 strafehud_fwd = false;
141 // determine player wishdir
142 if(strafeplayer == csqcplayer) // if entity is local player
144 if(strafehud_movement_x == 0)
146 if(strafehud_movement_y < 0)
148 strafehud_wishangle = -90;
150 else if(strafehud_movement_y > 0)
152 strafehud_wishangle = 90;
156 strafehud_wishangle = 0;
161 if(strafehud_movement_y == 0)
163 strafehud_wishangle = 0;
167 strafehud_wishangle = RAD2DEG * atan2(strafehud_movement_y, strafehud_movement_x);
171 else // alternatively calculate wishdir by querying pressed keys
173 if(strafehud_keys & KEY_FORWARD)
175 strafehud_wishangle = 45;
177 else if(strafehud_keys & KEY_BACKWARD)
179 strafehud_wishangle = 135;
183 strafehud_wishangle = 90;
185 if(strafehud_keys & KEY_LEFT)
187 strafehud_wishangle *= -1;
189 else if(!(strafehud_keys & KEY_RIGHT))
191 strafehud_wishangle = 0;
195 // determine minimum required angle to display full strafe range
196 strafehud_range_minangle = fabs(strafehud_wishangle) % 90; // maximum range is 90 degree
197 if(strafehud_range_minangle > 45) // minimum angle range is 45
199 strafehud_range_minangle = 45 - fabs(strafehud_wishangle) % 45;
201 strafehud_range_minangle = 90 - strafehud_range_minangle; // calculate value which is never >90 or <45
203 if(autocvar_hud_panel_strafehud_angle == 0)
205 if(autocvar__hud_configure)
207 strafehud_hudangle = 45;
211 strafehud_hudangle = strafehud_range_minangle; // use minimum angle required if dynamically setting hud angle
216 strafehud_hudangle = bound(1, fabs(autocvar_hud_panel_strafehud_angle), 360) / 2; // limit HUD range to 360 degrees, higher values don't make sense and break the code
219 // detecting strafe turning
220 if(!autocvar__hud_configure)
222 if(strafehud_onground != strafehud_state_onground)
224 strafehud_state_onground_time = time;
226 strafehud_state_onground = strafehud_onground;
227 if((fabs(strafehud_wishangle) == 90) != strafehud_state_strafekeys)
229 strafehud_state_strafekeys_time = time;
231 strafehud_state_strafekeys = fabs(strafehud_wishangle) == 90;
232 if((strafehud_keys & KEY_FORWARD) || (strafehud_keys & KEY_BACKWARD))
234 strafehud_turn = false;
236 else if(strafehud_onground)
238 if((time - strafehud_state_onground_time) >= strafehud_timeout_ground)
240 strafehud_turn = false;
243 else // air strafe only
245 if(fabs(strafehud_wishangle) == 90)
247 if((time - strafehud_state_onground_time) >= strafehud_timeout_air)
249 strafehud_turn = true; // CPMA turning
250 strafehud_turnangle = strafehud_wishangle;
253 else if((time - strafehud_state_strafekeys_time) >= strafehud_timeout_strafe)
255 strafehud_turn = false;
260 strafehud_maxspeed = PHYS_MAXAIRSTRAFESPEED(strafeplayer) * strafehud_maxspeed_swamp_mod; // no crouching here because it doesn't affect air strafing
261 strafehud_wishangle = strafehud_turnangle;
265 strafehud_indicator_minspeed = strafehud_indicator_minspeed < 0 ? strafehud_maxspeed + .1 : strafehud_indicator_minspeed;
267 // get current strafing angle ranging from -180° to +180°
268 if(!autocvar__hud_configure)
270 if(!strafehud_fwd) strafehud_wishangle += strafehud_wishangle < 0 ? 180 : strafehud_wishangle > 0 ? -180 : 0;
271 if(strafehud_speed > 0)
273 if(!strafehud_fwd) strafehud_view_angle += strafehud_view_angle < 0 ? 180 : strafehud_view_angle > 0 ? -180 : 0;
274 strafehud_angle = strafehud_view_angle - strafehud_vel_angle;
276 if (strafehud_angle > 180) strafehud_angle = -360 + strafehud_angle;
277 else if(strafehud_angle < -180) strafehud_angle = 360 + strafehud_angle;
279 strafehud_angle = 180 - strafehud_angle;
280 if(strafehud_angle > 180)
282 strafehud_angle = -fabs(360 - strafehud_angle);
285 // making the hud less flickery in case of rounding errors
286 if(strafehud_angle > 179.9 || strafehud_angle < -179.9)
288 strafehud_currentangle_color = strafehud_alert_color;
291 if(strafehud_angle < .1 && strafehud_angle > -.1)
301 else // simulate turning for HUD setup
303 if(autocvar__hud_panel_strafehud_center)
305 strafehud_angle = strafehud_demo_angle = 0;
306 strafehud_demo_time = 0;
307 strafehud_wishangle = 0;
311 if(autocvar__hud_panel_strafehud_demo && ((time - strafehud_demo_time) >= .025))
313 strafehud_demo_time = time;
314 strafehud_demo_angle += strafehud_demo_direction;
315 if(fabs(strafehud_demo_angle) >= 55)
317 strafehud_demo_direction = -strafehud_demo_direction;
320 strafehud_angle = strafehud_demo_angle;
321 strafehud_wishangle = 45 * (strafehud_demo_angle > 0 ? 1 : -1);
325 if (autocvar_v_flipped)
327 strafehud_angle = -strafehud_angle;
328 strafehud_wishangle = -strafehud_wishangle;
331 strafehud_moveangle = strafehud_angle + strafehud_wishangle;
333 if(strafehud_wishangle != 0)
335 strafehud_direction = strafehud_wishangle > 0 ? 1 : -1;
339 strafehud_direction = strafehud_moveangle > 0 ? 1 : strafehud_moveangle < 0 ? -1 : 0;
342 // decelerating at this angle
343 strafehud_maxangle = 90 - fabs(strafehud_wishangle);
344 // best angle to strafe at
345 strafehud_bestangle = (strafehud_speed > strafehud_maxspeed ? acos(strafehud_maxspeed / strafehud_speed) : 0) * RAD2DEG * (strafehud_direction < 0 ? -1 : 1) - strafehud_wishangle;
346 // various offsets and size calculations of hud indicators elements
347 // best strafe acceleration angle
348 strafehud_bestangle_offset = floor( strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
349 strafehud_bestangle_opposite_offset = floor(-strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
350 strafehud_bestangle_size.x = floor(panel_size.x * .01 + .5);
351 strafehud_bestangle_size.y = panel_size.y;
353 strafehud_currentangle_offset = floor(bound(-strafehud_hudangle, strafehud_angle, strafehud_hudangle)/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
354 strafehud_currentangle_size.x = floor(panel_size.x * .005 + .5);
355 strafehud_currentangle_size.y = floor(panel_size.y * 1.5 + .5);
356 // direction indicator
357 strafehud_direction_size_vertical.x = floor(panel_size.x * .0075 + .5);
358 strafehud_direction_size_vertical.y = panel_size.y;
359 strafehud_direction_size_horizontal.x = floor(strafehud_direction_size_vertical.x * 3 + .5);
360 strafehud_direction_size_horizontal.y = strafehud_direction_size_vertical.x;
362 strafehud_mirrorangle = strafehud_maxangle + strafehud_hudangle - 180; // how many degrees of overturn area are on the opposite side of the hud
363 strafehud_overturn_size.x = floor((panel_size.x * (strafehud_hudangle - strafehud_maxangle) / strafehud_hudangle) / 2 + .5);
364 strafehud_mirror_overturn_size.x = floor(panel_size.x * strafehud_mirrorangle / (strafehud_hudangle * 2) + .5);
366 // if the strafe bar fills the whole hud panel
367 if(!(strafehud_speed >= strafehud_indicator_minspeed) || !(fabs(strafehud_bestangle) <= strafehud_hudangle) || !(strafehud_direction != 0))
369 // add a background to the strafe-o-meter
370 if(panel_size.x != 0 && panel_size.y != 0)
372 HUD_Panel_DrawProgressBar(panel_pos, panel_size, "progressbar", 1, 0, 0, strafehud_bar_color, strafehud_bar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
376 // mark the ideal strafe angle
377 if(strafehud_speed >= strafehud_indicator_minspeed) // draw indicators if strafing is required to gain speed
379 if (fabs(strafehud_bestangle) <= strafehud_hudangle) // don't draw angle indicator and acceleration zones if outside of hud range
381 if (strafehud_direction != 0) // only draw acceleration zones if strafe direction can be determined
383 // calculate zone in which strafe acceleration happens
384 if(strafehud_direction < 0) // moving left
386 strafehud_accelzone_offset = 0;
387 strafehud_accelzone_size.x = strafehud_bestangle_offset;
391 strafehud_accelzone_offset = strafehud_bestangle_offset + strafehud_bestangle_size.x;
392 strafehud_accelzone_size.x = panel_size.x - strafehud_accelzone_offset;
395 if(strafehud_hudangle > strafehud_maxangle) // draw overturn area and move acceleration zone
397 if(strafehud_direction < 0) // moving left
399 // calculate offset of overturn area
400 strafehud_overturn_offset = 0;
401 // move/adjust acceleration zone
402 strafehud_accelzone_offset += strafehud_overturn_size.x;
403 strafehud_accelzone_size.x -= strafehud_overturn_size.x;
404 // calculate the remainder of the overturn zone on the opposite side
405 strafehud_mirror_overturn_offset = panel_size.x - strafehud_mirror_overturn_size.x;
409 // calculate offset of overturn area
410 strafehud_overturn_offset = panel_size.x - strafehud_overturn_size.x;
411 // adjust acceleration zone
412 strafehud_accelzone_size.x -= strafehud_overturn_size.x;
413 // calculate the remainder of the overturn zone on the opposite side
414 strafehud_mirror_overturn_offset = 0;
416 // draw overturn area
417 if(strafehud_overturn_size.x != 0 && strafehud_overturn_size.y != 0)
419 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);
421 // draw remaining overturn area on the opposite side if there is any (180 degree in total)
422 if(strafehud_mirrorangle > 0 && strafehud_mirror_overturn_size.x != 0 && strafehud_mirror_overturn_size.y != 0)
424 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);
428 // draw acceleration zone
429 if(strafehud_accelzone_size.x != 0 && strafehud_accelzone_size.y != 0)
431 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);
434 // add a background to the strafe-o-meter
435 if(strafehud_direction < 0)
437 strafehud_bar_offset = strafehud_accelzone_offset + strafehud_accelzone_size.x;
438 strafehud_bar_size.x = panel_size.x - strafehud_bar_offset - (panel_size.x - (strafehud_mirrorangle > 0 ? strafehud_mirror_overturn_offset : panel_size.x));
442 strafehud_bar_offset = strafehud_mirrorangle > 0 ? strafehud_mirror_overturn_size.x : 0;
443 strafehud_bar_size.x = panel_size.x - strafehud_bar_offset - (panel_size.x - strafehud_accelzone_offset);
445 if(strafehud_bar_size.x != 0 && strafehud_bar_size.y != 0)
447 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);
450 // draw the direction indicator caps at the sides of the hud
452 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);
453 // top horizontal line
454 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);
455 // bottom horizontal line
456 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);
458 // draw opposite best strafe angle
459 drawfill(panel_pos + eX * (strafehud_bestangle_opposite_offset - (-strafehud_direction < 0 ? strafehud_bestangle_size.x : 0)), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
460 // draw current best strafe angle
461 drawfill(panel_pos + eX * (strafehud_bestangle_offset - (strafehud_direction < 0 ? strafehud_bestangle_size.x : 0)), strafehud_bestangle_size, strafehud_bestangle_color, panel_fg_alpha, DRAWFLAG_NORMAL);
465 // draw best angles for acceleration
466 drawfill(panel_pos + eX * (strafehud_bestangle_opposite_offset - strafehud_bestangle_size.x), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
467 drawfill(panel_pos + eX * (strafehud_bestangle_offset), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
473 strafehud_bestangle_anywhere = true; // no indicators, moving forward should suffice to gain speed
476 // draw the actual strafe angle
477 if (!strafehud_bestangle_anywhere) // player gains speed with strafing
479 if ((strafehud_direction > 0 && strafehud_angle >= strafehud_bestangle) ||
480 (strafehud_direction < 0 && strafehud_angle <= strafehud_bestangle))
481 strafehud_currentangle_color = strafehud_good_color;
484 if (fabs(strafehud_moveangle) > 89.9) // player is overturning
486 strafehud_currentangle_color = strafehud_alert_color;
489 if (strafehud_speed <= (strafehud_maxspeed + .1) && strafehud_currentangle_color != strafehud_alert_color) // player gains speed without strafing
491 strafehud_currentangle_color = strafehud_good_color;
494 drawfill(panel_pos - '0 1 0'*floor(panel_size.y * .25 + .5) + 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);