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 HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
26 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
29 // ====================================
30 // Notifications List and Information
31 // ====================================
33 List of all notifications (including identifiers and display information)
34 Format: name, strnum, flnum, args, *icon/CPID, *durcnt, normal, gentle
35 Asterisked fields are not present in all notification types.
38 Number of STRING arguments (so that networking knows how many to send/receive)
39 Number of FLOAT arguments (so that networking knows how many to send/receive)
40 Arguments for sprintf(string, args), if no args needed then use ""
42 MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
43 MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
45 MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
46 Normal message (string for sprintf when gentle messages are NOT enabled)
47 Gentle message (string for sprintf when gentle messages ARE enabled)
49 Messages have ^F1, ^F2, and ^BG in them-- these are replaced
50 with colors according to the cvars the user has chosen.
51 ^F1 = highest priority, "primary"
52 ^F2 = next highest priority, "secondary"
53 ^BG = normal/less important priority, "tertiary"
55 Guidlines (please try and follow these):
56 ALWAYS start the string with a color, preferably background.
57 ALWAYS properly use tab spacing to even out the notifications.
58 NEVER re-declare an event twice.
59 NEVER add or remove fields from the format, it SHOULD already work.
60 MSG_INFO messages must ALWAYS end with a new line: \n
61 Be clean and simple with your notification naming, nothing too long.
62 Keep the notifications in alphabetical order.
63 ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
66 // flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
67 // weaponorder[f1].netname
68 #define MSG_INFO_NOTIFICATIONS \
69 MSG_INFO_NOTIF(INFO_CTF_EVENT_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"), "") \
72 #define MSG_CENTER_NOTIFICATIONS \
73 MSG_CENTER_NOTIF(CENTER_EMPTY, 0, 0, NO_STR_ARG, NO_CPID, XPND2(0, 0), "", "") \
74 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."), "") \
75 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."), "") \
76 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"), "") \
77 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"), "") \
78 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"), "") \
79 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"), "") \
80 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"), "") \
81 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"), "") \
82 MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGRequesting %s to pass you the flag"), "") \
83 MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BG%s requests you to pass the flag"), "") \
84 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^1RED^BG flag"), "") \
85 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^4BLUE^BG flag"), "") \
86 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou captured the ^1RED^BG flag"), "") \
87 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou captured the ^4BLUE^BG flag"), "") \
88 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PICKUP_RED, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou got the ^1RED^BG flag!"), "") \
89 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PICKUP_BLUE, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou got the ^4BLUE^BG flag!"), "") \
90 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PICKUP_TEAM, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
91 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_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!"), "") \
92 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PICKUP_ENEMY, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
93 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_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!"), "") \
94 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!"), "") \
95 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!"), "") \
96 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."), "") \
97 #undef MSG_CENTER_NOTIF
99 #define MSG_WEAPON_NOTIFICATIONS \
100 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"), "") \
101 #undef MSG_WEAPON_NOTIF
104 // ====================================
105 // Initialization/Create Declarations
106 // ====================================
108 #define NOTIF_FIRST 1
109 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
110 float NOTIF_INFO_COUNT;
111 float NOTIF_CENTER_COUNT;
112 float NOTIF_WEAPON_COUNT;
113 float NOTIF_CPID_COUNT;
116 #define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
117 #define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
119 #define ADD_CSQC_AUTOCVAR(name)
122 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
123 ADD_CSQC_AUTOCVAR(name) \
125 void RegisterNotification_##name() \
127 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
128 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
130 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
132 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
133 ADD_CSQC_AUTOCVAR(name) \
136 void RegisterNotification_##name() \
138 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
139 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
140 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
142 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
144 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
145 ADD_CSQC_AUTOCVAR(name) \
147 void RegisterNotification_##name() \
149 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
150 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
152 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
154 // NOW we actually activate the declarations
155 MSG_INFO_NOTIFICATIONS
156 MSG_CENTER_NOTIFICATIONS
157 MSG_WEAPON_NOTIFICATIONS
160 // ======================
161 // Supporting Functions
162 // ======================
164 // select between the normal or the gentle message string based on client (or server) settings
165 string normal_or_gentle(string normal, string gentle)
168 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
170 if(autocvar_sv_gentle)
172 return ((gentle != "") ? gentle : normal);
177 float notif_stringcount(string s1, string s2)
180 if(s1 != NO_STR_ARG) ++stringcount;
181 if(s2 != NO_STR_ARG) ++stringcount;
185 float notif_floatcount(float f1, float f2, float f3)
188 if(f1 != NO_FL_ARG) ++floatcount;
189 if(f2 != NO_FL_ARG) ++floatcount;
190 if(f3 != NO_FL_ARG) ++floatcount;
194 #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
195 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
196 else if(field == F_STRNUM) { output = ftos(strnum); } \
197 else if(field == F_FLNUM) { output = ftos(flnum); }
199 // get the actual name of a notification and return it as a string
200 string Get_Field_Value(float field, float net_type, float net_name)
208 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
209 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
210 MSG_INFO_NOTIFICATIONS
215 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
216 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
217 MSG_CENTER_NOTIFICATIONS
222 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
223 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
224 MSG_WEAPON_NOTIFICATIONS
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);