Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
centerprint.qc
Go to the documentation of this file.
1#include "centerprint.qh"
2
3#include <client/draw.qh>
6
7// CenterPrint (#16)
8
10{
11 // allow saving cvars that aesthetically change the panel into hud skin files
12 HUD_Write_Cvar("hud_panel_centerprint_align");
13 HUD_Write_Cvar("hud_panel_centerprint_flip");
14 HUD_Write_Cvar("hud_panel_centerprint_fontscale");
15 HUD_Write_Cvar("hud_panel_centerprint_fontscale_bold");
16 HUD_Write_Cvar("hud_panel_centerprint_time");
17 HUD_Write_Cvar("hud_panel_centerprint_fade_in");
18 HUD_Write_Cvar("hud_panel_centerprint_fade_out");
19 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent");
20 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent_passone");
21 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent_passone_minalpha");
22 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent_passtwo");
23 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent_passtwo_minalpha");
24 HUD_Write_Cvar("hud_panel_centerprint_fade_subsequent_minfontsize");
25 HUD_Write_Cvar("hud_panel_centerprint_fade_minfontsize");
26}
27
28// These are the functions that draw the text at the center of the screen (e.g. frag messages and server MOTD).
29// centerprint_Add parses a message and puts it in the circular buffer centerprint_messages
30// centerprint_Add is usually called by Local_Notification_centerprint_Add, which is called
31// by Local_Notification.
32// HUD_CenterPrint draws all the messages on screen
33
34const int CENTERPRINT_MAX_MSGS = 10;
36const float CENTERPRINT_BASE_SIZE = 1.3;
37const float CENTERPRINT_SPACING = 0.3;
38const float CENTERPRINT_TITLE_SPACING = 0.35;
47
51
52void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num)
53{
54 TC(int, new_id); TC(int, countdown_num);
55 //LOG_INFOF("centerprint_Add: ^2id: %d ^3dur: %d ^5countdown: %d\n'%s'", new_id, duration, countdown_num, strreplace("\n", "^7\\n^7", strMessage));
56
57 if (strMessage == "" && new_id == 0)
58 return;
59
60 // strip trailing newlines
61 int j = strlen(strMessage) - 1;
62 while (substring(strMessage, j, 1) == "\n" && j >= 0)
63 --j;
64 if (j < strlen(strMessage) - 1)
65 strMessage = substring(strMessage, 0, j + 1);
66
67 if (strMessage == "" && new_id == 0)
68 return;
69
70 // strip leading newlines
71 j = 0;
72 while (substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
73 ++j;
74 if (j > 0)
75 strMessage = substring(strMessage, j, strlen(strMessage) - j);
76
77 if (strMessage == "" && new_id == 0)
78 return;
79
82
83 int i;
84 for (i = 0, j = cpm_index; i < CENTERPRINT_MAX_MSGS; ++i, ++j)
85 {
86 if (j == CENTERPRINT_MAX_MSGS)
87 j = 0;
88 if (new_id && new_id == centerprint_msgID[j])
89 {
90 if (strMessage == "" && centerprint_messages[j] != "")
91 {
92 // fade out the current msg (duration and countdown_num are ignored)
95 centerprint_expire_time[j] = -1; // don't use the variable time here!
96 return;
97 }
98 break; // found a msg with the same id, at position j
99 }
100 }
101
102 if (strMessage == "")
103 return;
104
105 if (i == CENTERPRINT_MAX_MSGS)
106 {
107 // a msg with the same id was not found, add the msg at the next position
108 --cpm_index;
109 if (cpm_index == -1)
111 j = cpm_index;
112 }
113 strcpy(centerprint_messages[j], strMessage);
115 centerprint_msgID[j] = new_id;
116 if (duration < 0)
117 {
118 centerprint_time[j] = -1;
119 centerprint_expire_time[j] = -1; // don't use the variable time here!
120 }
121 else
122 {
123 if (duration == 0)
125 centerprint_time[j] = duration;
126 centerprint_expire_time[j] = -1; // don't use the variable time here!
127 }
128 centerprint_countdown_num[j] = countdown_num;
129}
130
132{
133 TC(int, id);
134 centerprint_Add(id, "", 0, 0);
135}
136
137void centerprint_AddStandard(string strMessage)
138{
140}
141
143{
144 for (int i = 0; i < CENTERPRINT_MAX_MSGS; ++i)
145 {
148 centerprint_time[i] = 1;
149 centerprint_msgID[i] = 0;
151 }
152}
153
160
161void centerprint_SetTitle(string title)
162{
163 if (title != centerprint_title)
165}
166
173
175void HUD_CenterPrint(bool should_draw)
176{
177 if (!should_draw)
178 return;
180 {
182 return;
184 {
187 }
188 }
189 else
190 {
192 {
194 hud_configure_cp_generation_time = time; // show a message immediately
195 }
197 {
198 if (highlightedPanel == panel)
199 {
200 centerprint_SetTitle(_("Title"));
201
202 float r = random();
203 if (r > 0.8)
204 centerprint_Add(floor(r*1000), sprintf(_("^3Countdown message at time %s, seconds left: ^COUNT"), seconds_tostring(time)), 1, 10);
205 else if (r > 0.55)
206 centerprint_Add(0, sprintf(_("^1Multiline message at time %s that\n^BOLDlasts longer than normal"), seconds_tostring(time)), 20, 0);
207 else
208 centerprint_AddStandard(sprintf(_("Message at time %s"), seconds_tostring(time)));
210 }
211 else
212 {
213 centerprint_Add(0, _("Generic message"), 10, 0);
215 }
216 }
217 }
218
220
222 {
224 return;
225
226 panel_pos.x = 0.5 * (vid_conwidth - panel_size.x);
229 }
231 {
232 vector target_pos = vec2(0.5 * (vid_conwidth - panel_size.x), scoreboard_bottom);
233 if (target_pos.y > panel_pos.y)
234 {
235 panel_pos += (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha);
237 }
238
239 // move the panel below the scoreboard
240 if (panel_pos.y >= vid_conheight)
241 return;
242 }
243
246 else
249
251 return;
252
254 {
255 panel_pos += '1 1 0' * panel_bg_padding;
256 panel_size -= '2 2 0' * panel_bg_padding;
257 }
258
260 float current_msg_posY = 0; // k9er: this is unused?
261 bool all_messages_expired = true;
262
263 vector pos = panel_pos;
265 pos.y += panel_size.y;
266 float align = bound(0, autocvar_hud_panel_centerprint_align, 1);
267
268 // Show title if available
269 if (centerprint_title != "" || centerprint_title_left != "")
270 {
272 float width = 0, right_width = 0, left_width = 0, max_rl_width = 0;
273 if (centerprint_title != "")
274 width = stringwidth(centerprint_title, true, fontsize);
275 else
276 {
277 right_width = stringwidth(centerprint_title_right, true, fontsize);
278 left_width = stringwidth(centerprint_title_left, true, fontsize);
279 }
280
282 pos.y -= fontsize.y;
283
284 vector duel_title_pos = '0 0 0';
285 float padding = stringwidth(" ", false, fontsize) * 2;
286 if (centerprint_title_left != "")
287 {
288 if (left_width > right_width)
289 max_rl_width = left_width;
290 else
291 max_rl_width = right_width;
292
293 width = max_rl_width * 2 + padding * 6 + stringwidth(_("vs"), false, fontsize);
294 pos.x += (panel_size.x - width) * align;
295 duel_title_pos = pos;
296
297 pos.x += padding;
298 if (left_width < right_width)
299 pos.x += (max_rl_width - left_width) * 0.5;
301
302 pos.x = duel_title_pos.x + max_rl_width + padding * 3;
303 drawstring(pos, _("vs"), fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
304
305 pos.x = duel_title_pos.x + width - padding - max_rl_width;
306 if (left_width >= right_width)
307 pos.x += (max_rl_width - right_width) * 0.5;
309 }
310 else
311 {
312 pos.x = panel_pos.x + (panel_size.x - width) * align;
314 }
315
317 pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
318 else
319 pos.y += fontsize.y + (hud_fontsize.y * CENTERPRINT_TITLE_SPACING);
320
321 if (centerprint_title_left != "")
322 {
323 pos.x = duel_title_pos.x;
324 drawfill(pos, vec2(max_rl_width + padding * 2, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
325 drawfill(pos + vec2(width - max_rl_width - padding * 2, 0), vec2(max_rl_width + padding * 2, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
326 }
327 else
328 drawfill(pos - eX * padding, vec2(width + 2 * padding, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
329
331 pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
332 else
333 pos.y += cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
334
335 all_messages_expired = false;
336 }
337
338 for (int g = 0, i = 0, j = cpm_index; i < CENTERPRINT_MAX_MSGS; ++i, ++j)
339 {
340 if (j == CENTERPRINT_MAX_MSGS)
341 j = 0;
342 if (centerprint_expire_time[j] == -1)
343 {
344 // here we are sure the time variable is not altered by CSQC_Ent_Update
346 if (centerprint_time[j] > 0)
348 }
350 {
352 {
354 if (centerprint_countdown_num[j] == 0)
355 continue;
357 }
358 else if (centerprint_time[j] != -1)
359 continue;
360 }
361
362 all_messages_expired = false;
363
365 continue;
366
367 float fade_in_time = autocvar_hud_panel_centerprint_fade_in;
368 float fade_out_time = autocvar_hud_panel_centerprint_fade_out;
369
371 {
372 fade_in_time = 0;
373 fade_out_time = 0;
374 }
375
376 // fade
377 float a = 1;
378 if (centerprint_msgID[j] != ORDINAL(CPID_TIMEIN))
379 {
380 if (fade_in_time && centerprint_start_time[j] && time < centerprint_start_time[j] + fade_in_time) // Fade in
381 a = (time - centerprint_start_time[j]) / fade_in_time;
382 else if (time < centerprint_expire_time[j] - fade_out_time || centerprint_time[j] < 0) // Regularily printed or forced
383 a = 1;
384 else if (fade_out_time) // Expiring soon, so fade it out.
385 a = (centerprint_expire_time[j] - time) / fade_out_time;
386 }
387
388 // while counting down show it anyway in order to hold the current message position
389 if (a <= (0.5/255.0) && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show.
390 continue;
391
392 // also fade it based on positioning
394 {
395 // pass one: all messages after the first have half alpha
397 // pass two: after that, gradually lower alpha even more for each message
399 }
400 a *= panel_fg_alpha;
401
402 // finally set the size based on the alpha
405
406 int n, k;
408 n = tokenizebyseparator(strreplace("^COUNT", ftos(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
409 else
411
412 string ts;
414 {
415 // check if the message can be entirely shown
416 for (k = 0; k < n; ++k)
417 {
420 {
421 bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
423
424 ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
425 if (ts != "")
426 pos.y -= fontsize.y;
427 else
428 pos.y -= fontsize.y * CENTERPRINT_SPACING * 0.5;
429 }
430 }
431 current_msg_posY = pos.y; // save starting pos (first line) of the current message
432 }
433
434 float msg_size = pos.y;
435 for (k = 0; k < n; ++k)
436 {
438
439 bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
441 if (is_bold)
443
445 {
446 ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
447 if (ts != "")
448 {
449 if (align)
450 pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize) * sz) * align;
451 if (a > 0.5/255.0) // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker.
452 if (is_bold)
454 drawcolorcodedstring(pos + eY * 0.5 * (1 - sz * hud_scale.x) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL);
455 if (is_bold)
457 pos.y += fontsize.y;
458 }
459 else
460 pos.y += fontsize.y * CENTERPRINT_SPACING * 0.5;
461 }
462 }
463
464 ++g; // move next position number up
465
466 msg_size = pos.y - msg_size;
467
469 {
470 pos.y -= msg_size + CENTERPRINT_SPACING * cp_fontsize.y;
471 if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
472 pos.y += (1 - sqrt(a));
473
474 if (pos.y < panel_pos.y) // check if the next message can be shown
475 {
477 return;
478 }
479 }
480 else
481 {
482 pos.y += CENTERPRINT_SPACING * cp_fontsize.y;
483 if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
484 pos.y -= (1 - sqrt(a));
485
486 if (pos.y > panel_pos.y + panel_size.y - cp_fontsize.y) // check if the next message can be shown
487 {
489 return;
490 }
491 }
492 }
493
495 if (all_messages_expired)
496 {
497 centerprint_showing = false;
499 }
500}
int centerprint_msgID[CENTERPRINT_MAX_MSGS]
int cpm_index
void centerprint_KillAll()
const float CENTERPRINT_BASE_SIZE
void HUD_CenterPrint_Export(int fh)
Definition centerprint.qc:9
void centerprint_ClearTitle()
const int CENTERPRINT_MAX_ENTRIES
const int CENTERPRINT_MAX_MSGS
string centerprint_title_left
string centerprint_title_right
bool centerprint_showing
float hud_configure_cp_generation_time
void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num)
string centerprint_messages[CENTERPRINT_MAX_MSGS]
void centerprint_SetTitle(string title)
const float CENTERPRINT_TITLE_SPACING
void centerprint_SetDuelTitle(string left, string right)
int centerprint_countdown_num[CENTERPRINT_MAX_MSGS]
void HUD_CenterPrint(bool should_draw)
const float CENTERPRINT_SPACING
float centerprint_time[CENTERPRINT_MAX_MSGS]
float centerprint_expire_time[CENTERPRINT_MAX_MSGS]
void centerprint_Kill(int id)
void centerprint_AddStandard(string strMessage)
string centerprint_title
float centerprint_start_time[CENTERPRINT_MAX_MSGS]
float autocvar_hud_panel_centerprint_align
Definition centerprint.qh:5
bool autocvar_hud_panel_centerprint_flip
float autocvar_hud_panel_centerprint_fade_subsequent
Definition centerprint.qh:8
float autocvar_hud_panel_centerprint_fade_out
Definition centerprint.qh:7
float autocvar_hud_panel_centerprint_fade_subsequent_passone
Definition centerprint.qh:9
float autocvar_hud_panel_centerprint_fade_subsequent_passtwo
bool autocvar_hud_panel_centerprint
Definition centerprint.qh:4
float autocvar_hud_panel_centerprint_fontscale_bold
float autocvar_hud_panel_centerprint_fade_minfontsize
float autocvar_hud_panel_centerprint_fontscale_title
float autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha
float autocvar_hud_panel_centerprint_time
bool autocvar_hud_panel_centerprint_dynamichud
float autocvar_hud_panel_centerprint_fontscale
float autocvar_hud_panel_centerprint_fade_in
Definition centerprint.qh:6
float autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha
#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
vector drawfontscale
Definition draw.qh:3
#define drawfill(position, size, rgb, alpha, flag)
Definition draw.qh:36
#define draw_endBoldFont()
Definition draw.qh:5
vector hud_fontsize
Definition main.qh:77
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1171
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1131
string getWrappedLine_remaining
Definition util.qh:147
const float DRAWFLAG_NORMAL
float time
#define stringwidth
#define strlen
#define tokenizebyseparator
#define ORDINAL(it)
Definition enumclass.qh:25
void HUD_Panel_LoadCvars()
Definition hud.qc:215
void HUD_Scale_Enable()
Definition hud.qc:91
void HUD_Scale_Disable()
Definition hud.qc:84
entity highlightedPanel
Definition hud.qh:107
vector panel_size
Definition hud.qh:163
float hud_panel_radar_bottom
Definition hud.qh:72
float panel_fg_alpha
Definition hud.qh:169
float panel_bg_padding
Definition hud.qh:174
#define HUD_Panel_DrawBg()
Definition hud.qh:55
vector hud_scale
Definition hud.qh:221
vector panel_pos
Definition hud.qh:162
entity panel
Definition hud.qh:147
bool HUD_Radar_Clickable()
Definition radar.qc:26
#define HUD_Write_Cvar(cvar)
Definition hud_config.qh:40
float hud_configure_prev
Definition hud_config.qh:18
bool autocvar__hud_configure
Definition hud_config.qh:3
#define TC(T, sym)
Definition _all.inc:82
noref float vid_conwidth
Definition draw.qh:8
noref float vid_conheight
Definition draw.qh:9
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float random(void)
float sqrt(float f)
float min(float f,...)
string ftos(float f)
float floor(float f)
string argv(float n)
float max(float f,...)
#define BOLD_OPERATOR
Definition all.qh:12
float scoreboard_fade_alpha
Definition scoreboard.qh:17
float scoreboard_bottom
Definition scoreboard.qh:22
float autocvar_hud_panel_scoreboard_namesize
Definition scoreboard.qh:7
vector
Definition self.qh:92
#define strfree(this)
Definition string.qh:59
ERASEABLE string seconds_tostring(float seconds)
Definition string.qh:126
string CCR(string input)
color code replace, place inside of sprintf and parse the string
Definition string.qh:216
#define strcpy(this, s)
Definition string.qh:52
float stringwidth_colors(string s, vector theSize)
Definition string.qh:30
const vector eY
Definition vector.qh:45
const vector eX
Definition vector.qh:44
#define vec2(...)
Definition vector.qh:90