Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
util.qc
Go to the documentation of this file.
1#include "util.qh"
2
6
7// non-local players
8#include <common/ent_cs.qh> // CSQCModel_server2csqc()
9#include <common/animdecide.qh> // anim_implicit_state
10
11// convert a strafe angle into a HUD width value
12float StrafeHUD_AngleToWidth(float angle, float range)
13{
14 return angle / range * panel_size.x;
15}
16
17// convert a strafe angle into a centered HUD offset value
18float StrafeHUD_AngleToOffset(float angle, float range)
19{
20 return StrafeHUD_AngleToWidth(angle, range) + panel_size.x * 0.5;
21}
22
23// turn a ratio into a projected ratio based on the total angular distance
24float StrafeHUD_Project(float ratio, float range, bool reverse)
25{
26 range *= DEG2RAD * 0.5;
28 {
29 default:
31 return ratio;
33 if (!reverse)
34 {
35 ratio *= range;
36 ratio = tan(ratio) / tan(range);
37 }
38 else
39 {
40 ratio = atan(ratio * tan(range));
41 ratio /= range;
42 }
43 break;
45 if (!reverse)
46 {
47 ratio *= range;
48 ratio = tan(ratio * 0.5) / tan(range * 0.5);
49 }
50 else
51 {
52 ratio = atan(ratio * tan(range * 0.5)) * 2;
53 ratio /= range;
54 }
55 break;
56 }
57 return ratio;
58}
59
60// project a centered HUD offset value
61float StrafeHUD_ProjectOffset(float offset, float range, bool reverse)
62{
64 return offset;
65
66 float ratio = (offset - (panel_size.x * 0.5)) / (panel_size.x * 0.5);
67 ratio = StrafeHUD_Project(ratio, range, reverse);
68 offset = ratio * (panel_size.x * 0.5) + (panel_size.x * 0.5);
69 return offset;
70}
71
72// project a HUD width value
73float StrafeHUD_ProjectWidth(float offset, float width, float range)
74{
76 return width;
77
78 return StrafeHUD_ProjectOffset(offset + width, range, false) - StrafeHUD_ProjectOffset(offset, range, false);
79}
80
81// length unit conversion (km and miles are only included to match the GetSpeedUnit* functions)
82float StrafeHUD_GetLengthUnitFactor(int length_unit)
83{
84 switch (length_unit)
85 {
86 default:
87 case 1: return 1.0;
88 case 2: return 0.0254;
89 case 3: return 0.0254 * 0.001;
90 case 4: return 0.0254 * 0.001 * 0.6213711922;
91 case 5: return 0.0254 * 0.001 * 0.5399568035;
92 }
93}
94
95string StrafeHUD_GetLengthUnit(int length_unit)
96{
97 switch (length_unit)
98 {
99 // translator-friendly strings without the initial space
100 default:
101 case 1: return strcat(" ", _("qu"));
102 case 2: return strcat(" ", _("m"));
103 case 3: return strcat(" ", _("km"));
104 case 4: return strcat(" ", _("mi"));
105 case 5: return strcat(" ", _("nmi"));
106 }
107}
108
110{
111 // use the local csqcmodel entity if it's valid (i.e. is_local == true)
112 return is_local
113 ? csqcplayer
115}
116
117// check the player waterlevel without affecting the player entity, this way we can fetch waterlevel even if client prediction is disabled
119{
120 // store old values
121 void old_contentstransition(int, int) = e.contentstransition;
122 float old_watertype = e.watertype;
123 float old_waterlevel = e.waterlevel;
124
125 e.contentstransition = func_null; // unset the contentstransition function if present
127 float new_waterlevel = e.waterlevel; // store the player waterlevel
128
129 // restore old values
130 e.contentstransition = old_contentstransition;
131 e.watertype = old_watertype;
132 e.waterlevel = old_waterlevel;
133
134 return new_waterlevel;
135}
136
137// determine frametime, to avoid jitter, average the frametime in case client prediction is used
139{
140 static float dt_update = 0;
141 static int dt_time = 0;
142 static float dt_sum = 0;
143 static float dt = 0;
145 {
146 float dt_client = input_timelength;
147
148 if (dt_client > 0.05) // server splits frames longer than 50 ms into two moves (DarkPlaces behaviour)
149 dt_client *= 0.5; // does not ensure frames are smaller than 50 ms, just splits large frames in half, matches server behaviour
150
151 /* calculate average frametime
152 * calculated using a weighted arithmetic mean, where the weighting is equal to the frametime itself
153 * for example, given a 1 ms frame and a 9 ms frame we have:
154 * a total time of 10 ms
155 * a weighted sum of 1 ms * 1 ms + 9 ms * 9 ms = 82 ms^2
156 * the final result is 82 ms^2 / 10 ms = 8.2 ms
157 */
158 dt_sum += dt_client * dt_client; // weighted sum of all frametimes (mean numerator)
159 dt_time += dt_client; // time spent averaging (mean denominator)
160
161 if ((time - dt_update) > autocvar_hud_panel_strafehud_fps_update || dt_update == 0)
162 {
163 dt = dt_sum / dt_time;
164 dt_update = time;
165 dt_time = dt_sum = 0;
166 }
167 }
168 else // when spectating other players server ticrate will be used, this may not be accurate but there is no way to find other player's frametime
169 {
170 dt = ticrate;
171 dt_update = dt_time = dt_sum = 0;
172 }
173
174 return dt;
175}
176
177// determine player wishdir, non-local player movement is limited to 45 degree steps
178float StrafeHUD_DetermineWishAngle(vector movement, int keys, bool is_local)
179{
180 float wishangle;
181 if (is_local) // if entity is local player
182 {
183 if (movement.y == 0)
184 wishangle = 0;
185 else
186 {
187 wishangle = RAD2DEG * atan2(movement.y, movement.x);
188 // wrap the wish angle if it exceeds ±90°
189 if (fabs(wishangle) > 90)
190 {
191 if (wishangle < 0)
192 wishangle += 180;
193 else
194 wishangle -= 180;
195
196 wishangle *= -1;
197 }
198 }
199 }
200 else // alternatively calculate wishdir by querying pressed keys
201 {
202 if (keys & (KEY_FORWARD | KEY_BACKWARD))
203 wishangle = 45;
204 else
205 wishangle = 90;
206 if (keys & KEY_LEFT)
207 wishangle *= -1;
208 else if (!(keys & KEY_RIGHT))
209 wishangle = 0; // wraps at 180°
210 }
211
212 return wishangle;
213}
214
215// determine whether the player is pressing forwards or backwards keys
216int StrafeHUD_DetermineForwardKeys(vector movement, int keys, bool is_local)
217{
218 if (is_local) // if entity is local player
219 {
220 if (movement.x > 0)
222 else if (movement.x < 0)
224 else
225 return STRAFEHUD_KEYS_NONE;
226 }
227 else // alternatively determine direction by querying pressed keys
228 {
229 if ((keys & KEY_FORWARD) && !(keys & KEY_BACKWARD))
231 else if (!(keys & KEY_FORWARD) && (keys & KEY_BACKWARD))
233 else
234 return STRAFEHUD_KEYS_NONE;
235 }
236}
237
238float StrafeHUD_DetermineHudAngle(float absolute_wishangle, float absolute_overturnangle, float strafity)
239{
240 // determine the minimal required HUD angle to contain the full strafing angle range
241 // this is useful for the velocity centered mode where the zones do not follow the strafing angle
242 // how it works:
243 // the angle where the most acceleration occurs moves relative to the player velocity
244 // from 0 - wishangle to absolute_overturnangle - wishangle
245 // the angle farther away from the center is the maximum the optimal strafing angle can
246 // diverge from the direction of velocity
247 // this angle has to be multiplied by two since the HUD extends in both directions which
248 // halves the amount it extends in a single direction
249 float range_minangle = max(absolute_wishangle, absolute_overturnangle - absolute_wishangle) * 2;
250
251 float range_normal = autocvar_hud_panel_strafehud_range;
253 float range_used;
254
255 float hfov = getproperty(VF_FOVX);
256 if (isnan(range_normal) || isnan(range_side) || isnan(hfov))
257 return 360;
258
259 // negative values enable different behaviour
260 // no exact matching so that all negative values are caught
261 if (range_normal == 0) // range = 0, use minimum angle required if dynamically setting hud angle
262 range_normal = autocvar__hud_configure ? 90 : range_minangle;
263 else if (range_normal < 0) // range = -1, use the current field of view
264 range_normal = hfov;
265
266 if (range_side < -1) // range = -2, use the normal range
267 range_used = range_normal;
268 else
269 {
270 if (range_side == 0) // range = 0, use minimum angle required if dynamically setting hud angle
271 range_side = autocvar__hud_configure ? 90 : range_minangle;
272 else if (range_side < 0) // range = -1, use the current field of view
273 range_side = hfov;
274
275 range_used = GeomLerp(range_normal, strafity, range_side);
276 }
277 float hudangle = bound(0, fabs(range_used), 360); // limit HUD range to 360 degrees, higher values don't make sense
278
279 // limit strafe-meter angle to values suitable for the current projection mode
281 {
282 // those limits are a little less than the maximal FOV the game allows
283 // however, they suffice for all realistic use cases
285 hudangle = min(hudangle, 170);
286 break;
288 hudangle = min(hudangle, 350);
289 break;
290 }
291
292 return hudangle;
293}
294
295// determine whether the player is strafing left or right
296float StrafeHUD_DetermineDirection(float angle, float wishangle, float antiflicker_angle)
297{
298 if (wishangle > 0)
300 else if (wishangle < 0)
302 else
303 {
304 if (angle > antiflicker_angle && angle < (180 - antiflicker_angle))
306 else if (angle < -antiflicker_angle && angle > (-180 + antiflicker_angle))
308 else
310 }
311}
312
313// determine whether the player holds the jump key
314// try to ignore if track_canjump is enabled
315// does not work in spectator mode if the spectated player uses +jetpack or cl_movement_track_canjump
316bool StrafeHUD_DetermineJumpHeld(entity e, int keys, bool is_local)
317{
318 if (is_local)
319 {
321 return true;
322 }
323 else
324 {
325 if ((keys & KEY_JUMP) && !PHYS_TRACK_CANJUMP(e))
326 return true;
327 }
328
329 return false;
330}
331
332// determine whether the player is on ground
333// this return value is used in real_onground, not onground
335{
336 return is_local ? IS_ONGROUND(e) : !(e.anim_implicit_state & ANIMIMPLICITSTATE_INAIR);
337}
338
339// determine whether the player is standing on a slick surface
340// NOTE: this isn't the only condition for the slick detector being drawn
342{
343 // do not use IS_ONSLICK(), it only works for the local player and only if client prediction is enabled
345 tracebox(e.origin, e.mins, e.maxs, e.origin - '0 0 1', MOVE_NOMONSTERS, e);
347}
348
349// mix two colors based on a ratio
350// TODO: move mixing colors out of the HUD, this could be useful for other code
351vector StrafeHUD_MixColors(vector color1, vector color2, float ratio)
352{
353 if (ratio <= 0)
354 return color1;
355 if (ratio >= 1)
356 return color2;
357 return color1 + (color2 - color1) * ratio;
358}
359
361{
362 pos.x *= panel_size.x * 0.5; // more intuitive since we start in the middle, this turns the range from -0.5 to +0.5 into -1 to +1
363 pos.y *= panel_size.y;
364 pos.z = 0;
365
366 return pos;
367}
const int ANIMIMPLICITSTATE_INAIR
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
entity csqcplayer
Definition cl_player.qh:26
const int CSQCPLAYERSTATUS_PREDICTED
Definition cl_player.qh:30
float csqcplayer_status
Definition cl_player.qh:27
entity StrafeHUD_GetStrafeplayer(bool is_local)
Definition util.qc:109
float StrafeHUD_DetermineDirection(float angle, float wishangle, float antiflicker_angle)
Definition util.qc:296
vector StrafeHUD_CalculateTextIndicatorPosition(vector pos)
Definition util.qc:360
float StrafeHUD_GetLengthUnitFactor(int length_unit)
Definition util.qc:82
bool StrafeHUD_DetermineOnSlick(entity e)
Definition util.qc:341
float StrafeHUD_ProjectWidth(float offset, float width, float range)
Definition util.qc:73
float StrafeHUD_Project(float ratio, float range, bool reverse)
Definition util.qc:24
float StrafeHUD_AngleToWidth(float angle, float range)
Definition util.qc:12
float StrafeHUD_DetermineWishAngle(vector movement, int keys, bool is_local)
Definition util.qc:178
int StrafeHUD_DetermineForwardKeys(vector movement, int keys, bool is_local)
Definition util.qc:216
string StrafeHUD_GetLengthUnit(int length_unit)
Definition util.qc:95
float StrafeHUD_DetermineFrameTime()
Definition util.qc:138
bool StrafeHUD_DetermineJumpHeld(entity e, int keys, bool is_local)
Definition util.qc:316
float StrafeHUD_DetermineWaterLevel(entity e)
Definition util.qc:118
bool StrafeHUD_DetermineOnGround(entity e, bool is_local)
Definition util.qc:334
float StrafeHUD_AngleToOffset(float angle, float range)
Definition util.qc:18
float StrafeHUD_DetermineHudAngle(float absolute_wishangle, float absolute_overturnangle, float strafity)
Definition util.qc:238
vector StrafeHUD_MixColors(vector color1, vector color2, float ratio)
Definition util.qc:351
float StrafeHUD_ProjectOffset(float offset, float range, bool reverse)
Definition util.qc:61
float ticrate
Definition main.qh:209
float GeomLerp(float a, float _lerp, float b)
Definition player.qc:154
#define PHYS_CL_TRACK_CANJUMP(s)
Definition player.qh:285
vector movement
Definition player.qh:229
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition player.qh:151
#define PHYS_INPUT_BUTTON_JETPACK(s)
Definition player.qh:162
#define PHYS_TRACK_CANJUMP(s)
Definition player.qh:140
const int KEY_JUMP
Definition constants.qh:39
const int KEY_RIGHT
Definition constants.qh:38
const int KEY_BACKWARD
Definition constants.qh:36
const int KEY_FORWARD
Definition constants.qh:35
const int KEY_LEFT
Definition constants.qh:37
const float MOVE_NOMONSTERS
float DEG2RAD
float RAD2DEG
float Q3SURFACEFLAG_SLICK
float time
float trace_dphitq3surfaceflags
float player_localentnum
float input_timelength
const float VF_FOVX
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
vector panel_size
Definition hud.qh:163
bool autocvar__hud_configure
Definition hud_config.qh:3
float angle
Definition viewloc.qc:114
bool isnan(float e)
Definition mathlib.qc:25
float bound(float min, float value, float max)
float min(float f,...)
float fabs(float f)
float max(float f,...)
bool _Movetype_CheckWater(entity this)
Definition movetypes.qc:334
#define IS_ONGROUND(s)
Definition movetypes.qh:16
var void func_null()
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
vector
Definition self.qh:92
const int STRAFEHUD_PROJECTION_PANORAMIC
Definition strafehud.qh:152
const int STRAFEHUD_KEYS_NONE
Definition strafehud.qh:136
const int STRAFEHUD_DIRECTION_RIGHT
Definition strafehud.qh:124
float autocvar_hud_panel_strafehud_range
Definition strafehud.qh:9
const int STRAFEHUD_KEYS_FORWARD
Definition strafehud.qh:137
float autocvar_hud_panel_strafehud_fps_update
Definition strafehud.qh:19
const int STRAFEHUD_PROJECTION_PERSPECTIVE
Definition strafehud.qh:151
float autocvar_hud_panel_strafehud_range_sidestrafe
Definition strafehud.qh:10
const int STRAFEHUD_KEYS_BACKWARD
Definition strafehud.qh:138
int autocvar_hud_panel_strafehud_projection
Definition strafehud.qh:100
const int STRAFEHUD_DIRECTION_LEFT
Definition strafehud.qh:123
const int STRAFEHUD_DIRECTION_NONE
Definition strafehud.qh:122
const int STRAFEHUD_PROJECTION_LINEAR
Definition strafehud.qh:150