]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Rename this, change that, blah blah blah
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: September, 2012
4 // ================================================
5
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)
11
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
16
17 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
18 #ifdef SVQC
19 #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
20 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
21 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
22 #endif
23
24 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
25 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
26 #define VAR_TO_TEXT(var) #var
27
28 #define CHECK_FIELD_AND_COUNT(field,count) if(!field) { field = (NOTIF_FIRST + count); ++count; }
29 #define CHECK_MAX_NOTIFICATIONS(name,count) if(count == NOTIF_MAX) { error(strcat("Maximum notifications hit: ", VAR_TO_TEXT(name), ": ", ftos(count), ".\n"); }
30
31
32 // ====================================
33 //  Notifications List and Information
34 // ====================================
35 /*
36  List of all notifications (including identifiers and display information)
37  Format: name, args, *icon/CPID, *durcnt, normal, gentle
38  Asterisked fields are not present in all notification types.
39  Specifications:
40     Name of notification
41     Arguments for sprintf(string, args), if no args needed then use ""
42     *Icon/CPID:
43       MSG_NOTIFY: STRING: icon string name for the hud notify panel, "" if no icon is used
44       MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
45     *Duration/Countdown:
46       MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
47     Normal message (string for sprintf when gentle messages are NOT enabled)
48     Gentle message (string for sprintf when gentle messages ARE enabled)
49
50  Messages have ^F1, ^F2, and ^BG in them-- these are replaced
51  with colors according to the cvars the user has chosen.
52     ^F1 = highest priority, "primary"
53     ^F2 = next highest priority, "secondary"
54     ^BG = normal/less important priority, "tertiary"
55
56  Guidlines (please try and follow these):
57     ALWAYS start the string with a color, preferably background.
58     ALWAYS end messages with a new line.
59     ALWAYS properly use tab spacing to even out the notifications.
60     NEVER re-declare an event twice.
61     NEVER add or remove fields from the format, it SHOULD already work.
62     ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
63     Be clean and simple with your notification naming, nothing too long.
64     Keep the notifications in alphabetical order.
65 */
66 #define MSG_INFO_NOTIFICATIONS \
67         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"), "") \
68         #undef MSG_INFO_NOTIF
69
70 #define MSG_NOTIFY_NOTIFICATIONS \
71         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"), "") \
72         #undef MSG_NOTIFY_NOTIF
73
74 #define MSG_CENTER_NOTIFICATIONS \
75         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."), "") \
76         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."), "") \
77         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"), "") \
78         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"), "") \
79         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"), "") \
80         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN,                               s1,                                     CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
81         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE,                              s1,                                     NO_CPID,                                XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
82         #undef MSG_CENTER_NOTIF
83
84 #define MSG_WEAPON_NOTIFICATIONS \
85         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"), "") \
86         #undef MSG_WEAPON_NOTIF
87
88
89 // ====================================
90 //  Initialization/Create Declarations
91 // ====================================
92
93 #define NOTIF_FIRST 1
94 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
95 float NOTIF_INFO_COUNT;
96 float NOTIF_NOTIFY_COUNT;
97 float NOTIF_CENTER_COUNT;
98 float NOTIF_WEAPON_COUNT;
99 float NOTIF_CPID_COUNT;
100         
101 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
102         float name; \
103         void DecNotif_##name() \
104         { \
105                 CHECK_FIELD_AND_COUNT(name, NOTIF_INFO_COUNT) \
106                 CHECK_MAX_NOTIFICATIONS(name, NOTIF_INFO_COUNT) \
107         } \
108         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
109
110 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
111         float name; \
112         void DecNotif_##name() \
113         { \
114                 CHECK_FIELD_AND_COUNT(name, NOTIF_NOTIFY_COUNT) \
115                 CHECK_MAX_NOTIFICATIONS(name, NOTIF_NOTIFY_COUNT) \
116         } \
117         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
118
119 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
120         float name; \
121         float cpid; \
122         void DecNotif_##name() \
123         { \
124                 CHECK_FIELD_AND_COUNT(name, NOTIF_CENTER_COUNT) \
125                 CHECK_FIELD_AND_COUNT(cpid, NOTIF_CPID_COUNT) \
126                 CHECK_MAX_NOTIFICATIONS(name, NOTIF_CENTER_COUNT) \
127         } \
128         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
129
130 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
131         float name; \
132         void DecNotif_##name() \
133         { \
134                 CHECK_FIELD_AND_COUNT(name, NOTIF_WEAPON_COUNT) \
135                 CHECK_MAX_NOTIFICATIONS(name, NOTIF_WEAPON_COUNT) \
136         } \
137         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
138
139 // NOW we actually activate the declarations
140 MSG_INFO_NOTIFICATIONS
141 MSG_NOTIFY_NOTIFICATIONS
142 MSG_CENTER_NOTIFICATIONS
143 MSG_WEAPON_NOTIFICATIONS
144
145
146 // ======================
147 //  Supporting Functions
148 // ======================
149
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)
152 {
153         #ifdef CSQC
154         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
155         #else
156         if(autocvar_sv_gentle)
157         #endif
158                 return ((gentle != "") ? gentle : normal);
159         else
160                 return normal;
161 }
162
163 // get the actual name of a notification and return it as a string
164 string Get_Notif_Name(float net_type, float net_name)
165 {
166         switch(net_type)
167         {
168                 case MSG_INFO:
169                 {
170                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
171                                 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
172                         MSG_INFO_NOTIFICATIONS
173                         break;
174                 }
175                 case MSG_NOTIFY:
176                 {
177                         #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
178                                 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
179                         MSG_NOTIFY_NOTIFICATIONS
180                         break;
181                 }
182                 case MSG_CENTER:
183                 {
184                         #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
185                                 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
186                         MSG_CENTER_NOTIFICATIONS
187                         break;
188                 }
189                 case MSG_WEAPON:
190                 {
191                         #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
192                                 { NOTIF_MATCH(name,net_name) { return VAR_TO_TEXT(name); } }
193                         MSG_WEAPON_NOTIFICATIONS
194                         break;
195                 }
196         }
197
198         return "";
199 }
200
201 // color code replace, place inside of sprintf and parse the string
202 string CCR(string input)
203 {
204         input = strreplace("^F1", "^3", input);
205         input = strreplace("^F2", "^2", input);
206         input = strreplace("^BG", "^7", input);
207
208         input = strreplace("^N", "^7", input); // "none"-- reset to white
209
210         return input;
211 }
212
213
214 // ===============================
215 //  Frontend Notification Pushing
216 // ===============================
217
218 #ifdef CSQC
219 void Local_Notification(float net_type, float net_name, string s1, string s2, string s3)
220 {
221         switch(net_type)
222         {
223                 case MSG_INFO:
224                 {
225                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
226                                 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
227
228                         MSG_INFO_NOTIFICATIONS
229                         break;
230                 }
231
232                 case MSG_NOTIFY:
233                 {
234                         break;
235                 }
236
237                 case MSG_CENTER:
238                 {
239                         #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
240                                 { NOTIF_MATCH(name, net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
241
242                         MSG_CENTER_NOTIFICATIONS
243                         break;
244                 }
245
246                 case MSG_WEAPON:
247                 {
248                         break;
249                 }
250         }
251 }
252 #endif
253
254
255 // =========================
256 //  Notification Networking
257 // =========================
258
259 #ifdef SVQC
260 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
261 {
262         print("notification: ", Get_Notif_Name(net_type, net_name), ": ", ftos(net_name), ".\n");
263         if(net_type && net_name)
264         {
265                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
266                 {
267                         msg_entity = client;
268                         WRITESPECTATABLE_MSG_ONE({
269                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
270                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
271                                 WriteShort(MSG_ONE, net_type);
272                                 WriteCoord(MSG_ONE, net_name);
273                                 WriteString(MSG_ONE, s1);
274                                 WriteString(MSG_ONE, s2);
275                                 WriteString(MSG_ONE, s3);
276                         });
277                 }
278
279                 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
280                 {
281                         switch(net_type)
282                         {
283                                 case MSG_INFO:
284                                 {
285                                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
286                                                 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
287
288                                         MSG_INFO_NOTIFICATIONS
289                                         break;
290                                 }
291
292                                 case MSG_NOTIFY:
293                                 {
294                                         break;
295                                 }
296                         }
297                 }
298         }
299         else { backtrace("Incorrect usage of Send_Notification!\n"); }
300 }
301
302 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
303 {
304         entity tmp_entity;
305         FOR_EACH_REALCLIENT(tmp_entity)
306         {
307                 if(tmp_entity.classname == STR_PLAYER)
308                 if(tmp_entity.team == targetteam)
309                 if(tmp_entity != except)
310                 {
311                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
312                 }
313         }
314 }
315
316 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
317 {
318         entity tmp_entity;
319         FOR_EACH_REALCLIENT(tmp_entity)
320         {
321                 if((tmp_entity.classname == STR_PLAYER) || spectators)
322                 if(tmp_entity != except)
323                 {
324                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
325                 }
326         }
327 }
328
329 // LEGACY NOTIFICATION SYSTEMS
330 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
331 {
332         WriteByte(MSG_ALL, SVC_TEMPENTITY);
333         WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
334         WriteString(MSG_ALL, s1);
335         WriteString(MSG_ALL, s2);
336         WriteString(MSG_ALL, s3);
337         WriteShort(MSG_ALL, msg);
338         WriteByte(MSG_ALL, type);
339 }
340
341 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
342 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
343 {
344         if (clienttype(e) == CLIENTTYPE_REAL)
345         {
346                 msg_entity = e;
347                 WRITESPECTATABLE_MSG_ONE({
348                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
349                         WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
350                         WriteString(MSG_ONE, s1);
351                         WriteString(MSG_ONE, s2);
352                         WriteShort(MSG_ONE, msg);
353                         WriteByte(MSG_ONE, type);
354                 });
355         }
356 }
357
358 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
359 {
360         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
361         {
362                 msg_entity = e;
363                 WRITESPECTATABLE_MSG_ONE({
364                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
365                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
366                         WriteByte(MSG_ONE, id);
367                         WriteString(MSG_ONE, s);
368                         if (id != 0 && s != "")
369                         {
370                                 WriteByte(MSG_ONE, duration);
371                                 WriteByte(MSG_ONE, countdown_num);
372                         }
373                 });
374         }
375 }
376 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
377 {
378         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
379 }
380 #endif