1 // ================================================
2 // Unified notification system, written by Samual
3 // Last updated: September, 2012
4 // ================================================
6 // main types/groups of notifications
7 #define MSG_INFO 1 // "Global" information messages (sent to console)
8 #define MSG_NOTIFY 2 // "Global" events to be sent to the notification panel
9 #define MSG_CENTER 3 // "Personal" centerprint messages
10 #define MSG_WEAPON 4 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
12 // expand multiple arguments into one argument
13 #define XPND4(a,b,c,d) a, b, c, d
14 #define XPND3(a,b,c) a, b, c
15 #define XPND2(a,b) a, b
17 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
19 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
20 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
21 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
24 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
25 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
26 #define VAR_TO_TEXT(var) #var
28 #define CHECK_FIELD_AND_COUNT(field,count) if(!field) { field = (NOTIF_FIRST + count); ++count; }
29 #define CHECK_MAX_NOTIFICATIONS(name,count) if(count == NOTIF_MAX) { error(strcat("Maximum notifications hit: ", VAR_TO_TEXT(name), ": ", ftos(count), ".\n")); }
32 // ====================================
33 // Notifications List and Information
34 // ====================================
36 List of all notifications (including identifiers and display information)
37 Format: name, args, *icon/CPID, *durcnt, normal, gentle
38 Asterisked fields are not present in all notification types.
41 Arguments for sprintf(string, args), if no args needed then use ""
43 MSG_NOTIFY: STRING: icon string name for the hud notify panel, "" if no icon is used
44 MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
46 MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
47 Normal message (string for sprintf when gentle messages are NOT enabled)
48 Gentle message (string for sprintf when gentle messages ARE enabled)
50 Messages have ^F1, ^F2, and ^BG in them-- these are replaced
51 with colors according to the cvars the user has chosen.
52 ^F1 = highest priority, "primary"
53 ^F2 = next highest priority, "secondary"
54 ^BG = normal/less important priority, "tertiary"
56 Guidlines (please try and follow these):
57 ALWAYS start the string with a color, preferably background.
58 ALWAYS end messages with a new line.
59 ALWAYS properly use tab spacing to even out the notifications.
60 NEVER re-declare an event twice.
61 NEVER add or remove fields from the format, it SHOULD already work.
62 ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
63 Be clean and simple with your notification naming, nothing too long.
64 Keep the notifications in alphabetical order.
66 #define MSG_INFO_NOTIFICATIONS \
67 MSG_INFO_NOTIF(DEATH_MARBLES_LOST, XPND3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
70 #define MSG_NOTIFY_NOTIFICATIONS \
71 MSG_NOTIFY_NOTIF(DEATH_MARBLES_LOST2, XPND3(s1, s2, s3), "notify_death", _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
72 #undef MSG_NOTIFY_NOTIF
74 #define MSG_CENTER_NOTIFICATIONS \
75 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED, "", CPID_CTF_CAPTURESHIELD, XPND2(0, 0), _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
76 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE, "", CPID_CTF_CAPTURESHIELD, XPND2(0, 0), _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
77 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS, XPND2(s1, s2, s3), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^F1%s^BG to %s"), "") \
78 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_SENT, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^F1%s^BG to %s"), "") \
79 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_RECEIVED, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^F1%s^BG from %s"), "") \
80 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
81 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, s1, NO_CPID, XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
82 #undef MSG_CENTER_NOTIF
84 #define MSG_WEAPON_NOTIFICATIONS \
85 MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, XPND3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
86 #undef MSG_WEAPON_NOTIF
89 // ====================================
90 // Initialization/Create Declarations
91 // ====================================
94 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
95 float NOTIF_INFO_COUNT;
96 float NOTIF_NOTIFY_COUNT;
97 float NOTIF_CENTER_COUNT;
98 float NOTIF_WEAPON_COUNT;
99 float NOTIF_CPID_COUNT;
101 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
103 void DecNotif_##name() \
105 CHECK_FIELD_AND_COUNT(name, NOTIF_INFO_COUNT) \
106 CHECK_MAX_NOTIFICATIONS(name, NOTIF_INFO_COUNT) \
108 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
110 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
112 void DecNotif_##name() \
114 CHECK_FIELD_AND_COUNT(name, NOTIF_NOTIFY_COUNT) \
115 CHECK_MAX_NOTIFICATIONS(name, NOTIF_NOTIFY_COUNT) \
117 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
119 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
122 void DecNotif_##name() \
124 CHECK_FIELD_AND_COUNT(name, NOTIF_CENTER_COUNT) \
125 CHECK_FIELD_AND_COUNT(cpid, NOTIF_CPID_COUNT) \
126 CHECK_MAX_NOTIFICATIONS(name, NOTIF_CENTER_COUNT) \
128 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
130 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
132 void DecNotif_##name() \
134 CHECK_FIELD_AND_COUNT(name, NOTIF_WEAPON_COUNT) \
135 CHECK_MAX_NOTIFICATIONS(name, NOTIF_WEAPON_COUNT) \
137 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
139 // NOW we actually activate the declarations
140 MSG_INFO_NOTIFICATIONS
141 MSG_NOTIFY_NOTIFICATIONS
142 MSG_CENTER_NOTIFICATIONS
143 MSG_WEAPON_NOTIFICATIONS
146 // ======================
147 // Supporting Functions
148 // ======================
150 // select between the normal or the gentle message string based on client (or server) settings
151 string normal_or_gentle(string normal, string gentle)
154 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
156 if(autocvar_sv_gentle)
158 return ((gentle != "") ? gentle : normal);
163 // get the actual name of a notification and return it as a string
164 string Get_Notif_Name(float net_type, float net_name)
170 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
171 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
172 MSG_INFO_NOTIFICATIONS
177 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
178 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
179 MSG_NOTIFY_NOTIFICATIONS
184 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
185 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
186 MSG_CENTER_NOTIFICATIONS
191 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
192 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
193 MSG_WEAPON_NOTIFICATIONS
200 // color code replace, place inside of sprintf and parse the string
201 string CCR(string input)
203 input = strreplace("^F1", "^3", input);
204 input = strreplace("^F2", "^2", input);
205 input = strreplace("^K1", "^1", input);
206 input = strreplace("^K2", "^5", input);
207 input = strreplace("^BG", "^7", input);
209 input = strreplace("^N", "^7", input); // "none"-- reset to white
215 // ===============================
216 // Frontend Notification Pushing
217 // ===============================
220 void Local_Notification(float net_type, float net_name, string s1, string s2, string s3)
226 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
227 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
229 MSG_INFO_NOTIFICATIONS
234 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
235 { NOTIF_MATCH(name,net_name) { print("unhandled\n"); } }
237 MSG_NOTIFY_NOTIFICATIONS
242 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
243 { NOTIF_MATCH(name, net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
245 MSG_CENTER_NOTIFICATIONS
250 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
251 { NOTIF_MATCH(name,net_name) { print("unhandled\n"); } }
253 MSG_WEAPON_NOTIFICATIONS
261 // =========================
262 // Notification Networking
263 // =========================
266 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
268 if(net_type && net_name)
270 print("notification: ", Get_Notif_Name(net_type, net_name), ": ", ftos(net_name), ".\n");
272 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
274 // personal/direct notification sent to ONE person and their spectators
276 WRITESPECTATABLE_MSG_ONE({
277 WriteByte(MSG_ONE, SVC_TEMPENTITY);
278 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
279 WriteShort(MSG_ONE, net_type);
280 WriteCoord(MSG_ONE, net_name);
281 WriteString(MSG_ONE, s1);
282 WriteString(MSG_ONE, s2);
283 WriteString(MSG_ALL, s3);
288 // global notification sent to EVERYONE
289 WriteByte(MSG_ALL, SVC_TEMPENTITY);
290 WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
291 WriteShort(MSG_ALL, net_type);
292 WriteCoord(MSG_ALL, net_name);
293 WriteString(MSG_ALL, s1);
294 WriteString(MSG_ALL, s2);
295 WriteString(MSG_ALL, s3);
298 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
304 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
305 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
307 MSG_INFO_NOTIFICATIONS
313 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
314 { NOTIF_MATCH(name,net_name) { print("unhandled\n"); } }
316 MSG_NOTIFY_NOTIFICATIONS
322 else { backtrace("Incorrect usage of Send_Notification!\n"); }
325 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
328 FOR_EACH_REALCLIENT(tmp_entity)
330 if(tmp_entity.classname == STR_PLAYER)
331 if(tmp_entity.team == targetteam)
332 if(tmp_entity != except)
334 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
339 // use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
340 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
343 FOR_EACH_REALCLIENT(tmp_entity)
345 if((tmp_entity.classname == STR_PLAYER) || spectators)
346 if(tmp_entity != except)
348 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
353 // LEGACY NOTIFICATION SYSTEMS
354 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
356 WriteByte(MSG_ALL, SVC_TEMPENTITY);
357 WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
358 WriteString(MSG_ALL, s1);
359 WriteString(MSG_ALL, s2);
360 WriteString(MSG_ALL, s3);
361 WriteShort(MSG_ALL, msg);
362 WriteByte(MSG_ALL, type);
365 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
366 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
368 if (clienttype(e) == CLIENTTYPE_REAL)
371 WRITESPECTATABLE_MSG_ONE({
372 WriteByte(MSG_ONE, SVC_TEMPENTITY);
373 WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
374 WriteString(MSG_ONE, s1);
375 WriteString(MSG_ONE, s2);
376 WriteShort(MSG_ONE, msg);
377 WriteByte(MSG_ONE, type);
382 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
384 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
387 WRITESPECTATABLE_MSG_ONE({
388 WriteByte(MSG_ONE, SVC_TEMPENTITY);
389 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
390 WriteByte(MSG_ONE, id);
391 WriteString(MSG_ONE, s);
392 if (id != 0 && s != "")
394 WriteByte(MSG_ONE, duration);
395 WriteByte(MSG_ONE, countdown_num);
400 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
402 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);