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 > 0)
64 {
65 lapstr = sprintf(_(" (-%dL)"), lapdelta);
66 col = "^2";
67 }
68 else if (lapdelta < 0)
69 {
70 lapstr = sprintf(_(" (+%dL)"), -lapdelta);
71 col = "^1";
72 }
73 }
74 else if (theirtime > 0) // anticipation
75 {
76 if (mytime >= theirtime)
77 timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS));
78 else
79 timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime), false);
80 col = "^3";
81 if (mytime >= othertime)
82 othertimestr = strcat("+", ftos_decimals(mytime - othertime, TIME_DECIMALS));
83 else
84 othertimestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(othertime), false);
85 othercol = "^7";
86 }
87
88 string cpname;
89 if (cp == 254)
90 cpname = _("Start line");
91 else if (cp == 255)
92 cpname = _("Finish line");
93 else if (cp)
94 cpname = sprintf(_("Checkpoint %d"), cp);
95 else
96 cpname = _("Finish line");
97
98 if (theirname != "" && theirtime >= 0)
99 {
100 float namesize = autocvar_cl_race_cptimes_namesize * hud_fontsize.x;
101 theirname = textShortenToWidth(ColorTranslateRGB(theirname), namesize, hud_fontsize, stringwidth_colors);
102 }
103
104 if (theirtime < 0)
105 return strcat(col, cpname);
106 else if (theirname == "")
107 return strcat(col, sprintf("%s (%s)", cpname, timestr));
108 else if (othertime)
109 return strcat(col, sprintf("%s %s(%s)%s (%s %s)", cpname, othercol, othertimestr, col, timestr, strcat(theirname, col, lapstr)));
110 else
111 return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr)));
112}
113
114void ClearCheckpointSplits(bool quiet)
115{
116 bool once = true;
117 bool changed_player = racetimer_have_stored_splits_player != current_player + 1;
118 for (int i = 0; i < 256; ++i)
119 {
121 && autocvar_cl_race_checkpoint_splits_console
122 && !quiet)
123 {
124 if (once)
125 {
126 LOG_HELP(_("Checkpoint times:"));
127 once = false;
128 }
130 }
132 if (changed_player)
134 }
137 if (changed_player)
139}
140
141void StoreCheckpointSplits(float race_checkpoint, string forcetime, string s)
142{
143 // store checkpoint splits string for later printing
146
147 // 0 or 255 go to 255 as finish, strcpy does the free if needed
148 strcpy(race_checkpoint_splits[race_checkpoint ? race_checkpoint : 255], (forcetime != "") ? sprintf("%s %s", forcetime, s) : s);
149 // cache
155}
156
158{
160 {
162 return;
163 if (!MUTATOR_CALLHOOK(ShowRaceTimer))
164 return;
165 }
166
168
169 vector pos = panel_pos;
170 vector mySize = panel_size;
171
174 else
178 {
179 pos += '1 1 0' * panel_bg_padding;
180 mySize -= '2 2 0' * panel_bg_padding;
181 }
182
183 // always force 4:1 aspect
184 vector newSize = '0 0 0';
185 if (mySize.x / mySize.y > 4)
186 {
187 newSize.x = 4 * mySize.y;
188 newSize.y = mySize.y;
189
190 pos.x += (mySize.x - newSize.x) * 0.5;
191 }
192 else
193 {
194 newSize.y = 1/4 * mySize.x;
195 newSize.x = mySize.x;
196
197 pos.y += (mySize.y - newSize.y) * 0.5;
198 }
199 mySize = newSize;
200
201 float a;
202 string s;
203 vector str_pos;
204
206 {
207 s = "0:13:37";
209 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(s, false, '1 1 0' * 0.6 * mySize.y));
210 drawstring(str_pos, s, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
212 float speed_conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
213 string units_text = autocvar_cl_race_cptimes_showspeed_unit ? GetSpeedUnit(autocvar_hud_speed_unit) : "";
214 s = strcat("^1", sprintf(_("Checkpoint %d"), 1), " (+15.42)", autocvar_cl_race_cptimes_showspeed ? sprintf(" ^7%d%s %s(%+d%s)", 345 * speed_conversion_factor, units_text, rgb_to_hexcolor(autocvar_hud_progressbar_acceleration_color), 123 * speed_conversion_factor, units_text) : "");
215 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
216 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
217 s = sprintf(_("PENALTY: %.1f (%s)"), 2, _("missing a checkpoint"));
218 s = strcat("^1", s);
219 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.8 * mySize.y);
220 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
221 }
222 else if (race_checkpointtime)
223 {
224 a = bound(0, 2 - (time - race_checkpointtime), 1);
225 s = "";
226 string forcetime = "";
227 if (a > 0) // display a frozen split for the just reached checkpoint
228 {
229 if (race_checkpoint != 254 && race_time != 0)
230 {
231 if (race_checkpoint == racetimer_lastcheckpoint // same cp *and* player
233 {
234 // use cached strings
236 forcetime = racetimer_checkpoint_time;
237 }
238 else
239 {
242
243 // build checkpoint split strings
247
248 if (autocvar_cl_race_cptimes_showspeed)
249 {
250 float speed_conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
251 float speed = race_timespeed;
253 string units_text = autocvar_cl_race_cptimes_showspeed_unit ? GetSpeedUnit(autocvar_hud_speed_unit) : "";
254
255 // figure out color
257 if (speed_diff > 0)
259 else if (speed_diff == 0)
260 speed_color = "^3";
261
263 s = sprintf("%s ^7%d%s %s(%+d%s)", s, speed * speed_conversion_factor, units_text, speed_color, speed_diff * speed_conversion_factor, units_text);
264 else
265 s = sprintf("%s ^7%d%s", s, speed * speed_conversion_factor, units_text);
266
267 // if fastest cp time, store speed for later comparison
270 }
271 if (race_time)
272 forcetime = TIME_ENCODED_TOSTRING(race_time, false);
273
275 }
276 }
277 else
278 {
279 // clean cp splits on start
282 }
283 }
284 else
285 {
286 if (race_laptime && race_nextcheckpoint != 254)
287 {
289 {
292 if (a > 0) // next one?
294 }
295 }
296 }
297
298 if (s != "" && a > 0)
299 {
300 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
301 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
302 }
303
305 {
306 a = bound(0, 2 - (time - race_penaltyeventtime), 1);
307 if (a > 0)
308 {
309 s = sprintf(_("PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason);
310 s = strcat("^1", s);
311 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.8 * mySize.y);
312 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
313 }
314 }
315
317
318 if (forcetime != "")
319 {
320 a = bound(0, (time - race_checkpointtime) / 0.5, 1);
321 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y));
322 drawstring_expanding(str_pos, forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a);
323 }
324 else
325 a = 1;
326
327 if (race_laptime && race_checkpoint != 255)
328 {
330 str_pos = pos + eX * 0.5 * (mySize.x - stringwidth(s, false, '0.6 0.6 0' * mySize.y));
331 drawstring(str_pos, s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
332 }
333
335 }
336 else
337 {
338 if (racetimer_have_stored_splits && (race_time == 0 || time < STAT(GAMESTARTTIME)))
340
342 {
343 a = bound(0, 2 - (time - race_mycheckpointtime), 1);
345 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
346 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
347 }
349 {
350 a = bound(0, 2 - (time - race_othercheckpointtime), 1);
352 str_pos = pos + vec2(0.5 * (mySize.x - stringwidth(s, true, '1 1 0' * 0.2 * mySize.y)), 0.6 * mySize.y);
353 drawcolorcodedstring(str_pos, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
354 }
355
357 {
358 float t = race_penaltytime * 0.1 + race_penaltyeventtime;
359 a = bound(0, (1 + t - time), 1);
360 if (a > 0)
361 {
362 string col;
363 if (time < t)
364 {
365 t = (t - time) * 0.1;
366 col = "^1";
367 }
368 else
369 {
370 t = 0;
371 col = "^2";
372 }
373 s = strcat(col, sprintf(_("PENALTY: %.1f (%s)"), t, race_penaltyreason));
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 }
378 }
379}
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
void drawstring_expanding(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
Definition draw.qc:121
#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:1122
float GetSpeedUnitFactor(int speed_unit)
Definition main.qc:1109
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:183
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1071
#define TIME_ENCODE(t)
Definition util.qh:100
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:96
const int TIME_DECIMALS
Definition util.qh:94
#define TIME_DECODE(n)
Definition util.qh:101
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
#define TC(T, sym)
Definition _all.inc:82
#define STAT(...)
Definition stats.qh:82
#define LOG_HELP(...)
Definition log.qh:85
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:114
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 HUD_RaceTimer()
Definition racetimer.qc:157
void StoreCheckpointSplits(float race_checkpoint, string forcetime, string s)
Definition racetimer.qc:141
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:92
ERASEABLE string ColorTranslateRGB(string s)
Definition string.qh:196
ERASEABLE string ftos_decimals(float number, int decimals)
converts a number to a string with the indicated number of decimals
Definition string.qh:469
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
float stringwidth_colors(string s, vector theSize)
Definition string.qh:30
const vector eX
Definition vector.qh:44
#define vec2(...)
Definition vector.qh:90