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, and notify panel if it has an icon)
8 #define MSG_CENTER 2 // "Personal" centerprint messages
9 #define MSG_WEAPON 3 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
12 #define NO_FL_ARG -12345
18 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
20 #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
21 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
22 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
25 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
27 #define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
28 #define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
29 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
31 #define ADD_CSQC_AUTOCVAR(name)
35 // ====================================
36 // Notifications List and Information
37 // ====================================
39 List of all notifications (including identifiers and display information)
40 Format: name, strnum, flnum, args, *icon/CPID, *durcnt, normal, gentle
41 Asterisked fields are not present in all notification types.
44 Number of STRING arguments (so that networking knows how many to send/receive)
45 Number of FLOAT arguments (so that networking knows how many to send/receive)
46 Arguments for sprintf(string, args), if no args needed then use ""
48 MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
49 MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
51 MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
52 Normal message (string for sprintf when gentle messages are NOT enabled)
53 Gentle message (string for sprintf when gentle messages ARE enabled)
55 Messages have ^F1, ^F2, and ^BG in them-- these are replaced
56 with colors according to the cvars the user has chosen.
57 ^F1 = highest priority, "primary"
58 ^F2 = next highest priority, "secondary"
59 ^BG = normal/less important priority, "tertiary"
61 Guidlines (please try and follow these):
62 ALWAYS start the string with a color, preferably background.
63 ALWAYS properly use tab spacing to even out the notifications.
64 NEVER re-declare an event twice.
65 NEVER add or remove fields from the format, it SHOULD already work.
66 MSG_INFO messages must ALWAYS end with a new line: \n
67 Be clean and simple with your notification naming, nothing too long.
68 Keep the notifications in alphabetical order.
69 ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
72 // flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
73 // weaponorder[f1].netname
74 #define MSG_INFO_NOTIFICATIONS \
75 MSG_INFO_NOTIF(INFO_CTF_PICKUP_RED, 2, 1, XPND3(s1, s2, "foobar"), "notify_death", _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
78 #define MSG_CENTER_NOTIFICATIONS \
79 MSG_CENTER_NOTIF(CENTER_EMPTY, 0, 0, NO_STR_ARG, NO_CPID, XPND2(0, 0), "", "") \
80 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED, 0, 0, NO_STR_ARG, 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."), "") \
81 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE, 0, 0, NO_STR_ARG, 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."), "") \
82 MSG_CENTER_NOTIF(CENTER_CTF_PASS_OTHER_RED, 2, 0, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^1RED^BG flag to %s"), "") \
83 MSG_CENTER_NOTIF(CENTER_CTF_PASS_OTHER_BLUE, 2, 0, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^4BLUE^BG flag to %s"), "") \
84 MSG_CENTER_NOTIF(CENTER_CTF_PASS_SENT_RED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^1RED^BG flag to %s"), "") \
85 MSG_CENTER_NOTIF(CENTER_CTF_PASS_SENT_BLUE, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^4BLUE^BG flag to %s"), "") \
86 MSG_CENTER_NOTIF(CENTER_CTF_PASS_RECEIVED_RED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^1RED^BG flag from %s"), "") \
87 MSG_CENTER_NOTIF(CENTER_CTF_PASS_RECEIVED_BLUE, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^4BLUE^BG flag from %s"), "") \
88 MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGRequesting %s to pass you the flag"), "") \
89 MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BG%s requests you to pass the flag"), "") \
90 MSG_CENTER_NOTIF(CENTER_CTF_RETURN_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^1RED^BG flag"), "") \
91 MSG_CENTER_NOTIF(CENTER_CTF_RETURN_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^4BLUE^BG flag"), "") \
92 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURE_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou captured the ^1RED^BG flag"), "") \
93 MSG_CENTER_NOTIF(CENTER_CTF_CAPTURE_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou captured the ^4BLUE^BG flag"), "") \
94 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou got the ^1RED^BG flag!"), "") \
95 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou got the ^4BLUE^BG flag!"), "") \
96 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
97 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM_VERBOSE, 2, 0, XPND3(s1, s2, s1), CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
98 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
99 MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY_VERBOSE, 2, 0, XPND3(s1, s2, s1), CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
100 MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_CARRIER, 0, 0, NO_STR_ARG, CPID_STALEMATE, XPND2(0, 0), _("^BGStalemate! Enemies can now see you on radar!"), "") \
101 MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_OTHER, 0, 0, NO_STR_ARG, CPID_STALEMATE, XPND2(0, 0), _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
102 MSG_CENTER_NOTIF(CENTER_CTF_FLAG_THROW_PUNISH, 0, 1, f1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGToo many flag throws! Throwing disabled for %d seconds."), "") \
103 #undef MSG_CENTER_NOTIF
105 #define MSG_WEAPON_NOTIFICATIONS \
106 MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, 2, 1, XPND3(s1, s2, f1), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
107 #undef MSG_WEAPON_NOTIF
110 // ====================================
111 // Initialization/Create Declarations
112 // ====================================
114 #define NOTIF_FIRST 1
115 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
116 float NOTIF_INFO_COUNT;
117 float NOTIF_CENTER_COUNT;
118 float NOTIF_WEAPON_COUNT;
119 float NOTIF_CPID_COUNT;
121 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
122 ADD_CSQC_AUTOCVAR(name) \
124 void RegisterNotification_##name() \
126 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
127 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
129 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
131 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
132 ADD_CSQC_AUTOCVAR(name) \
135 void RegisterNotification_##name() \
137 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
138 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
139 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
141 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
143 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
144 ADD_CSQC_AUTOCVAR(name) \
146 void RegisterNotification_##name() \
148 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
149 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
151 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
153 // NOW we actually activate the declarations
154 MSG_INFO_NOTIFICATIONS
155 MSG_CENTER_NOTIFICATIONS
156 MSG_WEAPON_NOTIFICATIONS
159 // ======================
160 // Supporting Functions
161 // ======================
163 // select between the normal or the gentle message string based on client (or server) settings
164 string normal_or_gentle(string normal, string gentle)
167 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
169 if(autocvar_sv_gentle)
171 return ((gentle != "") ? gentle : normal);
176 float notif_stringcount(string s1, string s2)
179 if(s1 != NO_STR_ARG) ++stringcount;
180 if(s2 != NO_STR_ARG) ++stringcount;
184 float notif_floatcount(float f1, float f2, float f3)
187 if(f1 != NO_FL_ARG) ++floatcount;
188 if(f2 != NO_FL_ARG) ++floatcount;
189 if(f3 != NO_FL_ARG) ++floatcount;
193 // get the actual name of a notification and return it as a string
194 string Get_Field_Value(float field, float net_type, float net_name)
198 #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
199 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
200 else if(field == F_STRNUM) { output = ftos(strnum); } \
201 else if(field == F_FLNUM) { output = ftos(flnum); }
207 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
208 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
209 MSG_INFO_NOTIFICATIONS
214 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
215 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
216 MSG_CENTER_NOTIFICATIONS
221 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
222 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
223 MSG_WEAPON_NOTIFICATIONS
228 #undef GET_FIELD_VALUE_OUTPUT
232 // color code replace, place inside of sprintf and parse the string
233 string CCR(string input)
235 input = strreplace("^F1", "^3", input); // autocvar_notification_colors_F1
236 input = strreplace("^F2", "^2", input); // autocvar_notification_colors_F2
237 input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
238 input = strreplace("^K2", "^5", input); // autocvar_notification_colors_K2
239 input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
241 input = strreplace("^N", "^7", input); // "none"-- reset to white
247 // ===============================
248 // Frontend Notification Pushing
249 // ===============================
252 void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
258 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
259 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
260 MSG_INFO_NOTIFICATIONS
265 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
266 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
267 MSG_CENTER_NOTIFICATIONS
272 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
273 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print("unhandled\n"); } }
274 MSG_WEAPON_NOTIFICATIONS
282 // =========================
283 // Notification Networking
284 // =========================
287 void Read_Notification(void)
289 float net_type = ReadByte();
290 float net_name = ReadShort();
292 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
293 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
295 Local_Notification(net_type, net_name,
296 ((stringcount >= 1) ? ReadString() : ""),
297 ((stringcount == 2) ? ReadString() : ""),
298 ((floatcount >= 1) ? ReadLong() : 0),
299 ((floatcount >= 2) ? ReadLong() : 0),
300 ((floatcount == 3) ? ReadLong() : 0));
305 void Send_Notification(entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
307 if(net_type && net_name)
309 print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
311 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
312 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
314 if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
315 if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
317 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
319 // personal/direct notification sent to ONE person and their spectators
321 WRITESPECTATABLE_MSG_ONE({
322 WriteByte(MSG_ONE, SVC_TEMPENTITY);
323 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
324 WriteByte(MSG_ONE, net_type);
325 WriteShort(MSG_ONE, net_name);
326 if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
327 if(stringcount == 2) { WriteString(MSG_ONE, s2); }
328 if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
329 if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
330 if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
335 // global notification sent to EVERYONE
336 WriteByte(MSG_ALL, SVC_TEMPENTITY);
337 WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
338 WriteByte(MSG_ALL, net_type);
339 WriteShort(MSG_ALL, net_name);
340 if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
341 if(stringcount == 2) { WriteString(MSG_ALL, s2); }
342 if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
343 if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
344 if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
347 if(!server_is_local && (net_type == MSG_INFO))
349 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
350 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
351 MSG_INFO_NOTIFICATIONS
354 else { backtrace("Incorrect usage of Send_Notification!\n"); }
357 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
360 FOR_EACH_REALCLIENT(tmp_entity)
362 if(tmp_entity.classname == STR_PLAYER)
363 if(tmp_entity.team == targetteam)
364 if(tmp_entity != except)
366 Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
371 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
372 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
375 FOR_EACH_REALCLIENT(tmp_entity)
377 if((tmp_entity.classname == STR_PLAYER) || spectators)
378 if(tmp_entity != except)
380 Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
386 // =============================
387 // LEGACY NOTIFICATION SYSTEMS
388 // =============================
390 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
392 WriteByte(MSG_ALL, SVC_TEMPENTITY);
393 WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
394 WriteString(MSG_ALL, s1);
395 WriteString(MSG_ALL, s2);
396 WriteString(MSG_ALL, s3);
397 WriteShort(MSG_ALL, msg);
398 WriteByte(MSG_ALL, type);
401 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
402 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
404 if (clienttype(e) == CLIENTTYPE_REAL)
407 WRITESPECTATABLE_MSG_ONE({
408 WriteByte(MSG_ONE, SVC_TEMPENTITY);
409 WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
410 WriteString(MSG_ONE, s1);
411 WriteString(MSG_ONE, s2);
412 WriteShort(MSG_ONE, msg);
413 WriteByte(MSG_ONE, type);
418 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
420 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
423 WRITESPECTATABLE_MSG_ONE({
424 WriteByte(MSG_ONE, SVC_TEMPENTITY);
425 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
426 WriteByte(MSG_ONE, id);
427 WriteString(MSG_ONE, s);
428 if (id != 0 && s != "")
430 WriteByte(MSG_ONE, duration);
431 WriteByte(MSG_ONE, countdown_num);
436 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
438 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);