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 end messages with a new line.
58 ALWAYS properly use tab spacing to even out the notifications.
59 NEVER re-declare an event twice.
60 NEVER add or remove fields from the format, it SHOULD already work.
61 ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
62 Be clean and simple with your notification naming, nothing too long.
63 Keep the notifications in alphabetical order.
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_GOTFLAG_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_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."), "") \
74 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."), "") \
75 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_RED, 2, 0, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^1RED^BG flag to %s"), "") \
76 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_SENT_RED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^1RED^BG flag to %s"), "") \
77 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_RECEIVED_RED, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^1RED^BG flag from %s"), "") \
78 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_BLUE, 2, 0, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^4BLUE^BG flag to %s"), "") \
79 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_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_EVENT_PASS_RECEIVED_BLUE, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^4BLUE^BG flag from %s"), "") \
81 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
82 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, 0, 0, NO_STR_ARG, NO_CPID, XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
83 #undef MSG_CENTER_NOTIF
85 #define MSG_WEAPON_NOTIFICATIONS \
86 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"), "") \
87 #undef MSG_WEAPON_NOTIF
90 // ====================================
91 // Initialization/Create Declarations
92 // ====================================
95 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
96 float NOTIF_INFO_COUNT;
97 float NOTIF_CENTER_COUNT;
98 float NOTIF_WEAPON_COUNT;
99 float NOTIF_CPID_COUNT;
102 #define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
103 #define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
105 #define ADD_CSQC_AUTOCVAR(name)
108 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
109 ADD_CSQC_AUTOCVAR(name) \
111 void RegisterNotification_##name() \
113 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
114 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
116 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
118 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
119 ADD_CSQC_AUTOCVAR(name) \
122 void RegisterNotification_##name() \
124 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
125 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
126 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
128 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
130 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
131 ADD_CSQC_AUTOCVAR(name) \
133 void RegisterNotification_##name() \
135 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
136 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
138 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
140 // NOW we actually activate the declarations
141 MSG_INFO_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 float notif_stringcount(string s1, string s2)
166 if(s1 != NO_STR_ARG) ++stringcount;
167 if(s2 != NO_STR_ARG) ++stringcount;
171 float notif_floatcount(float f1, float f2, float f3)
174 if(f1 != NO_FL_ARG) ++floatcount;
175 if(f2 != NO_FL_ARG) ++floatcount;
176 if(f3 != NO_FL_ARG) ++floatcount;
180 #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
181 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
182 else if(field == F_STRNUM) { output = ftos(strnum); } \
183 else if(field == F_FLNUM) { output = ftos(flnum); }
185 // get the actual name of a notification and return it as a string
186 string Get_Field_Value(float field, float net_type, float net_name)
194 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
195 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
196 MSG_INFO_NOTIFICATIONS
201 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
202 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
203 MSG_CENTER_NOTIFICATIONS
208 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
209 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
210 MSG_WEAPON_NOTIFICATIONS
218 // color code replace, place inside of sprintf and parse the string
219 string CCR(string input)
221 input = strreplace("^F1", "^3", input); // autocvar_notification_colors_F1
222 input = strreplace("^F2", "^2", input); // autocvar_notification_colors_F2
223 input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
224 input = strreplace("^K2", "^5", input); // autocvar_notification_colors_K2
225 input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
227 input = strreplace("^N", "^7", input); // "none"-- reset to white
233 // ===============================
234 // Frontend Notification Pushing
235 // ===============================
238 void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
244 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
245 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
246 MSG_INFO_NOTIFICATIONS
251 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
252 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
253 MSG_CENTER_NOTIFICATIONS
258 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
259 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print("unhandled\n"); } }
260 MSG_WEAPON_NOTIFICATIONS
268 // =========================
269 // Notification Networking
270 // =========================
273 void Read_Notification(void)
275 float net_type = ReadByte();
276 float net_name = ReadShort();
278 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
279 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
281 Local_Notification(net_type, net_name,
282 ((stringcount >= 1) ? ReadString() : ""),
283 ((stringcount == 2) ? ReadString() : ""),
284 ((floatcount >= 1) ? ReadLong() : 0),
285 ((floatcount >= 2) ? ReadLong() : 0),
286 ((floatcount == 3) ? ReadLong() : 0));
291 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, float f1, float f2, float f3)
293 if(net_type && net_name)
295 print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
297 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
298 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
300 if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
301 if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
303 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
305 // personal/direct notification sent to ONE person and their spectators
307 WRITESPECTATABLE_MSG_ONE({
308 WriteByte(MSG_ONE, SVC_TEMPENTITY);
309 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
310 WriteByte(MSG_ONE, net_type);
311 WriteShort(MSG_ONE, net_name);
312 if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
313 if(stringcount == 2) { WriteString(MSG_ONE, s2); }
314 if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
315 if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
316 if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
321 // global notification sent to EVERYONE
322 WriteByte(MSG_ALL, SVC_TEMPENTITY);
323 WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
324 WriteByte(MSG_ALL, net_type);
325 WriteShort(MSG_ALL, net_name);
326 if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
327 if(stringcount == 2) { WriteString(MSG_ALL, s2); }
328 if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
329 if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
330 if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
333 if(!server_is_local && (net_type == MSG_INFO))
335 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
336 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
337 MSG_INFO_NOTIFICATIONS
340 else { backtrace("Incorrect usage of Send_Notification!\n"); }
343 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
346 FOR_EACH_REALCLIENT(tmp_entity)
348 if(tmp_entity.classname == STR_PLAYER)
349 if(tmp_entity.team == targetteam)
350 if(tmp_entity != except)
352 Send_Notification(net_type, tmp_entity, net_name, s1, s2, f1, f2, f3);
357 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
358 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
361 FOR_EACH_REALCLIENT(tmp_entity)
363 if((tmp_entity.classname == STR_PLAYER) || spectators)
364 if(tmp_entity != except)
366 Send_Notification(net_type, tmp_entity, net_name, s1, s2, f1, f2, f3);
372 // =============================
373 // LEGACY NOTIFICATION SYSTEMS
374 // =============================
376 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
378 WriteByte(MSG_ALL, SVC_TEMPENTITY);
379 WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
380 WriteString(MSG_ALL, s1);
381 WriteString(MSG_ALL, s2);
382 WriteString(MSG_ALL, s3);
383 WriteShort(MSG_ALL, msg);
384 WriteByte(MSG_ALL, type);
387 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
388 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
390 if (clienttype(e) == CLIENTTYPE_REAL)
393 WRITESPECTATABLE_MSG_ONE({
394 WriteByte(MSG_ONE, SVC_TEMPENTITY);
395 WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
396 WriteString(MSG_ONE, s1);
397 WriteString(MSG_ONE, s2);
398 WriteShort(MSG_ONE, msg);
399 WriteByte(MSG_ONE, type);
404 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
406 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
409 WRITESPECTATABLE_MSG_ONE({
410 WriteByte(MSG_ONE, SVC_TEMPENTITY);
411 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
412 WriteByte(MSG_ONE, id);
413 WriteString(MSG_ONE, s);
414 if (id != 0 && s != "")
416 WriteByte(MSG_ONE, duration);
417 WriteByte(MSG_ONE, countdown_num);
422 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
424 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);