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 // accumulate functions for declarations
19 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
20 float NOTIF_INFO_COUNT;
21 float NOTIF_NOTIFY_COUNT;
22 float NOTIF_CENTER_COUNT;
23 float NOTIF_WEAPON_COUNT;
24 float NOTIF_CPID_COUNT;
26 #define CHECK_MAX_NOTIFICATIONS(count) if(count == NOTIF_MAX) { error("Maximum notifications hit!\n"); }
28 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
30 void DecNotif_##name() { \
32 name = (NOTIF_FIRST + NOTIF_INFO_COUNT); \
33 ++NOTIF_INFO_COUNT; } \
34 CHECK_MAX_NOTIFICATIONS(NOTIF_INFO_COUNT) } \
35 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
37 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
39 void DecNotif_##name() { \
41 name = (NOTIF_FIRST + NOTIF_NOTIFY_COUNT); \
42 ++NOTIF_NOTIFY_COUNT; } \
43 CHECK_MAX_NOTIFICATIONS(NOTIF_NOTIFY_COUNT) } \
44 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
46 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
49 void DecNotif_##name() { \
51 name = (NOTIF_FIRST + NOTIF_CENTER_COUNT); \
52 ++NOTIF_CENTER_COUNT; } \
54 cpid = (NOTIF_FIRST + NOTIF_CPID_COUNT); \
55 ++NOTIF_CPID_COUNT; } \
56 CHECK_MAX_NOTIFICATIONS(NOTIF_CENTER_COUNT) } \
57 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
59 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
61 void DecNotif_##name() { \
63 name = (NOTIF_FIRST + NOTIF_WEAPON_COUNT); \
64 ++NOTIF_WEAPON_COUNT; } \
65 CHECK_MAX_NOTIFICATIONS(NOTIF_WEAPON_COUNT) } \
66 ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
69 // ====================================
70 // Notifications List and Information
71 // ====================================
73 List of all notifications (including identifiers and display information)
74 Format: name, number, args, icon/CPID, normal, gentle
77 ID number of notification
78 Arguments for sprintf(string, args), if no args needed then use ""
80 MSG_NOTIFY: STRING: icon string name for the hud notify panel, "" if no icon is used
81 MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
82 Normal message (string for sprintf when gentle messages are NOT enabled)
83 Gentle message (string for sprintf when gentle messages ARE enabled)
85 Messages have ^F1, ^F2, and ^BG in them-- these are replaced
86 with colors according to the cvars the user has chosen.
87 ^F1 = highest priority, "primary"
88 ^F2 = next highest priority, "secondary"
89 ^BG = normal/less important priority, "tertiary"
92 ALWAYS start the string with a color, preferably background
93 ALWAYS end messages with a new line
94 NEVER re-declare an event twice
95 ARIRE unir frk jvgu lbhe bja zbgure (gvc sbe zvxrrhfn)
97 #define MSG_INFO_NOTIFICATIONS \
98 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"), "") \
101 #define MSG_NOTIFY_NOTIFICATIONS \
102 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"), "") \
103 #undef MSG_NOTIFY_NOTIF
105 #define MSG_CENTER_NOTIFICATIONS \
106 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."), "") \
107 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."), "") \
108 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"), "") \
109 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"), "") \
110 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"), "") \
111 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
112 MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, s1, NO_CPID, XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
113 #undef MSG_CENTER_NOTIF
115 #define MSG_WEAPON_NOTIFICATIONS \
116 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"), "") \
117 #undef MSG_WEAPON_NOTIF
119 // NOW we actually activate the declarations
120 MSG_INFO_NOTIFICATIONS
121 MSG_NOTIFY_NOTIFICATIONS
122 MSG_CENTER_NOTIFICATIONS
123 MSG_WEAPON_NOTIFICATIONS
126 // ======================
127 // Supporting Functions
128 // ======================
131 #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
132 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
133 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
136 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
138 string normal_or_gentle(string normal, string gentle)
141 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
143 if(autocvar_sv_gentle)
145 return ((gentle != "") ? gentle : normal);
150 string CCR(string input) // color code replace, place inside of sprintf and parse the string
152 input = strreplace("^F1", "^3", input);
153 input = strreplace("^F2", "^2", input);
154 input = strreplace("^BG", "^7", input);
156 input = strreplace("^N", "^7", input); // "none"-- reset to white
162 // ===============================
163 // Frontend Notification Pushing
164 // ===============================
167 // =========================
168 // Notification Networking
169 // =========================
172 void Read_Notification()
174 float net_type = ReadByte();
175 float net_name = ReadCoord(); // byte only has 256 selections, we need more than that
176 string s1 = ReadString();
177 string s2 = ReadString();
178 string s3 = ReadString();
184 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
185 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
187 MSG_INFO_NOTIFICATIONS
198 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
199 { if(min(NOTIF_MAX, name) == net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
201 MSG_CENTER_NOTIFICATIONS
213 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
215 if(net_type && net_name)
217 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
220 WRITESPECTATABLE_MSG_ONE({
221 WriteByte(MSG_ONE, SVC_TEMPENTITY);
222 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
223 WriteByte(MSG_ONE, net_type);
224 WriteCoord(MSG_ONE, net_name);
225 WriteString(MSG_ONE, s1);
226 WriteString(MSG_ONE, s2);
227 WriteString(MSG_ONE, s3);
231 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
237 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
238 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
240 MSG_INFO_NOTIFICATIONS
251 else { backtrace("Incorrect usage of Send_Notification!\n"); }
254 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
257 FOR_EACH_REALCLIENT(tmp_entity)
259 if(tmp_entity.classname == STR_PLAYER)
260 if(tmp_entity.team == targetteam)
261 if(tmp_entity != except)
263 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
268 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
271 FOR_EACH_REALCLIENT(tmp_entity)
273 if((tmp_entity.classname == STR_PLAYER) || spectators)
274 if(tmp_entity != except)
276 Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
281 // LEGACY NOTIFICATION SYSTEMS
282 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
284 if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
287 WRITESPECTATABLE_MSG_ONE({
288 WriteByte(MSG_ONE, SVC_TEMPENTITY);
289 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
290 WriteByte(MSG_ONE, id);
291 WriteString(MSG_ONE, s);
292 if (id != 0 && s != "")
294 WriteByte(MSG_ONE, duration);
295 WriteByte(MSG_ONE, countdown_num);
300 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
302 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);