]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Use checkargs for both Kill_Notification and Send_Notification
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: February, 2013
4 // ================================================
5
6 string Get_Notif_TypeName(float net_type)
7 {
8         switch(net_type)
9         {
10                 case MSG_INFO: return "MSG_INFO";
11                 case MSG_CENTER: return "MSG_CENTER";
12                 case MSG_WEAPON: return "MSG_WEAPON";
13                 case MSG_DEATH: return "MSG_DEATH";
14         }
15         backtrace(sprintf("Get_Notif_TypeName(%d): Improper net type!\n", net_type));
16         return "your ass";
17 }
18
19 entity Get_Notif_Ent(float net_type, float net_name)
20 {
21         switch(net_type)
22         {
23                 case MSG_INFO: return msg_info_notifs[net_name - 1];
24                 case MSG_CENTER: return msg_center_notifs[net_name - 1];
25                 case MSG_WEAPON: return msg_weapon_notifs[net_name - 1];
26                 case MSG_DEATH: return msg_death_notifs[net_name - 1];
27         }
28         backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
29         return world;
30 }
31
32
33 // ===============================
34 //  Frontend Notification Pushing
35 // ===============================
36
37 void Dump_Notifications(float fh, float alsoprint)
38 {
39         #define NOTIF_WRITE(a) { \
40                 fputs(fh, a); \
41                 if(alsoprint) { print(a); } }
42         #define NOTIF_WRITE_SETA(name,default,text) { \
43                 notif_msg = \
44                         sprintf( \
45                                 "seta notification_%s %d \"notif string: %s^7\"\n", \
46                                 name, default, strreplace("\{3}", "", strreplace("\n", "\\n", text)) \
47                         ); \
48                 NOTIF_WRITE(notif_msg) }
49
50         string notif_msg;
51         float i;
52         entity e;
53
54         // Note: This warning only applies to the notifications.cfg file that is output...
55
56         // You ARE supposed to manually edit this function to add i.e. hard coded
57         // notification variables for mutators or game modes or such and then
58         // regenerate the notifications.cfg file from the new code.
59
60         NOTIF_WRITE("// ********************************************** //\n");
61         NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
62         NOTIF_WRITE("// **                                          ** //\n");
63         NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
64         NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
65         NOTIF_WRITE("// **                                          ** //\n");
66         NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
67         NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
68         NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
69         NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
70         NOTIF_WRITE("// **                                          ** //\n");
71         NOTIF_WRITE("// ********************************************** //\n");
72
73         // These notifications will also append their string as a comment...
74         // This is not necessary, and does not matter if they vary between config versions,
75         // it is just a semi-helpful tool for those who want to manually change their user settings.
76
77         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
78         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
79         {
80                 e = Get_Notif_Ent(MSG_INFO, i);
81                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
82                 NOTIF_WRITE_SETA(e.nent_name, e.nent_default, e.nent_string);
83         }
84
85         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
86         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
87         {
88                 e = Get_Notif_Ent(MSG_CENTER, i);
89                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
90                 NOTIF_WRITE_SETA(e.nent_name, e.nent_default, e.nent_string);
91         }
92
93         NOTIF_WRITE(sprintf("\n// MSG_WEAPON notifications (count = %d):\n", NOTIF_WEAPON_COUNT));
94         for(i = 1; i <= NOTIF_WEAPON_COUNT; ++i)
95         {
96                 e = Get_Notif_Ent(MSG_WEAPON, i);
97                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
98                 NOTIF_WRITE_SETA(e.nent_name, e.nent_default, sprintf("infoname: %s, centername: %s",
99                         e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name));
100         }
101
102         NOTIF_WRITE(sprintf("\n// MSG_DEATH notifications (count = %d):\n", NOTIF_DEATH_COUNT));
103         for(i = 1; i <= NOTIF_DEATH_COUNT; ++i)
104         {
105                 e = Get_Notif_Ent(MSG_DEATH, i);
106                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
107                 NOTIF_WRITE_SETA(e.nent_name, e.nent_default, sprintf("infoname: %s, centername: %s",
108                         e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name));
109         }
110
111         // edit these to match whichever cvars are used for specific notification options
112         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
113         NOTIF_WRITE("seta notification_allow_chatboxprint 1 \"Allow notifications to be printed to chat box by setting notification cvar to 2 (You can also set this cvar to 2 to force ALL notifications to be printed to the chatbox)\"\n");
114         NOTIF_WRITE("seta notification_show_location 0 \"Append location information to MSG_INFO death/kill messages\"\n");
115         NOTIF_WRITE("seta notification_show_location_string \"\" \"Replacement string piped into sprintf, so you can do different messages like this: ' at the %s' or ' (near %s)'\"\n");
116         NOTIF_WRITE("seta notification_show_sprees 1 \"Print information about sprees in death/kill messages\"\n");
117         NOTIF_WRITE("seta notification_show_sprees_center 1 \"Show spree information in MSG_CENTER messages... 0 = off, 1 = target (but only for first victim) and attacker\"\n");
118         NOTIF_WRITE("seta notification_show_sprees_center_specialonly 1 \"Don't show spree information in MSG_CENTER messages if it isn't an achievement\"\n");
119         NOTIF_WRITE("seta notification_show_sprees_info 3 \"Show spree information in MSG_INFO messages... 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker\"\n");
120         NOTIF_WRITE("seta notification_show_sprees_info_newline 0 \"Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself\"\n");
121         NOTIF_WRITE("seta notification_show_sprees_info_specialonly 1 \"Don't show attacker spree information in MSG_INFO messages if it isn't an achievement\"\n");
122         NOTIF_WRITE("seta notification_errors_are_fatal 1 \"If a notification fails upon initialization, cause a Host_Error to stop the program\"\n");
123         NOTIF_WRITE("seta notification_ctf_pickup_team_verbose 1 \"Show extra information if a team mate picks up a flag\"\n");
124         NOTIF_WRITE("seta notification_ctf_pickup_enemy_verbose 1 \"Show extra information if an enemy picks up a flag\"\n");
125         NOTIF_WRITE("seta notification_ctf_capture_verbose 1 \"Show extra information when someone captures a flag\"\n");
126         NOTIF_WRITE("seta notification_frag_verbose 1 \"Show extra information when you frag someone (or when you are fragged\"\n");
127
128         NOTIF_WRITE(sprintf("\n// Notification counts (total = %d): MSG_INFO = %d, MSG_CENTER = %d, MSG_WEAPON = %d, MSG_DEATH = %d\n",
129                 (NOTIF_INFO_COUNT + NOTIF_CENTER_COUNT + NOTIF_WEAPON_COUNT + NOTIF_DEATH_COUNT), 
130                 NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_WEAPON_COUNT, NOTIF_DEATH_COUNT));
131         
132         return;
133         #undef NOTIF_WRITE_SETA
134         #undef NOTIF_WRITE
135 }
136
137 #ifdef SVQC
138 void Notification_GetCvars()
139 {
140         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
141 }
142 #endif
143
144 string Local_Notification_sprintf(string input, string args, 
145         string s1, string s2, string s3, string s4,
146         float f1, float f2, float f3, float f4)
147 {
148         #ifdef NOTIFICATIONS_DEBUG
149         dprint(
150                 sprintf("Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
151                         strreplace("\n", "\\n", input),
152                         args,
153                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
154                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
155                 )
156         );
157         #endif
158         
159         string selected;
160         float sel_num;
161         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
162
163         string tmp_s;
164
165         for(sel_num = 0;(args != "");)
166         {
167                 selected = car(args); args = cdr(args);
168                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
169                 switch(strtolower(selected))
170                 {
171                         #define ARG_CASE(prog,selected,result) \
172                                 #ifdef CSQC \
173                                         #if (prog != ARG_SV) \
174                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
175                                         #endif \
176                                 #else \
177                                         #if (prog != ARG_CS) \
178                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
179                                         #endif \
180                                 #endif
181                         NOTIF_ARGUMENT_LIST
182                         #undef ARG_CASE
183                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
184                 }
185         }
186         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
187 }
188
189 #ifdef CSQC
190 void Local_Notification_HUD_Notify_Push(string icon, string hudargs, string s1, string s2, string s3, string s4)
191 {
192         string selected;
193         float sel_num;
194         arg_slot[0] = ""; arg_slot[1] = "";
195
196         for(sel_num = 0;(hudargs != "");)
197         {
198                 selected = car(hudargs); hudargs = cdr(hudargs);
199                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
200                 switch(strtolower(selected))
201                 {
202                         #define ARG_CASE(prog,selected,result) \
203                                 #if (prog == ARG_CS_SV_HA) \
204                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
205                                 #endif
206                         NOTIF_ARGUMENT_LIST
207                         #undef ARG_CASE
208                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
209                 }
210         }
211         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
212 }
213
214 void Local_Notification_centerprint_generic(string input, string durcnt, float cpid, float f1, float f2)
215 {
216         string selected;
217         float sel_num;
218         arg_slot[0] = ""; arg_slot[1] = "";
219
220         for(sel_num = 0;(durcnt != "");)
221         {
222                 selected = car(durcnt); durcnt = cdr(durcnt);
223                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
224                 switch(strtolower(selected))
225                 {
226                         #define ARG_CASE(prog,selected,result) \
227                                 #if (prog == ARG_CS_SV_DC) \
228                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
229                                 #endif
230                         NOTIF_ARGUMENT_LIST
231                         #undef ARG_CASE
232                         default:
233                         {
234                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
235                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
236                                 break;
237                         }
238                 }
239         }
240         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
241 }
242 #endif
243
244 void Local_Notification(float net_type, float net_name, ...count)
245 {
246         // check supplied type and name for errors
247         string checkargs = "";
248         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
249                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
250                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
251         switch(net_type)
252         {
253                 CHECKARG_TYPENAME(INFO)
254                 CHECKARG_TYPENAME(CENTER)
255                 CHECKARG_TYPENAME(WEAPON)
256                 CHECKARG_TYPENAME(DEATH)
257                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
258         }
259         #undef CHECKARG_TYPENAME
260         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
261
262         entity notif = Get_Notif_Ent(net_type, net_name);
263         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
264         if not(notif.nent_enabled) { print("Local_Notification: Entity was disabled...\n"); return; }
265
266         if((notif.nent_stringcount + notif.nent_floatcount) > count)
267         {
268                 backtrace(sprintf(
269                         strcat(
270                                 "Not enough arguments for Local_Notification(%d, %s, ...)! ",
271                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
272                                 "Check the definition and function call for accuracy...?\n"
273                         ),
274                         net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
275                 return;
276         }
277         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
278         {
279                 backtrace(sprintf(
280                         strcat(
281                                 "Too many arguments for Local_Notification(%d, %s, ...)! ",
282                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
283                                 "Check the definition and function call for accuracy...?\n"
284                         ),
285                         net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
286                 return;
287         }
288
289         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
290         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
291         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
292         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
293         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
294         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
295         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
296         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
297
298         #ifdef NOTIFICATIONS_DEBUG
299         dprint(
300                 sprintf("Local_Notification(%d, %s, %s, %s);\n",
301                         net_type,
302                         notif.nent_name,
303                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
304                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
305                 )
306         );
307         #endif
308         
309         switch(net_type)
310         {
311                 case MSG_INFO:
312                 {
313                         print(
314                                 Local_Notification_sprintf(
315                                         notif.nent_string,
316                                         notif.nent_args, 
317                                         s1, s2, s3, s4,
318                                         f1, f2, f3, f4)
319                         );
320                         #ifdef CSQC 
321                         if(notif.nent_icon != "")
322                         {
323                                 Local_Notification_HUD_Notify_Push(
324                                         notif.nent_icon,
325                                         notif.nent_hudargs,
326                                         s1, s2, s3, s4);
327                         } 
328                         #endif 
329                         break;
330                 }
331                 
332                 #ifdef CSQC
333                 case MSG_CENTER:
334                 {
335                         Local_Notification_centerprint_generic(
336                                 Local_Notification_sprintf(
337                                         notif.nent_string,
338                                         notif.nent_args, 
339                                         s1, s2, s3, s4,
340                                         f1, f2, f3, f4),
341                                 notif.nent_durcnt,
342                                 notif.nent_cpid,
343                                 f1, f2);
344                         break;
345                 }
346                 #endif
347                 
348                 case MSG_WEAPON:
349                 case MSG_DEATH:
350                 {
351                         if(notif.nent_msginfo)
352                         if(notif.nent_msginfo.nent_enabled)
353                         {
354                                 Local_Notification_WOVA(
355                                         MSG_INFO,
356                                         notif.nent_msginfo.nent_id, 
357                                         notif.nent_msginfo.nent_stringcount, 
358                                         notif.nent_msginfo.nent_floatcount, 
359                                         s1, s2, s3, s4,
360                                         f1, f2, f3, f4);
361                         }
362                         #ifdef CSQC
363                         if(notif.nent_msgcenter)
364                         if(notif.nent_msgcenter.nent_enabled)
365                         {
366                                 Local_Notification_WOVA(
367                                         MSG_CENTER,
368                                         notif.nent_msgcenter.nent_id, 
369                                         notif.nent_msgcenter.nent_stringcount, 
370                                         notif.nent_msgcenter.nent_floatcount, 
371                                         s1, s2, s3, s4,
372                                         f1, f2, f3, f4); 
373                         }
374                         #endif
375                         break;
376                 }
377         }
378 }
379
380 // WOVA = Without Variable Arguments 
381 void Local_Notification_WOVA(float net_type, float net_name,
382         float stringcount, float floatcount,
383         string s1, string s2, string s3, string s4,
384         float f1, float f2, float f3, float f4)
385 {
386         #define VARITEM(stringc,floatc,args) \
387                 if((stringcount == stringc) && (floatcount == floatc)) \
388                         { Local_Notification(net_type, net_name, args); return; }
389         EIGHT_VARS_TO_VARARGS_VARLIST
390         #undef VARITEM
391         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
392 }
393
394
395 // =========================
396 //  Notification Networking
397 // =========================
398
399 #ifdef CSQC
400 void Read_Notification(float is_new)
401 {
402         float net_type = ReadByte();
403         float net_name = ReadShort();
404
405         if(net_type == (-1 * MSG_CENTER))
406         {
407                 if(is_new)
408                 {
409                         if(net_name == 0)
410                         {
411                                 print("clearing all centerprints\n");
412                                 reset_centerprint_messages();
413                         }
414                         else
415                         {
416                                 entity notif = Get_Notif_Ent(net_type, net_name);
417                                 if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
418                                 centerprint_generic(notif.nent_cpid, "", 0, 0);
419                         }
420                 }
421                 return;
422         }
423         
424         entity notif = Get_Notif_Ent(net_type, net_name);
425         if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
426
427         string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
428         string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
429         string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
430         string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
431         float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
432         float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
433         float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
434         float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
435
436         #ifdef NOTIFICATIONS_DEBUG
437         dprint(sprintf("Read_Notification(%d) at %f: net_name = %s.\n", is_new, time, notif.nent_name));
438         #endif
439         
440         if(is_new)
441         {
442                 Local_Notification_WOVA(
443                         net_type, net_name,
444                         notif.nent_stringcount,
445                         notif.nent_floatcount,
446                         s1, s2, s3, s4,
447                         f1, f2, f3, f4);
448         }
449 }
450 #endif
451
452 #ifdef SVQC
453 void Net_Notification_Remove()
454 {
455         float i;
456         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
457         remove(self);
458 }
459
460 float Net_Write_Notification(entity client, float sf)
461 {
462         float i, send = FALSE;
463         
464         switch(self.nent_broadcast)
465         {
466                 case NOTIF_ONE: // send to one client and their spectator
467                 {
468                         if(
469                                 (client == self.nent_client)
470                                 ||
471                                 (
472                                         (client.classname == STR_SPECTATOR)
473                                         &&
474                                         (client.enemy == self.nent_client)
475                                 )
476                         ) { send = TRUE; }
477                         break;
478                 }
479                 case NOTIF_ONE_ONLY: // send ONLY to one client
480                 {
481                         if(client == self.nent_client) { send = TRUE; }
482                         break;
483                 }
484                 case NOTIF_TEAM: // send only to X team and their spectators
485                 {
486                         if(
487                                 (client.team == self.nent_client.team)
488                                 ||
489                                 (
490                                         (client.classname == STR_SPECTATOR)
491                                         &&
492                                         (client.enemy.team == self.nent_client.team)
493                                 )
494                         ) { send = TRUE; }
495                         break;
496                 }
497                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
498                 {
499                         if(
500                                 (client != self.nent_client)
501                                 &&
502                                 (
503                                         (client.team == self.nent_client.team)
504                                         ||
505                                         (
506                                                 (client.classname == STR_SPECTATOR)
507                                                 &&
508                                                 (
509                                                         (client.enemy != self.nent_client)
510                                                         &&
511                                                         (client.enemy.team == self.nent_client.team)
512                                                 )
513                                         )
514                                 )
515                         ) { send = TRUE; }
516                         break;
517                 }
518                 case NOTIF_ANY: // send to everyone
519                 {
520                         send = TRUE;
521                         break;
522                 }
523                 case NOTIF_ANY_EXCEPT: // send to everyone except X person and their spectators
524                 {
525                         if(
526                                 (client != self.nent_client)
527                                 &&
528                                 !(
529                                         (client.classname == STR_SPECTATOR)
530                                         &&
531                                         (client.enemy == self.nent_client)
532                                 )
533                         ) { send = TRUE; }
534                         break;
535                 }
536                 default: { send = FALSE; break; }
537         }
538
539         if(send)
540         {               
541                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
542                 WriteByte(MSG_ENTITY, self.nent_net_type);
543                 WriteShort(MSG_ENTITY, self.nent_net_name);
544                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
545                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
546         }
547
548         return send; 
549 }
550
551 string Notification_CheckArgs(float broadcast, entity client, float net_type, float net_name)
552 {
553         // check supplied broadcast, target, type, and name for errors
554         string checkargs = "";
555         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
556                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
557                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
558         switch(net_type)
559         {
560                 CHECKARG_TYPENAME(INFO)
561                 CHECKARG_TYPENAME(CENTER)
562                 CHECKARG_TYPENAME(WEAPON)
563                 CHECKARG_TYPENAME(DEATH)
564                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
565         }
566         #undef CHECKARG_TYPENAME
567         if(checkargs != "") { checkargs = strcat(checkargs, " "); }
568         switch(broadcast)
569         {
570                 case NOTIF_ONE:
571                 case NOTIF_ONE_ONLY:
572                 {
573                         if(IS_NOT_A_CLIENT(client))
574                                 { checkargs = sprintf("%sNo client provided!", checkargs); }
575                         break;
576                 }
577                 
578                 case NOTIF_ANY_EXCEPT:
579                 {
580                         if(IS_NOT_A_CLIENT(client))
581                                 { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
582                         break;
583                 }
584                 
585                 case NOTIF_ANY:
586                 {
587                         if(client)
588                                 { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
589                         break;
590                 }
591                 
592                 case NOTIF_TEAM:
593                 case NOTIF_TEAM_EXCEPT:
594                 {
595                         if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
596                         else if(IS_NOT_A_CLIENT(client))
597                         {
598                                 if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
599                                 else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
600                         }
601                         break;
602                 }
603                 
604                 default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
605         }
606         return checkargs;
607 }
608
609 void Kill_Notification(float broadcast, entity client, float net_type, float net_name)
610 {
611         string checkargs = Notification_CheckArgs(broadcast, client, net_type, 1);
612         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
613
614         entity notif;
615
616         // if this is a centerprint, we must tell the client
617         // to kill the cpid in the centerprint queue
618         if(net_type == MSG_CENTER)
619         {
620                 notif = spawn();
621                 notif.classname = "net_kill_notification";
622                 notif.nent_broadcast = broadcast;
623                 notif.nent_client = client;
624                 notif.nent_net_type = (-1 * MSG_CENTER);
625                 notif.nent_net_name = net_name;
626                 Net_LinkEntity(notif, FALSE, 0.5, Net_Write_Notification);
627         }
628
629         for(notif = world; (notif = find(notif, classname, sprintf("net_%s", strtolower(Get_Notif_TypeName(net_type)))));)
630         {
631                 // now kill the old send notification entity
632                 print(sprintf("killed '%s'\n", notif.classname));
633                 notif.think();
634         }
635 }
636
637 void Send_Notification(float broadcast, entity client,
638         float net_type, float net_name, ...count)
639 {
640         // check supplied broadcast, target, type, and name for errors
641         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
642         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
643
644         // retreive counts for the arguments of this notification
645         entity notif = Get_Notif_Ent(net_type, net_name);
646         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
647
648         if((notif.nent_stringcount + notif.nent_floatcount) > count)
649         {
650                 backtrace(sprintf(
651                         strcat(
652                                 "Not enough arguments for Send_Notification(%d, %d, %s, ...)! ",
653                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
654                                 "Check the definition and function call for accuracy...?\n"
655                         ),
656                         broadcast, net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
657                 return;
658         }
659         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
660         {
661                 backtrace(sprintf(
662                         strcat(
663                                 "Too many arguments for Send_Notification(%d, %d, %s, ...)! ",
664                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
665                                 "Check the definition and function call for accuracy...?\n"
666                         ),
667                         broadcast, net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
668                 return;
669         }
670
671         #ifdef NOTIFICATIONS_DEBUG
672         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
673         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
674         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
675         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
676         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
677         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
678         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
679         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
680         dprint(
681                 sprintf("Send_Notification(%d, %d, %s, %s, %s - %d %d);\n",
682                         broadcast,
683                         net_type,
684                         notif.nent_name,
685                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
686                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
687                         notif.nent_stringcount, notif.nent_floatcount
688                 )
689         );
690         #endif
691
692         entity net_notif = spawn();
693         net_notif.classname = sprintf("net_%s", strtolower(Get_Notif_TypeName(net_type)));
694         net_notif.nent_broadcast = broadcast;
695         net_notif.nent_client = client;
696         net_notif.nent_net_type = net_type;
697         net_notif.nent_net_name = net_name;
698         net_notif.nent_stringcount = notif.nent_stringcount;
699         net_notif.nent_floatcount = notif.nent_floatcount;
700         
701         float i;
702         for(i = 0; i < net_notif.nent_stringcount; ++i) { net_notif.nent_strings[i] = strzone(...(i, string)); }
703         for(i = 0; i < net_notif.nent_floatcount; ++i) { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
704         
705         net_notif.think = Net_Notification_Remove;
706         net_notif.nextthink = (time + 0.5); 
707
708         Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
709
710         if(server_is_dedicated && (broadcast == NOTIF_ANY || broadcast == NOTIF_ANY_EXCEPT) && (net_type != MSG_CENTER))
711         {
712                 Local_Notification_WOVA(
713                         net_type, net_name,
714                         notif.nent_stringcount,
715                         notif.nent_floatcount,
716                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
717                         IFFL(0), IFFL(1), IFFL(2), IFFL(3));
718         }
719 }
720
721 // WOVA = Without Variable Arguments 
722 void Send_Notification_WOVA(float broadcast, entity client,
723         float net_type, float net_name,
724         string s1, string s2, string s3, string s4,
725         float f1, float f2, float f3, float f4)
726 {
727         entity notif = Get_Notif_Ent(net_type, net_name);
728         
729         #ifdef NOTIFICATIONS_DEBUG
730         dprint(
731                 sprintf("Send_Notification_WOVA(%d, %d, %s, %s, %s - %d %d);\n",
732                         broadcast,
733                         net_type,
734                         notif.nent_name,
735                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
736                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
737                         notif.nent_stringcount, notif.nent_floatcount
738                 )
739         );
740         #endif
741         
742         #define VARITEM(stringc,floatc,args) \
743                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
744                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
745         EIGHT_VARS_TO_VARARGS_VARLIST
746         #undef VARITEM
747         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
748 }
749
750
751 // =============================
752 //  LEGACY NOTIFICATION SYSTEMS
753 // =============================
754
755 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
756 {
757         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
758         {
759                 msg_entity = e;
760                 WRITESPECTATABLE_MSG_ONE({
761                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
762                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
763                         WriteByte(MSG_ONE, id);
764                         WriteString(MSG_ONE, s);
765                         if (id != 0 && s != "")
766                         {
767                                 WriteByte(MSG_ONE, duration);
768                                 WriteByte(MSG_ONE, countdown_num);
769                         }
770                 });
771         }
772 }
773 #endif // ifdef SVQC