4 // FIXME: strafehud doesn't work properly in spectate due to lack of IS_ONGROUND()
6 #include "strafehud.qh"
8 #include <client/autocvars.qh>
9 #include <client/miscfunctions.qh>
10 #include <common/ent_cs.qh>
11 #include <common/mapinfo.qh>
12 #include <common/mapobjects/trigger/swamp.qh>
13 #include <common/physics/movetypes/movetypes.qh>
14 #include <common/physics/player.qh>
15 #include <lib/csqcmodel/cl_player.qh>
17 bool strafehud_fwd = true;
18 float strafehud_demo_angle = -37;
19 float strafehud_demo_direction = 1;
20 float strafehud_demo_time = 0;
21 float strafehud_state_onground_time = 0;
22 float strafehud_state_strafekeys_time = 0;
23 bool strafehud_state_onground = true;
24 bool strafehud_state_strafekeys = false;
25 bool strafehud_turn = false;
26 float strafehud_turnangle;
32 if(!autocvar__hud_configure)
34 if(!autocvar_hud_panel_strafehud) return;
35 if(spectatee_status == -1 && (autocvar_hud_panel_strafehud == 1 || autocvar_hud_panel_strafehud == 3)) return;
36 if(autocvar_hud_panel_strafehud == 3 && !(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return;
39 HUD_Panel_LoadCvars();
41 if (autocvar_hud_panel_strafehud_dynamichud)
48 panel_pos += '1 1 0' * panel_bg_padding;
49 panel_size -= '2 2 0' * panel_bg_padding;
52 if(spectatee_status > 0)
54 strafeplayer = CSQCModel_server2csqc(player_localentnum - 1);
58 strafeplayer = csqcplayer;
62 if(csqcplayer && strafeplayer)
65 float strafehud_bar_alpha = autocvar_hud_panel_strafehud_bar_alpha;
66 vector strafehud_bar_color = autocvar_hud_panel_strafehud_bar_color;
67 vector strafehud_bestangle_color = autocvar_hud_panel_strafehud_indicator_color;
68 vector strafehud_bestangle_opposite_color = autocvar_hud_panel_strafehud_indicator_switch_color;
69 vector strafehud_good_color = autocvar_hud_panel_strafehud_good_color;
70 vector strafehud_warning_color = autocvar_hud_panel_strafehud_warning_color;
71 vector strafehud_alert_color = autocvar_hud_panel_strafehud_alert_color;
72 vector strafehud_direction_color = autocvar_hud_panel_strafehud_direction_color;
73 float strafehud_timeout_air = autocvar_hud_panel_strafehud_timeout_air; // timeout for slick ramps
74 float strafehud_timeout_ground = autocvar_hud_panel_strafehud_timeout_ground; // timeout for strafe jumping in general
75 float strafehud_timeout_strafe = autocvar_hud_panel_strafehud_timeout_strafe; // timeout for jumping with strafe keys only
76 float strafehud_indicator_minspeed = autocvar_hud_panel_strafehud_indicator_minspeed;
79 float strafehud_onground = IS_ONGROUND(strafeplayer);
80 float strafehud_speed = !autocvar__hud_configure ? vlen(vec2(csqcplayer.velocity)) : 1337; // use local csqcmodel entity for this even when spectating, flickers too much otherwise
81 float strafehud_maxspeed_crouch_mod = IS_DUCKED(strafeplayer) ? .5 : 1;
82 float strafehud_maxspeed_swamp_mod = strafeplayer.in_swamp ? strafeplayer.swamp_slowdown : 1;
83 float strafehud_maxspeed_phys = strafehud_onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
84 float strafehud_maxspeed = !autocvar__hud_configure ? (strafehud_maxspeed_phys * strafehud_maxspeed_crouch_mod * strafehud_maxspeed_swamp_mod) : 320;
85 float strafehud_vel_angle = vectoangles(strafeplayer.velocity).y;
86 float strafehud_view_angle = view_angles.y + 180;
87 float strafehud_angle;
88 float strafehud_direction;
89 vector strafehud_movement = PHYS_INPUT_MOVEVALUES(strafeplayer);
90 int strafehud_keys = STAT(PRESSED_KEYS);
91 float strafehud_wishangle;
92 float strafehud_shiftangle;
93 float strafehud_moveangle;
96 float strafehud_hudangle;
97 vector strafehud_currentangle_color = strafehud_warning_color;
98 vector strafehud_currentangle_size = '0 0 0';
99 float strafehud_currentangle_offset;
100 vector strafehud_bestangle_size = '0 0 0';
101 bool strafehud_bestangle_visible = true;
102 float strafehud_bestangle = 0;
103 float strafehud_bestangle_offset;
104 float strafehud_accelzone_offset;
105 vector strafehud_accelzone_size;
106 float strafehud_overturn_offset;
107 vector strafehud_overturn_size;
108 float strafehud_mirror_overturn_offset;
109 vector strafehud_mirror_overturn_size;
110 vector strafehud_direction_size_1 = '0 0 0';
111 vector strafehud_direction_size_2 = '0 0 0';
113 strafehud_indicator_minspeed = strafehud_indicator_minspeed < 0 ? strafehud_maxspeed + .1 : strafehud_indicator_minspeed;
115 // determine whether the player is strafing forwards or backwards
116 if(strafeplayer == csqcplayer) // if entity is local player
118 if(strafehud_movement_x > 0)
120 strafehud_fwd = true;
122 else if(strafehud_movement_x < 0)
124 strafehud_fwd = false;
127 else // alternatively determine direction by querying pressed keys
129 if((strafehud_keys & KEY_FORWARD) && !(strafehud_keys & KEY_BACKWARD))
131 strafehud_fwd = true;
133 else if(!(strafehud_keys & KEY_FORWARD) && (strafehud_keys & KEY_BACKWARD))
135 strafehud_fwd = false;
139 // determine player wishdir
140 if(strafeplayer == csqcplayer) // if entity is local player
142 if(strafehud_movement_x == 0)
144 if(strafehud_movement_y < 0)
146 strafehud_wishangle = -90;
148 else if(strafehud_movement_y > 0)
150 strafehud_wishangle = 90;
154 strafehud_wishangle = 0;
159 if(strafehud_movement_y == 0)
161 strafehud_wishangle = 0;
165 strafehud_wishangle = RAD2DEG * atan2(strafehud_movement_y, strafehud_movement_x);
169 else // alternatively calculate wishdir by querying pressed keys
171 if(strafehud_keys & KEY_FORWARD)
173 strafehud_wishangle = 45;
175 else if(strafehud_keys & KEY_BACKWARD)
177 strafehud_wishangle = 135;
181 strafehud_wishangle = 90;
183 if(strafehud_keys & KEY_LEFT)
185 strafehud_wishangle *= -1;
187 else if(!(strafehud_keys & KEY_RIGHT))
189 strafehud_wishangle = 0;
193 // determine how much the angle shifts in the hud
194 strafehud_shiftangle = fabs(remainder(strafehud_wishangle, 90));
195 if(strafehud_shiftangle > 45)
197 strafehud_shiftangle = 45 - fabs(remainder(strafehud_wishangle, 45));
199 strafehud_shiftangle = 90 - strafehud_shiftangle;
201 if(autocvar_hud_panel_strafehud_angle == 0)
203 if(autocvar__hud_configure)
205 strafehud_hudangle = 45;
209 strafehud_hudangle = strafehud_shiftangle;
214 strafehud_hudangle = bound(1, fabs(autocvar_hud_panel_strafehud_angle), 360) / 2; // sanity check this cvar for now
217 // detecting strafe turning
218 if(!autocvar__hud_configure)
220 if(strafehud_onground != strafehud_state_onground)
222 strafehud_state_onground_time = time;
224 strafehud_state_onground = strafehud_onground;
225 if((fabs(strafehud_wishangle) == 90) != strafehud_state_strafekeys)
227 strafehud_state_strafekeys_time = time;
229 strafehud_state_strafekeys = fabs(strafehud_wishangle) == 90;
230 if((strafehud_keys & KEY_FORWARD) || (strafehud_keys & KEY_BACKWARD))
232 strafehud_turn = false;
234 else if(strafehud_onground)
236 if((time - strafehud_state_onground_time) >= strafehud_timeout_ground)
238 strafehud_turn = false;
241 else // air strafe only
243 if(fabs(strafehud_wishangle) == 90)
245 if((time - strafehud_state_onground_time) >= strafehud_timeout_air)
247 strafehud_turn = true; // CPMA turning
248 strafehud_turnangle = strafehud_wishangle;
251 else if((time - strafehud_state_strafekeys_time) >= strafehud_timeout_strafe)
253 strafehud_turn = false;
258 strafehud_maxspeed = PHYS_MAXAIRSTRAFESPEED(strafeplayer) * strafehud_maxspeed_swamp_mod; // no crouching here because it doesn't affect air strafing
259 strafehud_wishangle = strafehud_turnangle;
263 // add a background to the strafe-o-meter
264 HUD_Panel_DrawProgressBar(panel_pos, panel_size, "progressbar", 1, 0, 0, strafehud_bar_color, strafehud_bar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
266 // get current strafing angle ranging from -180° to +180°
267 if(!autocvar__hud_configure)
269 if(!strafehud_fwd) strafehud_wishangle += strafehud_wishangle < 0 ? 180 : strafehud_wishangle > 0 ? -180 : 0;
270 if(strafehud_speed > 0)
272 if(!strafehud_fwd) strafehud_view_angle += strafehud_view_angle < 0 ? 180 : strafehud_view_angle > 0 ? -180 : 0;
273 strafehud_angle = strafehud_view_angle - strafehud_vel_angle;
275 if (strafehud_angle > 180) strafehud_angle = -360 + strafehud_angle;
276 else if(strafehud_angle < -180) strafehud_angle = 360 + strafehud_angle;
278 strafehud_angle = 180 - strafehud_angle;
279 if(strafehud_angle > 180)
281 strafehud_angle = -fabs(360 - strafehud_angle);
284 // making the hud less flickery in case of rounding errors
285 if(strafehud_angle > 179.9 || strafehud_angle < -179.9)
287 strafehud_currentangle_color = strafehud_alert_color;
290 if(strafehud_angle < .1 && strafehud_angle > -.1)
300 else // simulate turning for HUD setup
302 if(autocvar__hud_panel_strafehud_demo && ((time - strafehud_demo_time) >= .025))
304 strafehud_demo_time = time;
305 strafehud_demo_angle += 1 * strafehud_demo_direction;
306 if(fabs(strafehud_demo_angle) >= 55)
308 strafehud_demo_direction = -strafehud_demo_direction;
311 strafehud_angle = strafehud_demo_angle;
312 strafehud_wishangle = 45 * (strafehud_demo_angle > 0 ? 1 : -1);
315 if (autocvar_v_flipped)
317 strafehud_angle = -strafehud_angle;
318 strafehud_wishangle = -strafehud_wishangle;
321 strafehud_moveangle = strafehud_angle + strafehud_wishangle;
323 if(strafehud_wishangle != 0)
325 strafehud_direction = strafehud_wishangle > 0 ? 1 : -1;
329 strafehud_direction = strafehud_moveangle > 0 ? 1 : strafehud_moveangle < 0 ? -1 : 0;
332 switch(autocvar_hud_panel_strafehud_mode)
335 case 0: // view centered
337 // mark the ideal strafe angle
338 if(strafehud_speed >= strafehud_indicator_minspeed)
340 strafehud_bestangle_size.x = floor(panel_size.x * .01 + .5);
341 strafehud_bestangle_size.y = floor(panel_size.y + .5);
342 if (strafehud_direction != 0)
344 strafehud_bestangle = (strafehud_speed > strafehud_maxspeed ? acos(strafehud_maxspeed / strafehud_speed) : 0) * RAD2DEG * strafehud_direction - strafehud_wishangle;
345 if (fabs(strafehud_bestangle) <= strafehud_hudangle)
347 float strafehud_maxangle = 90 - fabs(strafehud_wishangle);
348 strafehud_bestangle_offset = floor(strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
349 strafehud_accelzone_offset = strafehud_direction < 0 ? 0 : strafehud_bestangle_offset + strafehud_bestangle_size.x;
350 strafehud_accelzone_size = panel_size;
351 strafehud_accelzone_size.x = strafehud_direction < 0 ? strafehud_bestangle_offset : panel_size.x - strafehud_accelzone_offset;
352 if(strafehud_hudangle > strafehud_maxangle)
354 float strafehud_mirrorangle = 90 - strafehud_shiftangle - (180 - strafehud_hudangle);
355 strafehud_overturn_size = strafehud_mirror_overturn_size = panel_size;
356 strafehud_overturn_size.x = floor((panel_size.x * (strafehud_hudangle - strafehud_maxangle) / strafehud_hudangle) / 2 + .5);
357 strafehud_mirror_overturn_size.x = panel_size.x * strafehud_mirrorangle / 360;
358 if(strafehud_direction < 0)
360 strafehud_overturn_offset = 0;
361 strafehud_accelzone_offset += strafehud_overturn_size.x;
362 strafehud_accelzone_size.x -= strafehud_overturn_size.x;
363 strafehud_mirror_overturn_offset = panel_size.x - strafehud_mirror_overturn_size.x;
367 strafehud_overturn_offset = panel_size.x - strafehud_overturn_size.x;
368 strafehud_accelzone_size.x -= strafehud_overturn_size.x;
369 strafehud_mirror_overturn_offset = 0;
371 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);
372 if(strafehud_mirrorangle > 0)
374 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);
377 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);
378 strafehud_bestangle_offset = floor(-strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
379 drawfill(panel_pos + eX * (strafehud_bestangle_offset - (-strafehud_direction < 0 ? strafehud_bestangle_size.x : 0)), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
380 strafehud_bestangle_offset = floor(strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
381 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);
383 strafehud_direction_size_1.x = floor(panel_size.x * .0075 + .5);
384 strafehud_direction_size_1.y = panel_size.y;
385 strafehud_direction_size_2.x = floor(strafehud_direction_size_1.x * 3 + .5);
386 strafehud_direction_size_2.y = strafehud_direction_size_1.x;
387 drawfill(panel_pos + eX * (strafehud_direction < 0 ? -strafehud_direction_size_1.x : panel_size.x), strafehud_direction_size_1, strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
388 drawfill(panel_pos + eX * (strafehud_direction < 0 ? -strafehud_direction_size_1.x : panel_size.x - strafehud_direction_size_2.x + strafehud_direction_size_1.x) - eY * strafehud_direction_size_2.y, strafehud_direction_size_2, strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
389 drawfill(panel_pos + eX * (strafehud_direction < 0 ? -strafehud_direction_size_1.x : panel_size.x - strafehud_direction_size_2.x + strafehud_direction_size_1.x) + eY * panel_size.y, strafehud_direction_size_2, strafehud_direction_color, panel_fg_alpha, DRAWFLAG_NORMAL);
393 strafehud_bestangle = (strafehud_speed > strafehud_maxspeed ? acos(strafehud_maxspeed / strafehud_speed) : 0) * RAD2DEG;
394 if (fabs(strafehud_bestangle) <= strafehud_hudangle)
396 strafehud_bestangle_offset = floor(-strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
397 drawfill(panel_pos + eX * (strafehud_bestangle_offset - strafehud_bestangle_size.x), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
398 strafehud_bestangle_offset = floor(strafehud_bestangle/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
399 drawfill(panel_pos + eX * (strafehud_bestangle_offset), strafehud_bestangle_size, strafehud_bestangle_opposite_color, panel_fg_alpha, DRAWFLAG_NORMAL);
405 strafehud_bestangle_visible = false;
408 // draw the actual strafe angle
409 if (strafehud_bestangle_visible)
411 if ((strafehud_direction > 0 && strafehud_angle >= strafehud_bestangle) ||
412 (strafehud_direction < 0 && strafehud_angle <= strafehud_bestangle))
413 strafehud_currentangle_color = strafehud_good_color;
416 if (fabs(strafehud_moveangle) > 89.9)
418 strafehud_currentangle_color = strafehud_alert_color;
421 if (strafehud_speed <= (strafehud_maxspeed + .1) && strafehud_currentangle_color != strafehud_alert_color)
423 strafehud_currentangle_color = strafehud_good_color;
426 strafehud_currentangle_offset = floor(bound(-strafehud_hudangle, strafehud_angle, strafehud_hudangle)/strafehud_hudangle * panel_size.x/2 + panel_size.x/2 + .5);
428 strafehud_currentangle_size.x = floor(panel_size.x * .005 + .5);
429 strafehud_currentangle_size.y = floor(panel_size.y * 1.5 + .5);
430 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);
432 case 1: // angle centered
433 // TODO: implement angle centered strafehud