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