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