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)
11 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
13 #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
14 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
15 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
18 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
19 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
22 // ====================================
23 // Notifications List and Information
24 // ====================================
26 List of all notifications (including identifiers and display information)
27 Format: name, args, *icon/CPID, *durcnt, normal, gentle
28 Asterisked fields are not present in all notification types.
31 Arguments for sprintf(string, args), if no args needed then use ""
33 MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
34 MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
36 MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
37 Normal message (string for sprintf when gentle messages are NOT enabled)
38 Gentle message (string for sprintf when gentle messages ARE enabled)
40 Messages have ^F1, ^F2, and ^BG in them-- these are replaced
41 with colors according to the cvars the user has chosen.
42 ^F1 = highest priority, "primary"
43 ^F2 = next highest priority, "secondary"
44 ^BG = normal/less important priority, "tertiary"
46 Guidlines (please try and follow these):
47 ALWAYS start the string with a color, preferably background.
48 ALWAYS end messages with a new line.
49 ALWAYS properly use tab spacing to even out the notifications.
50 NEVER re-declare an event twice.
51 NEVER add or remove fields from the format, it SHOULD already work.
52 ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
53 Be clean and simple with your notification naming, nothing too long.
54 Keep the notifications in alphabetical order.
56 #define MSG_INFO_NOTIFICATIONS \
57 MSG_INFO_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"), "") \
60 #define MSG_CENTER_NOTIFICATIONS \
61 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."), "") \
62 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."), "") \
63 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"), "") \
64 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"), "") \
65 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"), "") \
66 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
67 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, s1, NO_CPID, XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
68 #undef MSG_CENTER_NOTIF
70 #define MSG_WEAPON_NOTIFICATIONS \
71 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"), "") \
72 #undef MSG_WEAPON_NOTIF
75 // ====================================
76 // Initialization/Create Declarations
77 // ====================================
80 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
81 float NOTIF_INFO_COUNT;
82 float NOTIF_CENTER_COUNT;
83 float NOTIF_WEAPON_COUNT;
84 float NOTIF_CPID_COUNT;
86 #define ADD_CSQC_AUTOCVAR(name) \
88 var float autocvar_notification_##name = TRUE; \
91 #define MSG_INFO_NOTIF(name,args,icon,normal,gentle) \
92 ADD_CSQC_AUTOCVAR(name) \
94 void RegisterNotification_##name() \
96 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
97 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
99 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
101 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
102 var float autocvar_notification_##name = TRUE; \
105 void RegisterNotification_##name() \
107 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
108 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
109 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
111 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
113 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
114 var float autocvar_notification_##name = TRUE; \
116 void RegisterNotification_##name() \
118 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
119 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
121 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
123 // NOW we actually activate the declarations
124 MSG_INFO_NOTIFICATIONS
125 MSG_CENTER_NOTIFICATIONS
126 MSG_WEAPON_NOTIFICATIONS
129 // ======================
130 // Supporting Functions
131 // ======================
133 // select between the normal or the gentle message string based on client (or server) settings
134 string normal_or_gentle(string normal, string gentle)
137 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
139 if(autocvar_sv_gentle)
141 return ((gentle != "") ? gentle : normal);
146 // get the actual name of a notification and return it as a string
147 string Get_Notif_Name(float net_type, float net_name)
153 #define MSG_INFO_NOTIF(name,args,icon,normal,gentle) \
154 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
155 MSG_INFO_NOTIFICATIONS
160 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
161 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
162 MSG_CENTER_NOTIFICATIONS
167 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
168 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
169 MSG_WEAPON_NOTIFICATIONS
176 // color code replace, place inside of sprintf and parse the string
177 string CCR(string input)
179 input = strreplace("^F1", "^3", input);
180 input = strreplace("^F2", "^2", input);
181 input = strreplace("^K1", "^1", input);
182 input = strreplace("^K2", "^5", input);
183 input = strreplace("^BG", "^7", input);
185 input = strreplace("^N", "^7", input); // "none"-- reset to white
191 // ===============================
192 // Frontend Notification Pushing
193 // ===============================
196 void Local_Notification(float net_type, float net_name, string s1, string s2, string s3)
202 #define MSG_INFO_NOTIF(name,args,icon,normal,gentle) \
203 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
204 MSG_INFO_NOTIFICATIONS
209 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
210 { NOTIF_MATCH(name, net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
211 MSG_CENTER_NOTIFICATIONS
216 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
217 { NOTIF_MATCH(name, net_name) { print("unhandled\n"); } }
218 MSG_WEAPON_NOTIFICATIONS
226 // =========================
227 // Notification Networking
228 // =========================
231 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
233 if(net_type && net_name)
235 print("notification: ", Get_Notif_Name(net_type, net_name), ": ", ftos(net_name), ".\n");
237 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
239 // personal/direct notification sent to ONE person and their spectators
241 WRITESPECTATABLE_MSG_ONE({
242 WriteByte(MSG_ONE, SVC_TEMPENTITY);
243 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
244 WriteShort(MSG_ONE, net_type);
245 WriteCoord(MSG_ONE, net_name);
246 WriteString(MSG_ONE, s1);
247 WriteString(MSG_ONE, s2);
248 WriteString(MSG_ALL, s3);
253 // global notification sent to EVERYONE
254 WriteByte(MSG_ALL, SVC_TEMPENTITY);
255 WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
256 WriteShort(MSG_ALL, net_type);
257 WriteCoord(MSG_ALL, net_name);
258 WriteString(MSG_ALL, s1);
259 WriteString(MSG_ALL, s2);
260 WriteString(MSG_ALL, s3);
263 if(!server_is_local && (net_type == MSG_INFO))
265 #define MSG_INFO_NOTIF(name,args,icon,normal,gentle) \
266 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
267 MSG_INFO_NOTIFICATIONS
270 else { backtrace("Incorrect usage of Send_Notification!\n"); }
273 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
276 FOR_EACH_REALCLIENT(tmp_entity)
278 if(tmp_entity.classname == STR_PLAYER)
279 if(tmp_entity.team == targetteam)
280 if(tmp_entity != except)
282 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
287 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
288 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
291 FOR_EACH_REALCLIENT(tmp_entity)
293 if((tmp_entity.classname == STR_PLAYER) || spectators)
294 if(tmp_entity != except)
296 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
302 // =============================
303 // LEGACY NOTIFICATION SYSTEMS
304 // =============================
306 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
308 WriteByte(MSG_ALL, SVC_TEMPENTITY);
309 WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
310 WriteString(MSG_ALL, s1);
311 WriteString(MSG_ALL, s2);
312 WriteString(MSG_ALL, s3);
313 WriteShort(MSG_ALL, msg);
314 WriteByte(MSG_ALL, type);
317 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
318 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
320 if (clienttype(e) == CLIENTTYPE_REAL)
323 WRITESPECTATABLE_MSG_ONE({
324 WriteByte(MSG_ONE, SVC_TEMPENTITY);
325 WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
326 WriteString(MSG_ONE, s1);
327 WriteString(MSG_ONE, s2);
328 WriteShort(MSG_ONE, msg);
329 WriteByte(MSG_ONE, type);
334 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
336 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
339 WRITESPECTATABLE_MSG_ONE({
340 WriteByte(MSG_ONE, SVC_TEMPENTITY);
341 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
342 WriteByte(MSG_ONE, id);
343 WriteString(MSG_ONE, s);
344 if (id != 0 && s != "")
346 WriteByte(MSG_ONE, duration);
347 WriteByte(MSG_ONE, countdown_num);
352 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
354 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);