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
176{
178 {
180 return;
182 {
185 }
186 }
187 else
188 {
190 {
192 hud_configure_cp_generation_time = time; // show a message immediately
193 }
195 {
196 if (highlightedPanel == panel)
197 {
198 centerprint_SetTitle(_("Title"));
199
200 float r = random();
201 if (r > 0.8)
202 centerprint_Add(floor(r*1000), sprintf(_("^3Countdown message at time %s, seconds left: ^COUNT"), seconds_tostring(time)), 1, 10);
203 else if (r > 0.55)
204 centerprint_Add(0, sprintf(_("^1Multiline message at time %s that\n^BOLDlasts longer than normal"), seconds_tostring(time)), 20, 0);
205 else
206 centerprint_AddStandard(sprintf(_("Message at time %s"), seconds_tostring(time)));
208 }
209 else
210 {
211 centerprint_Add(0, _("Generic message"), 10, 0);
213 }
214 }
215 }
216
218
220 {
222 return;
223
224 panel_pos.x = 0.5 * (vid_conwidth - panel_size.x);
227 }
229 {
230 vector target_pos = vec2(0.5 * (vid_conwidth - panel_size.x), scoreboard_bottom);
231 if (target_pos.y > panel_pos.y)
232 {
235 }
236
237 // move the panel below the scoreboard
238 if (panel_pos.y >= vid_conheight)
239 return;
240 }
241
244 else
247
249 return;
250
252 {
253 panel_pos += '1 1 0' * panel_bg_padding;
254 panel_size -= '2 2 0' * panel_bg_padding;
255 }
256
258 float current_msg_posY = 0; // k9er: this is unused?
259 bool all_messages_expired = true;
260
261 vector pos = panel_pos;
263 pos.y += panel_size.y;
264 float align = bound(0, autocvar_hud_panel_centerprint_align, 1);
265
266 // Show title if available
267 if (centerprint_title != "" || centerprint_title_left != "")
268 {
270 float width = 0, right_width = 0, left_width = 0, max_rl_width = 0;
271 if (centerprint_title != "")
272 width = stringwidth(centerprint_title, true, fontsize);
273 else
274 {
275 right_width = stringwidth(centerprint_title_right, true, fontsize);
276 left_width = stringwidth(centerprint_title_left, true, fontsize);
277 }
278
280 pos.y -= fontsize.y;
281
282 vector duel_title_pos = '0 0 0';
283 float padding = stringwidth(" ", false, fontsize) * 2;
284 if (centerprint_title_left != "")
285 {
286 if (left_width > right_width)
287 max_rl_width = left_width;
288 else
289 max_rl_width = right_width;
290
291 width = max_rl_width * 2 + padding * 6 + stringwidth(_("vs"), false, fontsize);
292 pos.x += (panel_size.x - width) * align;
293 duel_title_pos = pos;
294
295 pos.x += padding;
296 if (left_width < right_width)
297 pos.x += (max_rl_width - left_width) * 0.5;
299
300 pos.x = duel_title_pos.x + max_rl_width + padding * 3;
301 drawstring(pos, _("vs"), fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
302
303 pos.x = duel_title_pos.x + width - padding - max_rl_width;
304 if (left_width >= right_width)
305 pos.x += (max_rl_width - right_width) * 0.5;
307 }
308 else
309 {
310 pos.x = panel_pos.x + (panel_size.x - width) * align;
312 }
313
315 pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
316 else
317 pos.y += fontsize.y + (hud_fontsize.y * CENTERPRINT_TITLE_SPACING);
318
319 if (centerprint_title_left != "")
320 {
321 pos.x = duel_title_pos.x;
322 drawfill(pos, vec2(max_rl_width + padding * 2, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
323 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);
324 }
325 else
326 drawfill(pos - eX * padding, vec2(width + 2 * padding, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
327
329 pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
330 else
331 pos.y += cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
332
333 all_messages_expired = false;
334 }
335
336 for (int g = 0, i = 0, j = cpm_index; i < CENTERPRINT_MAX_MSGS; ++i, ++j)
337 {
338 if (j == CENTERPRINT_MAX_MSGS)
339 j = 0;
340 if (centerprint_expire_time[j] == -1)
341 {
342 // here we are sure the time variable is not altered by CSQC_Ent_Update
344 if (centerprint_time[j] > 0)
346 }
348 {
350 {
352 if (centerprint_countdown_num[j] == 0)
353 continue;
355 }
356 else if (centerprint_time[j] != -1)
357 continue;
358 }
359
360 all_messages_expired = false;
361
363 continue;
364
365 float fade_in_time = autocvar_hud_panel_centerprint_fade_in;
366 float fade_out_time = autocvar_hud_panel_centerprint_fade_out;
367
369 {
370 fade_in_time = 0;
371 fade_out_time = 0;
372 }
373
374 // fade
375 float a = 1;
376 if (centerprint_msgID[j] != ORDINAL(CPID_TIMEIN))
377 {
378 if (fade_in_time && centerprint_start_time[j] && time < centerprint_start_time[j] + fade_in_time) // Fade in
379 a = (time - centerprint_start_time[j]) / fade_in_time;
380 else if (time < centerprint_expire_time[j] - fade_out_time || centerprint_time[j] < 0) // Regularily printed or forced
381 a = 1;
382 else if (fade_out_time) // Expiring soon, so fade it out.
383 a = (centerprint_expire_time[j] - time) / fade_out_time;
384 }
385
386 // while counting down show it anyway in order to hold the current message position
387 if (a <= (0.5/255.0) && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show.
388 continue;
389
390 // also fade it based on positioning
392 {
393 // pass one: all messages after the first have half alpha
395 // pass two: after that, gradually lower alpha even more for each message
397 }
398 a *= panel_fg_alpha;
399
400 // finally set the size based on the alpha
403
404 int n, k;
406 n = tokenizebyseparator(strreplace("^COUNT", ftos(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
407 else
409
410 string ts;
412 {
413 // check if the message can be entirely shown
414 for (k = 0; k < n; ++k)
415 {
418 {
419 bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
421
422 ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
423 if (ts != "")
424 pos.y -= fontsize.y;
425 else
426 pos.y -= fontsize.y * CENTERPRINT_SPACING * 0.5;
427 }
428 }
429 current_msg_posY = pos.y; // save starting pos (first line) of the current message
430 }
431
432 float msg_size = pos.y;
433 for (k = 0; k < n; ++k)
434 {
436
437 bool is_bold = (substring(getWrappedLine_remaining, 0, 5) == BOLD_OPERATOR);
439 if (is_bold)
441
443 {
444 ts = getWrappedLine(panel_size.x * hud_scale.x * sz, fontsize, stringwidth_colors);
445 if (ts != "")
446 {
447 if (align)
448 pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize) * sz) * align;
449 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.
450 if (is_bold)
452 drawcolorcodedstring(pos + eY * 0.5 * (1 - sz * hud_scale.x) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL);
453 if (is_bold)
455 pos.y += fontsize.y;
456 }
457 else
458 pos.y += fontsize.y * CENTERPRINT_SPACING * 0.5;
459 }
460 }
461
462 ++g; // move next position number up
463
464 msg_size = pos.y - msg_size;
465
467 {
468 pos.y -= msg_size + CENTERPRINT_SPACING * cp_fontsize.y;
469 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
470 pos.y += (1 - sqrt(a));
471
472 if (pos.y < panel_pos.y) // check if the next message can be shown
473 {
475 return;
476 }
477 }
478 else
479 {
480 pos.y += CENTERPRINT_SPACING * cp_fontsize.y;
481 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
482 pos.y -= (1 - sqrt(a));
483
484 if (pos.y > panel_pos.y + panel_size.y - cp_fontsize.y) // check if the next message can be shown
485 {
487 return;
488 }
489 }
490 }
491
493 if (all_messages_expired)
494 {
495 centerprint_showing = false;
497 }
498}
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]
const float CENTERPRINT_SPACING
float centerprint_time[CENTERPRINT_MAX_MSGS]
void HUD_CenterPrint()
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:1071
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:995
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