Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
racetimer.qc
Go to the documentation of this file.
1#include "racetimer.qh"
2#include "physics.qh"
3
4#include <client/draw.qh>
5#include <common/ent_cs.qh>
6
7// used for caching the string after passing through a checkpoint
13
14// Race timer (#8)
15
17{
18 // allow saving cvars that aesthetically change the panel into hud skin files
19}
20
21// return the string of the onscreen race timer
22string MakeRaceString(int cp, float mytime, float theirtime, float othertime, float lapdelta, string theirname)
23{
24 TC(int, cp);
25 string lapstr = "", timestr = "", col = "^7", othercol = "^7", othertimestr = "";
26 if (theirname == "" || !autocvar_cl_race_cptimes_showself)
27 othertime = 0; // don't count personal time
28
29 if (theirtime == 0) // goal hit
30 {
31 if (mytime > 0)
32 {
33 timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
34 col = "^1";
35 }
36 else if (mytime == 0)
37 {
38 timestr = "+0.0";
39 col = "^3";
40 }
41 else
42 {
43 timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
44 col = "^2";
45 }
46
47 if (othertime > 0)
48 {
49 othertimestr = strcat("+", ftos_decimals(+othertime, TIME_DECIMALS));
50 othercol = "^1";
51 }
52 else if (othertime == 0)
53 {
54 othertimestr = "+0.0";
55 othercol = "^3";
56 }
57 else
58 {
59 othertimestr = strcat("-", ftos_decimals(-othertime, TIME_DECIMALS));
60 othercol = "^2";
61 }
62
63 if (lapdelta)
64 {
65 // ^%+d forces a plus sign to show on positive numbers
66 // TRANSLATORS: lap difference, e.g. "+2L" where L stands for lap(s).
67 string lapdiff = sprintf(CTX(_("LAP_DIFF^%+dL")), -lapdelta);
68 lapstr = sprintf(_(" (%s)"), lapdiff);
69 col = lapdelta > 0 ? "^2" : "^1";
70 }
71 }
72 else if (theirtime > 0) // anticipation
73 {
74 if (mytime >= theirtime)
75 timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS));
76 else
77 timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime), false);
78 col = "^3";
79 if (mytime >= othertime)
80 othertimestr = strcat("+", ftos_decimals(mytime - othertime, TIME_DECIMALS));
81 else
82 othertimestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(othertime), false);
83 othercol = "^7";
84 }
85
86 string cpname;
87 if (cp == 254)
88 cpname = _("Start line");
89 else if (cp == 255)
90 cpname = _("Finish line");
91 else if (cp)
92 cpname = sprintf(_("Checkpoint %d"), cp);
93 else
94 cpname = _("Finish line");
95
96 if (theirname != "" && theirtime >= 0)
97 {
98 float namesize = autocvar_cl_race_cptimes_namesize * hud_fontsize.x;
99 theirname = textShortenToWidth(ColorTranslateRGB(theirname), namesize, hud_fontsize, stringwidth_colors);
100 }
101
102 if (theirtime < 0)
103 return strcat(col, cpname);
104 else if (theirname == "")
105 return strcat(col, cpname, sprintf(_(" (%s)"), timestr));
106 else
107 {
108 // TRANSLATORS: time difference and player, e.g. "+0.54 Bob".
109 string time_and_their_name = sprintf(CTX(_("TIMEDIFF_PLAYER^%s %s")), timestr, strcat(theirname, col, lapstr));
110 time_and_their_name = sprintf(_(" (%s)"), time_and_their_name);
111 if (othertime)
112 return strcat(col, cpname, othercol, sprintf(_(" (%s)"), othertimestr), col, time_and_their_name);
113 else
114 return strcat(col, cpname, time_and_their_name);
115 }
116}
117
118void ClearCheckpointSplits(bool quiet)
119{
120 bool once = true;
121 bool changed_player = racetimer_have_stored_splits_player != current_player + 1;
122 for (int i = 0; i < 256; ++i)
123 {
125 && autocvar_cl_race_checkpoint_splits_console
126 && !quiet)
127 {
128 if (once)
129 {
130 LOG_HELP(_("Checkpoint times:"));
131 once = false;
132 }
134 }
136 if (changed_player)
138 }
141 if (changed_player)
143}
144
145void StoreCheckpointSplits(float race_checkpoint, string forcetime, string s)
146{
147 // store checkpoint splits string for later printing
150
151 // 0 or 255 go to 255 as finish, strcpy does the free if needed
152 strcpy(race_checkpoint_splits[race_checkpoint ? race_checkpoint : 255], (forcetime != "") ? sprintf("%s %s", forcetime, s) : s);
153 // cache
159}
160
161void HUD_RaceTimer(bool should_draw)
162{
163 if (!should_draw)
164 return;
166 {
168 return;
169 if (!MUTATOR_CALLHOOK(ShowRaceTimer))
170 return;
171 }
172
174
175 vector pos = panel_pos;
176 vector mySize = panel_size;
177
180 else
184 {
185 pos += '1 1 0' * panel_bg_padding;
186 mySize -= '2 2 0' * panel_bg_padding;
187 }
188
189 // always force 4:1 aspect
190 vector newSize = '0 0 0';
191 if (mySize.x / mySize.y > 4)
192 {
193 newSize.x = 4 * mySize.y;
194 newSize.y = mySize.y;
195
196 pos.x += (mySize.x - newSize.x) * 0.5;
197 }
198 else
199 {
200 newSize.y = 1/4 * mySize.x;
201 newSize.x = mySize.x;
202
203 pos.y += (mySize.y - newSize.y) * 0.5;
204 }
205 mySize = newSize;
206
207 float a;
208 string s;
209 vector str_pos;
210
212 {
213 s = "0:13:37";
215 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(s, false, '1 1 0' * 0.6 * mySize.y));
216 drawstring(str_pos, s, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
218
219 float speed_conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
220 string units_text = autocvar_cl_race_cptimes_showspeed_unit ? GetSpeedUnit(autocvar_hud_speed_unit) : "";
221 string col = "^1";
222 string cpname = sprintf(_("Checkpoint %d"), 1);
223 string timestr = "+15.42";
224 // strcat ported from MakeRaceString, please keep in sync
225 s = strcat(col, cpname, sprintf(_(" (%s)"), timestr));
226 if (autocvar_cl_race_cptimes_showspeed)
227 {
228 string speed_str = sprintf(CTX(_("SPEED^ %d%s")), 345 * speed_conversion_factor, units_text);
230 float speed_diff = 123;
231 // strcat ported from another place of this function, please keep in sync
232 s = strcat(s, "^7", speed_str, speed_color, sprintf(_(" (%s)"), sprintf(CTX(_("SPEED_DIFF^%+d%s")), speed_diff * speed_conversion_factor, units_text)));
233 }
234
235 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
236 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
237 float race_penaltytime = 20;
238 string race_penaltyreason = _("missing a checkpoint");
239 // strcat ported from another place of this function, please keep in sync
240 s = strcat("^1", sprintf(_("PENALTY: %.1f"), race_penaltytime * 0.1), sprintf(_(" (%s)"), race_penaltyreason));
241 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.8 * mySize.y);
242 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
243 }
244 else if (race_checkpointtime)
245 {
246 a = bound(0, 2 - (time - race_checkpointtime), 1);
247 s = "";
248 string forcetime = "";
249 if (a > 0) // display a frozen split for the just reached checkpoint
250 {
251 if (race_checkpoint != 254 && race_time != 0)
252 {
253 if (race_checkpoint == racetimer_lastcheckpoint // same cp *and* player
255 {
256 // use cached strings
258 forcetime = racetimer_checkpoint_time;
259 }
260 else
261 {
264
265 // build checkpoint split strings
269
270 if (autocvar_cl_race_cptimes_showspeed)
271 {
272 float speed_conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
273 float speed = race_timespeed;
275 string units_text = autocvar_cl_race_cptimes_showspeed_unit ? GetSpeedUnit(autocvar_hud_speed_unit) : "";
276
277 // figure out color
279 if (speed_diff > 0)
281 else if (speed_diff == 0)
282 speed_color = "^3";
283
284 string speed_str = sprintf(CTX(_("SPEED^ %d%s")), speed * speed_conversion_factor, units_text);
286 s = strcat(s, "^7", speed_str, speed_color, sprintf(_(" (%s)"), sprintf(CTX(_("SPEED_DIFF^%+d%s")), speed_diff * speed_conversion_factor, units_text)));
287 else
288 s = strcat(s, "^7", speed_str);
289
290 // if fastest cp time, store speed for later comparison
293 }
294 if (race_time)
295 forcetime = TIME_ENCODED_TOSTRING(race_time, false);
296
298 }
299 }
300 else
301 {
302 // clean cp splits on start
305 }
306 }
307 else
308 {
309 if (race_laptime && race_nextcheckpoint != 254)
310 {
312 {
315 if (a > 0) // next one?
317 }
318 }
319 }
320
321 if (s != "" && a > 0)
322 {
323 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
324 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
325 }
326
328 {
329 a = bound(0, 2 - (time - race_penaltyeventtime), 1);
330 if (a > 0)
331 {
332 s = strcat("^1", sprintf(_("PENALTY: %.1f"), race_penaltytime * 0.1), sprintf(_(" (%s)"), race_penaltyreason));
333 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.8 * mySize.y);
334 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
335 }
336 }
337
339
340 if (forcetime != "")
341 {
342 a = bound(0, (time - race_checkpointtime) / 0.5, 1);
343 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y));
344 drawstring_expanding(str_pos, forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a);
345 }
346 else
347 a = 1;
348
349 if (race_laptime && race_checkpoint != 255)
350 {
352 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(s, false, '0.6 0.6 0' * mySize.y));
353 drawstring(str_pos, s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
354 }
355
357 }
358 else
359 {
360 if (racetimer_have_stored_splits && (race_time == 0 || time < STAT(GAMESTARTTIME)))
362
364 {
365 a = bound(0, 2 - (time - race_mycheckpointtime), 1);
367 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
368 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
369 }
371 {
372 a = bound(0, 2 - (time - race_othercheckpointtime), 1);
374 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
375 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
376 }
377
379 {
380 float t = race_penaltytime * 0.1 + race_penaltyeventtime;
381 a = bound(0, (1 + t - time), 1);
382 if (a > 0)
383 {
384 string col;
385 if (time < t)
386 {
387 t = (t - time) * 0.1;
388 col = "^1";
389 }
390 else
391 {
392 t = 0;
393 col = "^2";
394 }
395 s = strcat(col, sprintf(_("PENALTY: %.1f"), t), sprintf(_(" (%s)"), race_penaltyreason));
396 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
397 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
398 }
399 }
400 }
401}
#define MUTATOR_CALLHOOK(name,...)
Definition base.qh:186
void drawstring_expanding(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
Definition draw.qc:120
#define drawcolorcodedstring(position, text, scale, alpha, flag)
Definition draw.qh:30
#define drawstring(position, text, scale, rgb, alpha, flag)
Definition draw.qh:27
#define draw_beginBoldFont()
Definition draw.qh:4
#define draw_endBoldFont()
Definition draw.qh:5
vector autocvar_hud_progressbar_acceleration_neg_color
Definition physics.qh:30
vector autocvar_hud_progressbar_acceleration_color
Definition physics.qh:29
string GetSpeedUnit(int speed_unit)
Definition main.qc:1123
float GetSpeedUnitFactor(int speed_unit)
Definition main.qc:1110
vector hud_fontsize
Definition main.qh:77
int spectatee_status
the -1 disables HUD panels before CSQC receives necessary data
Definition main.qh:197
bool autocvar_cl_race_cptimes_showself
Definition main.qh:30
ERASEABLE string rgb_to_hexcolor(vector rgb)
Definition color.qh:177
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1173
#define TIME_ENCODE(t)
Definition util.qh:107
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:103
const int TIME_DECIMALS
Definition util.qh:101
#define TIME_DECODE(n)
Definition util.qh:108
const float DRAWFLAG_NORMAL
float time
#define stringwidth
float speed
Definition dynlight.qc:9
void HUD_Panel_LoadCvars()
Definition hud.qc:215
void HUD_Scale_Enable()
Definition hud.qc:91
void HUD_Scale_Disable()
Definition hud.qc:84
vector panel_size
Definition hud.qh:163
float panel_fg_alpha
Definition hud.qh:169
float panel_bg_padding
Definition hud.qh:174
int autocvar_hud_speed_unit
Definition hud.qh:207
#define HUD_Panel_DrawBg()
Definition hud.qh:55
float current_player
Definition hud.qh:185
vector panel_pos
Definition hud.qh:162
bool autocvar__hud_configure
Definition hud_config.qh:3
ERASEABLE string CTX(string s)
Definition i18n.qh:46
#define TC(T, sym)
Definition _all.inc:82
#define STAT(...)
Definition stats.qh:94
#define LOG_HELP(...)
Definition log.qh:83
float bound(float min, float value, float max)
float rint(float f)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void HUD_RaceTimer_Export(int fh)
Definition racetimer.qc:16
void ClearCheckpointSplits(bool quiet)
Definition racetimer.qc:118
string MakeRaceString(int cp, float mytime, float theirtime, float othertime, float lapdelta, string theirname)
Definition racetimer.qc:22
string racetimer_checkpoint_time
Definition racetimer.qc:10
float racetimer_have_stored_splits_player
Definition racetimer.qc:12
string racetimer_checkpoint_comparison
Definition racetimer.qc:9
float racetimer_lastcheckpoint
Definition racetimer.qc:8
bool racetimer_have_stored_splits
Definition racetimer.qc:11
void StoreCheckpointSplits(float race_checkpoint, string forcetime, string s)
Definition racetimer.qc:145
void HUD_RaceTimer(bool should_draw)
Definition racetimer.qc:161
float race_othercheckpointtime
Definition racetimer.qh:42
string race_othercheckpointenemy
Definition racetimer.qh:45
float race_nextcheckpoint
Definition racetimer.qh:19
float race_checkpoint_splits_speed[256]
Definition racetimer.qh:15
float race_othercheckpoint
Definition racetimer.qh:41
float race_penaltyaccumulator
Definition racetimer.qh:25
float race_mycheckpointlapsdelta
Definition racetimer.qh:39
string race_penaltyreason
Definition racetimer.qh:28
string race_nextbestname
Definition racetimer.qh:24
float race_checkpoint
Definition racetimer.qh:8
string race_mycheckpointenemy
Definition racetimer.qh:40
float race_othercheckpointdelta
Definition racetimer.qh:43
float race_previousbesttime
Definition racetimer.qh:16
float race_nextbesttime
Definition racetimer.qh:20
float race_mypreviousbesttime
Definition racetimer.qh:17
float race_time
Definition racetimer.qh:9
float race_penaltytime
Definition racetimer.qh:27
float race_mybesttime
Definition racetimer.qh:22
bool autocvar_hud_panel_racetimer_dynamichud
Definition racetimer.qh:5
float race_timespeed
Definition racetimer.qh:10
bool autocvar_hud_panel_racetimer
Definition racetimer.qh:4
float race_mycheckpoint
Definition racetimer.qh:36
float race_checkpointtime
Definition racetimer.qh:13
float race_mycheckpointtime
Definition racetimer.qh:37
float race_othercheckpointlapsdelta
Definition racetimer.qh:44
float race_penaltyeventtime
Definition racetimer.qh:26
float race_mycheckpointdelta
Definition racetimer.qh:38
string race_previousbestname
Definition racetimer.qh:18
string race_checkpoint_splits[256]
Definition racetimer.qh:14
float race_laptime
Definition racetimer.qh:12
vector
Definition self.qh:96
ERASEABLE string ColorTranslateRGB(string s)
Definition string.qh:194
ERASEABLE string ftos_decimals(float number, int decimals)
Converts a number to a string with the indicated number of decimals.
Definition string.qh:476
#define strfree(this)
Definition string.qh:57
#define strcpy(this, s)
Definition string.qh:51
float stringwidth_colors(string s, vector theSize)
Definition string.qh:30
const vector eX
Definition vector.qh:43
#define vec2(...)
Definition vector.qh:95