]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/notifications.qc
Merge branch 'master' into divVerent/4team_ctf
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
index 0f51f3524af426dfff9d494d2609a088cacab437..09868a4c2df465b2270294e4856d78c8fe945c93 100644 (file)
 // ================================================
 //  Unified notification system, written by Samual
 // ================================================
 //  Unified notification system, written by Samual
-//  Last updated: February, 2013
+//  Last updated: June, 2013
 // ================================================
 
 // ================================================
 
-#ifndef MENUQC
-string arg_slot[7];
+string Get_Notif_TypeName(float net_type)
+{
+       switch(net_type)
+       {
+               case MSG_ANNCE: return "MSG_ANNCE";
+               case MSG_INFO: return "MSG_INFO";
+               case MSG_CENTER: return "MSG_CENTER";
+               case MSG_CENTER_CPID: return "MSG_CENTER_CPID";
+               case MSG_MULTI: return "MSG_MULTI";
+       }
+       backtrace(sprintf("Get_Notif_TypeName(%d): Improper net type!\n", net_type));
+       return "";
+}
+
 entity Get_Notif_Ent(float net_type, float net_name)
 {
        switch(net_type)
        {
 entity Get_Notif_Ent(float net_type, float net_name)
 {
        switch(net_type)
        {
+               case MSG_ANNCE: return msg_annce_notifs[net_name - 1];
                case MSG_INFO: return msg_info_notifs[net_name - 1];
                case MSG_CENTER: return msg_center_notifs[net_name - 1];
                case MSG_INFO: return msg_info_notifs[net_name - 1];
                case MSG_CENTER: return msg_center_notifs[net_name - 1];
-               case MSG_WEAPON: return msg_weapon_notifs[net_name - 1];
-               case MSG_DEATH: return msg_death_notifs[net_name - 1];
+               case MSG_MULTI: return msg_multi_notifs[net_name - 1];
        }
        backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
        return world;
 }
 
        }
        backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
        return world;
 }
 
-string Get_Notif_Name(float net_type, float net_name)
+string Notification_CheckArgs_TypeName(float net_type, float net_name)
 {
 {
-       entity e = Get_Notif_Ent(net_type, net_name);
-       if(e) { return e.nent_name; }
-       backtrace(sprintf("Get_Notif_Name(%d, %d): Could not find entity!\n", net_type, net_name));
-       return "";
+       // check supplied type and name for errors
+       string checkargs = "";
+       #define CHECKARG_TYPENAME(type) case MSG_##type##: \
+               { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
+               { checkargs = sprintf("Improper name: %d!", net_name); } break; }
+       switch(net_type)
+       {
+               CHECKARG_TYPENAME(ANNCE)
+               CHECKARG_TYPENAME(INFO)
+               CHECKARG_TYPENAME(CENTER)
+               CHECKARG_TYPENAME(MULTI)
+               default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
+       }
+       #undef CHECKARG_TYPENAME
+       return checkargs;
 }
 
 }
 
-float Get_Notif_Infval(float net_type, float net_name)
+#ifdef SVQC
+string Notification_CheckArgs(
+       float broadcast, entity client,
+       float net_type, float net_name)
 {
 {
-       entity e = Get_Notif_Ent(net_type, net_name);
-       if(e) { return e.nent_infoname; }
-       backtrace(sprintf("Get_Notif_Infval(%d, %d): Could not find entity!\n", net_type, net_name));
-       return NO_MSG;
+       // check supplied broadcast, target, type, and name for errors
+       string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
+       if(checkargs != "") { checkargs = strcat(checkargs, " "); }
+       switch(broadcast)
+       {
+               case NOTIF_ONE:
+               case NOTIF_ONE_ONLY:
+               {
+                       if(IS_NOT_A_CLIENT(client))
+                               { checkargs = sprintf("%sNo client provided!", checkargs); }
+                       break;
+               }
+               
+               case NOTIF_ALL_EXCEPT:
+               {
+                       if(IS_NOT_A_CLIENT(client))
+                               { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
+                       break;
+               }
+               
+               case NOTIF_ALL:
+               {
+                       if(client)
+                               { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
+                       break;
+               }
+               
+               case NOTIF_TEAM:
+               case NOTIF_TEAM_EXCEPT:
+               {
+                       if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
+                       else if(IS_NOT_A_CLIENT(client))
+                       {
+                               if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
+                               else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
+                       }
+                       break;
+               }
+               
+               default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
+       }
+       return checkargs;
 }
 }
+#endif
+
+// ===============================
+//  Initialization Core Functions
+// ===============================
 
 
-float Get_Notif_Cenval(float net_type, float net_name)
+// used by restartnotifs command to initialize notifications
+void Destroy_Notification_Entity(entity notif)
 {
 {
-       entity e = Get_Notif_Ent(net_type, net_name);
-       if(e) { return e.nent_centername; }
-       backtrace(sprintf("Get_Notif_Cenval(%d, %d): Could not find entity!\n", net_type, net_name));
-       return NO_MSG;
+       if(notif.nent_name != "") { strunzone(notif.nent_name); }
+       if(notif.nent_snd != "") { strunzone(notif.nent_snd); }
+       if(notif.nent_args != "") { strunzone(notif.nent_args); }
+       if(notif.nent_hudargs != "") { strunzone(notif.nent_hudargs); }
+       if(notif.nent_icon != "") { strunzone(notif.nent_icon); }
+       if(notif.nent_durcnt != "") { strunzone(notif.nent_durcnt); }
+       if(notif.nent_string != "") { strunzone(notif.nent_string); }
+       remove(notif);
 }
 
 }
 
-float Get_Notif_Strnum(float net_type, float net_name)
+void Destroy_All_Notifications(void)
 {
 {
-       entity e = Get_Notif_Ent(net_type, net_name);
-       if(e) { return e.nent_stringcount; }
-       backtrace(sprintf("Get_Notif_Strnum(%d, %d): Could not find entity!\n", net_type, net_name));
-       return NO_MSG;
+       entity notif;
+       float i;
+       
+       #define DESTROY_LOOP(type,count) \
+               for(i = 1; i <= count; ++i) \
+               { \
+                       notif = Get_Notif_Ent(type, i); \
+                       if not(notif) { backtrace("Destroy_All_Notifications(): Missing notification entity!\n"); return; } \
+                       Destroy_Notification_Entity(notif); \
+               }
+
+       // kill all networked notifications and centerprints
+       #ifdef SVQC
+       Kill_Notification(NOTIF_ALL, world, 0, 0);
+       #else
+       reset_centerprint_messages();
+       #endif
+
+       // kill all real notification entities
+       DESTROY_LOOP(MSG_ANNCE, NOTIF_ANNCE_COUNT)
+       DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
+       DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
+       DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
+       #undef DESTROY_LOOP
 }
 
 }
 
-float Get_Notif_Flnum(float net_type, float net_name)
+string Process_Notif_Line(
+       float msg_is_info,
+       float chat,
+       string input,
+       string notiftype,
+       string notifname,
+       string stringtype)
 {
 {
-       entity e = Get_Notif_Ent(net_type, net_name);
-       if(e) { return e.nent_floatcount; }
-       backtrace(sprintf("Get_Notif_Flnum(%d, %d): Could not find entity!\n", net_type, net_name));
-       return NO_MSG;
+       if(msg_is_info)
+       {
+               #ifdef CSQC
+               if((chat && autocvar_notification_allow_chatboxprint)
+                       || (autocvar_notification_allow_chatboxprint == 2))
+               {
+                       // pass 1: add ETX char at beginning of line
+                       input = strcat("\{3}", input);
+
+                       // pass 2: add ETX char at end of each new line (so that
+                       // messages with multiple lines are put through chatbox too)
+                       input = strreplace("\n", "\n\{3}", input);
+
+                       // pass 3: strip trailing ETX char
+                       if(substring(input, (strlen(input) - 1), 1) == "\{3}")
+                               { input = substring(input, 0, (strlen(input) - 1)); }
+               }
+               #endif
+               if(substring(input, (strlen(input) - 1), 1) != "\n")
+               {
+                       print(sprintf(
+                               strcat(
+                                       "^1MISSING/BROKEN NEW LINE AT END OF NOTIFICATION: ",
+                                       "^7net_type = %s, net_name = %s, string = %s.\n"
+                               ),
+                               notiftype,
+                               notifname,
+                               stringtype
+                       ));
+                       notif_error = TRUE;
+                       return strcat(input, "\n");
+               }
+       }
+       return input;
 }
 }
-#endif // ifndef MENUQC
 
 
+string Process_Notif_Args(
+       float arg_type,
+       string args,
+       string notiftype,
+       string notifname)
+{
+       string selected, remaining = args;
+       float sel_num = 0;
 
 
-// ===============================
-//  Frontend Notification Pushing
-// ===============================
+       for(;(remaining != "");)
+       {
+               selected = car(remaining); remaining = cdr(remaining);
 
 
-void Dump_Notifications(float fh, float alsoprint)
+               switch(arg_type)
+               {
+                       case 1: // normal args
+                       {
+                               if(sel_num == NOTIF_MAX_ARGS)
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
+                                                       "^7net_type = %s, net_name = %s, max args = %d.\n"
+                                               ),
+                                               notiftype,
+                                               notifname,
+                                               NOTIF_MAX_ARGS
+                                       ));
+                                       notif_error = TRUE;
+                                       break;
+                               }
+
+                               switch(strtolower(selected))
+                               {
+                                       #define ARG_CASE(prog,selected,result) \
+                                               #if (prog != ARG_DC) \
+                                                       case selected: { ++sel_num; break; } \
+                                               #endif
+                                       NOTIF_ARGUMENT_LIST
+                                       #undef ARG_CASE
+                                       default:
+                                       {
+                                               print(sprintf(
+                                                       strcat(
+                                                               "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
+                                                               "^7net_type = %s, net_name = %s, args arg = '%s'.\n"
+                                                       ),
+                                                       notiftype,
+                                                       notifname,
+                                                       selected
+                                               ));
+                                               notif_error = TRUE;
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+                       case 2: // hudargs
+                       {
+                               if(sel_num == NOTIF_MAX_HUDARGS)
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
+                                                       "^7net_type = %s, net_name = %s, max hudargs = %d.\n"
+                                               ),
+                                               notiftype,
+                                               notifname,
+                                               NOTIF_MAX_HUDARGS
+                                       ));
+                                       notif_error = TRUE;
+                                       break;
+                               }
+
+                               switch(strtolower(selected))
+                               {
+                                       #define ARG_CASE(prog,selected,result) \
+                                               #if (prog == ARG_CS_SV_HA) \
+                                                       case selected: { ++sel_num; break; } \
+                                               #endif
+                                       NOTIF_ARGUMENT_LIST
+                                       #undef ARG_CASE
+                                       default:
+                                       {
+                                               print(sprintf(
+                                                       strcat(
+                                                               "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
+                                                               "^7net_type = %s, net_name = %s, hudargs arg = '%s'.\n"
+                                                       ),
+                                                       notiftype,
+                                                       notifname,
+                                                       selected
+                                               ));
+                                               notif_error = TRUE;
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+                       case 3: // durcnt 
+                       {
+                               if(sel_num == NOTIF_MAX_DURCNT)
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
+                                                       "^7net_type = %s, net_name = %s, max durcnt = %d.\n"
+                                               ),
+                                               notiftype,
+                                               notifname,
+                                               NOTIF_MAX_DURCNT
+                                       ));
+                                       notif_error = TRUE;
+                                       break;
+                               }
+
+                               switch(strtolower(selected))
+                               {
+                                       #define ARG_CASE(prog,selected,result) \
+                                               #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
+                                                       case selected: { ++sel_num; break; } \
+                                               #endif
+                                       NOTIF_ARGUMENT_LIST
+                                       #undef ARG_CASE
+                                       default:
+                                       {
+                                               if(ftos(stof(selected)) != "") { ++sel_num; }
+                                               else
+                                               {
+                                                       print(sprintf(
+                                                               strcat(
+                                                                       "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
+                                                                       "^7net_type = %s, net_name = %s, durcnt arg = '%s'.\n"
+                                                               ),
+                                                               notiftype,
+                                                               notifname,
+                                                               selected
+                                                       ));
+                                                       notif_error = TRUE;
+                                               }
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+               }
+       }
+       return args;
+}
+
+void Create_Notification_Entity(
+       float var_default,
+       float var_cvar,
+       float typeid,
+       float nameid,
+       string namestring,
+       float anncename,
+       float infoname,
+       float centername,
+       float channel,
+       string snd,
+       float vol,
+       float position,
+       float strnum,
+       float flnum,
+       string args,
+       string hudargs,
+       string icon,
+       float cpid,
+       string durcnt,
+       string normal,
+       string gentle,
+       float msg_is_info,
+       float msg_is_multi)
 {
 {
-       float MSG_INFO_NOTIFS = 0, MSG_CENTER_NOTIFS = 0, MSG_WEAPON_NOTIFS = 0, MSG_DEATH_NOTIFS = 0;
+       // =====================
+       //  Global Entity Setup
+       // =====================
+       entity notif = spawn();
+       string typestring = "";
+       switch(typeid)
+       {
+               case MSG_ANNCE:
+               {
+                       typestring = "MSG_ANNCE";
+                       msg_annce_notifs[nameid - 1] = notif;
+                       notif.classname = "msg_annce_notification";
+                       break;
+               }
+               case MSG_INFO:
+               {
+                       typestring = "MSG_INFO";
+                       msg_info_notifs[nameid - 1] = notif;
+                       notif.classname = "msg_info_notification";
+                       break;
+               }
+               case MSG_CENTER:
+               {
+                       typestring = "MSG_CENTER";
+                       msg_center_notifs[nameid - 1] = notif;
+                       notif.classname = "msg_center_notification";
+                       break;
+               }
+               case MSG_MULTI:
+               {
+                       typestring = "MSG_MULTI";
+                       msg_multi_notifs[nameid - 1] = notif;
+                       notif.classname = "MSG_MULTI_notification";
+                       break;
+               }
 
 
-       #define NOTIF_WRITE(type,name,text) { \
-               ++##type##_NOTIFS; \
-               notif_msg = sprintf("seta %s 1 // %s - %s\n", name, #type, strreplace("\n", "\\n", text)); \
-               fputs(fh, notif_msg); \
-               if(alsoprint) { print(strreplace("^", "^^", notif_msg)); } }
+               default:
+               {
+                       error(sprintf(
+                               strcat(
+                                       "^1NOTIFICATION WITH IMPROPER TYPE: ",
+                                       "^7net_type = %d, net_name = %s.\n"
+                               ),
+                               typeid,
+                               namestring
+                       ));
+                       return; // It's not possible to recover from this one
+               }
+       }
+       notif.nent_default = var_default;
+       notif.nent_name = strzone(namestring);
+       notif.nent_id = nameid;
+       notif.nent_enabled = (1 <= var_cvar);
+
+       // Other pre-notif-setup requisites
+       notif_error = FALSE;
+
+       // ====================
+       //  Notification Setup
+       // ====================
+       if(msg_is_multi)
+       {
+               // Set MSG_MULTI string/float counts
+               if((anncename == NO_MSG) && (infoname == NO_MSG) && (centername == NO_MSG))
+               {
+                       print(sprintf(
+                               strcat(
+                                       "^1NOTIFICATION WITH NO SUBCALLS: ",
+                                       "^7net_type = %s, net_name = %s.\n"
+                               ),
+                               typestring,
+                               namestring
+                       ));
+                       notif_error = TRUE;
+               }
+               else
+               {
+                       // announcements don't actually need any arguments, so lets not even count them.
+                       if(anncename != NO_MSG) { notif.nent_msgannce = msg_annce_notifs[anncename - 1]; }
+                       
+                       float infoname_stringcount = 0, infoname_floatcount = 0;
+                       float centername_stringcount = 0, centername_floatcount = 0;
+                       
+                       if(infoname != NO_MSG)
+                       {
+                               notif.nent_msginfo = msg_info_notifs[infoname - 1];
+                               infoname_stringcount = notif.nent_msginfo.nent_stringcount;
+                               infoname_floatcount = notif.nent_msginfo.nent_floatcount;
+                       }
+                       
+                       if(centername != NO_MSG)
+                       {
+                               notif.nent_msgcenter = msg_center_notifs[centername - 1];
+                               centername_stringcount = notif.nent_msgcenter.nent_stringcount;
+                               centername_floatcount = notif.nent_msgcenter.nent_floatcount;
+                       }
+                       
+                       // set the requirements of THIS notification to the totals of its subcalls
+                       notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
+                       notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
+               }
+       }
+       else if(typeid == MSG_ANNCE)
+       {
+               // Set MSG_ANNCE information and handle precaching
+               #ifdef CSQC
+               if not(GENTLE && (var_cvar == 1))
+               {
+                       if(snd != "")
+                       {
+                               if(notif.nent_enabled)
+                               {
+                                       precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
+                                       notif.nent_channel = channel;
+                                       notif.nent_snd = strzone(snd);
+                                       notif.nent_vol = vol;
+                                       notif.nent_position = position;
+                               }
+                       }
+                       else
+                       {
+                               print(sprintf(
+                                       strcat(
+                                               "^1NOTIFICATION WITH NO SOUND: ",
+                                               "^7net_type = %s, net_name = %s.\n"
+                                       ),
+                                       typestring,
+                                       namestring
+                               ));
+                               notif_error = TRUE;
+                       }
+               }
+               else { notif.nent_enabled = FALSE; }
+               #else
+               notif.nent_enabled = FALSE;
+               #endif
+       }
+       else
+       {
+               // Set MSG_INFO and MSG_CENTER string/float counts
+               notif.nent_stringcount = strnum;
+               notif.nent_floatcount = flnum;
+
+               // Only initialize arguments if we're either a client or on a dedicated server
+               #ifdef SVQC
+               float should_process_args = server_is_dedicated;
+               #else
+               float should_process_args = TRUE;
+               #endif
+
+               if(should_process_args)
+               {
+                       // ========================
+                       //  Process Main Arguments
+                       // ========================
+                       if(strnum + flnum)
+                       {
+                               if(args != "")
+                               {
+                                       notif.nent_args = strzone(
+                                               Process_Notif_Args(1, args, typestring, namestring));
+                               }
+                               else if((hudargs == "") && (durcnt ==""))
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: ",
+                                                       "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d\n"
+                                               ),
+                                               typestring,
+                                               namestring,
+                                               strnum,
+                                               flnum
+                                       ));
+                                       notif_error = TRUE;
+                               }
+                       }
+                       else if(args != "")
+                       {
+                               notif.nent_args = strzone(
+                                       Process_Notif_Args(1, args, typestring, namestring));
+                       }
+
+
+                       // =======================================
+                       //  Process HUD and Centerprint Arguments
+                       //    Only processed on CSQC, as these
+                       //    args are only for HUD features.
+                       // =======================================
+                       #ifdef CSQC
+                       if(hudargs != "")
+                       {
+                               notif.nent_hudargs = strzone(
+                                       Process_Notif_Args(2, hudargs, typestring, namestring));
+                                       
+                               if(icon != "") { notif.nent_icon = strzone(icon); }
+                               else
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS HUDARGS BUT NO ICON: ",
+                                                       "^7net_type = %s, net_name = %s.\n"
+                                               ),
+                                               typestring,
+                                               namestring
+                                       ));
+                                       notif_error = TRUE;
+                               }
+                       }
+                       else if(icon != "")
+                       {
+                               print(sprintf(
+                                       strcat(
+                                               "^1NOTIFICATION HAS ICON BUT NO HUDARGS: ",
+                                               "^7net_type = %s, net_name = %s.\n"
+                                       ),
+                                       typestring,
+                                       namestring
+                               ));
+                               notif_error = TRUE;
+                       }
+
+                       if(durcnt != "")
+                       {
+                               notif.nent_durcnt = strzone(
+                                       Process_Notif_Args(3, durcnt, typestring, namestring));
+                                       
+                               if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
+                               else
+                               {
+                                       print(sprintf(
+                                               strcat(
+                                                       "^1NOTIFICATION HAS DURCNT BUT NO CPID: ",
+                                                       "^7net_type = %s, net_name = %s.\n"
+                                               ),
+                                               typestring,
+                                               namestring
+                                       ));
+                                       notif_error = TRUE;
+                               }
+                       } 
+                       else if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
+                       #endif
+
+
+                       // ======================
+                       //  Process Notif String
+                       // ======================
+                       #define SET_NOTIF_STRING(string,stringname) \
+                               notif.nent_string = strzone(CCR( \
+                                       Process_Notif_Line( \
+                                               msg_is_info, \
+                                               (var_cvar > 1), \
+                                               string, \
+                                               typestring, \
+                                               namestring, \
+                                               stringname \
+                                       )) \
+                               );
+
+                       if(GENTLE)
+                       {
+                               if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE") }
+                               else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
+                       }
+                       else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
+                       
+                       #undef SET_NOTIF_STRING
+
+                       // Check to make sure a string was chosen
+                       if(notif.nent_string == "")
+                       {
+                               print(sprintf(
+                                       strcat(
+                                               "^1EMPTY NOTIFICATION: ",
+                                               "^7net_type = %s, net_name = %s.\n"
+                                       ),
+                                       typestring,
+                                       namestring
+                               ));
+                               notif_error = TRUE;
+                       }
+               }
+       }
+
+       // now check to see if any errors happened 
+       if(notif_error)
+       {
+               notif.nent_enabled = FALSE; // disable the notification so it can't cause trouble
+               notif_global_error = TRUE; // throw the red flag that an error happened on init
+       }
+}
+
+
+// =========================================
+//  Cvar Handling With 'dumpnotifs' Command
+// =========================================
+
+void Dump_Notifications(float fh, float alsoprint)
+{
+       #define NOTIF_WRITE(a) { \
+               fputs(fh, a); \
+               if(alsoprint) { print(a); } }
+       #define NOTIF_WRITE_ENTITY(description) { \
+               notif_msg = \
+                       sprintf( \
+                               "seta notification_%s \"%d\" \"%s\"\n", \
+                               e.nent_name, e.nent_default, description \
+                       ); \
+               NOTIF_WRITE(notif_msg) }
+       #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
+               notif_msg = \
+                       sprintf( \
+                               "seta notification_%s \"%s\" \"%s\"\n", \
+                               cvar, default, description \
+                       ); \
+               NOTIF_WRITE(notif_msg) }
 
 
-       #ifndef MENUQC
        string notif_msg;
        float i;
        entity e;
 
        string notif_msg;
        float i;
        entity e;
 
-       for(i = 0; i < NOTIF_INFO_COUNT; ++i) {
+       // Note: This warning only applies to the notifications.cfg file that is output...
+
+       // You ARE supposed to manually edit this function to add i.e. hard coded
+       // notification variables for mutators or game modes or such and then
+       // regenerate the notifications.cfg file from the new code.
+
+       NOTIF_WRITE("// ********************************************** //\n");
+       NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
+       NOTIF_WRITE("// **                                          ** //\n");
+       NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
+       NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
+       NOTIF_WRITE("// **                                          ** //\n");
+       NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
+       NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
+       NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
+       NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
+       NOTIF_WRITE("// **                                          ** //\n");
+       NOTIF_WRITE("// ********************************************** //\n");
+
+       // These notifications will also append their string as a comment...
+       // This is not necessary, and does not matter if they vary between config versions,
+       // it is just a semi-helpful tool for those who want to manually change their user settings.
+
+       NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
+       for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i)
+       {
+               e = Get_Notif_Ent(MSG_ANNCE, i);
+               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
+               
+               NOTIF_WRITE_ENTITY(
+                       "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)"
+               );
+       }
+
+       NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
+       for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
+       {
                e = Get_Notif_Ent(MSG_INFO, i);
                e = Get_Notif_Ent(MSG_INFO, i);
-               NOTIF_WRITE(MSG_INFO, e.nent_name, e.nent_string); }
+               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
                
                
-       for(i = 0; i < NOTIF_CENTER_COUNT; ++i) {
+               NOTIF_WRITE_ENTITY(
+                       "Notification control cvar: 0 = off, 1 = print to console, "
+                       "2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+               );
+       }
+
+       NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
+       for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
+       {
                e = Get_Notif_Ent(MSG_CENTER, i);
                e = Get_Notif_Ent(MSG_CENTER, i);
-               NOTIF_WRITE(MSG_CENTER, e.nent_name, e.nent_string); }
-       
-       for(i = 0; i < NOTIF_WEAPON_COUNT; ++i) {
-               e = Get_Notif_Ent(MSG_WEAPON, i);
-               NOTIF_WRITE(MSG_WEAPON, e.nent_name, sprintf("infoname: %s, centername: %s",
-                       Get_Notif_Name(MSG_INFO, Get_Notif_Infval(MSG_WEAPON, i)),
-                       Get_Notif_Name(MSG_CENTER, Get_Notif_Cenval(MSG_WEAPON, i)) ) ); }
+               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
                
                
-       for(i = 0; i < NOTIF_DEATH_COUNT; ++i) {
-               e = Get_Notif_Ent(MSG_DEATH, i);
-               NOTIF_WRITE(MSG_DEATH, e.nent_name, sprintf("infoname: %s, centername: %s",
-                       Get_Notif_Name(MSG_INFO, Get_Notif_Infval(MSG_DEATH, i)),
-                       Get_Notif_Name(MSG_CENTER, Get_Notif_Cenval(MSG_DEATH, i)) ) ); }
+               NOTIF_WRITE_ENTITY(
+                       "Notification control cvar: 0 = off, 1 = centerprint"
+               );
+       }
+
+       NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
+       for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
+       {
+               e = Get_Notif_Ent(MSG_MULTI, i);
+               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
                
                
-       #endif
+               NOTIF_WRITE_ENTITY(
+                       "Notification control cvar: 0 = off, 1 = trigger subcalls"
+               );
+       }
+
+       // edit these to match whichever cvars are used for specific notification options
+       NOTIF_WRITE("\n// HARD CODED notification variables:\n");
+       
+       NOTIF_WRITE_HARDCODED(
+               "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)"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "ctf_capture_verbose", "0",
+               "Show extra information when someone captures a flag"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "ctf_pickup_enemy_verbose", "0",
+               "Show extra information if an enemy picks up a flag"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "ctf_pickup_team_verbose", "0",
+               "Show extra information if a team mate picks up a flag"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "debug", "0",
+               "Print extra debug information on all notification function calls "
+               "(Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... "
+               "0 = disabled, 1 = dprint, 2 = print"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "errors_are_fatal", "1",
+               "If a notification fails upon initialization, cause a Host_Error to stop the program"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "frag_verbose", "1",
+               "Show extra information when you frag someone (or when you are fragged"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "item_centerprinttime", "1.5",
+               "How long to show item information centerprint messages (like 'You got the Electro' or such)"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "lifetime_mapload", "10",
+               "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)"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "lifetime_runtime", "0.5",
+               "Amount of time that notification entities last on the server during runtime (In seconds)"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "server_allows_frag_verbose", "1",
+               "Server side cvar for showing extra information in frag messages... 0 = no extra frag information, "
+               "1 = frag information only in warmup, 2 = frag information allowed all the time"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "server_allows_location", "1",
+               "Server side cvar for allowing death messages to show location information too"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_location", "0",
+               "Append location information to MSG_INFO death/kill messages"
+       );
+       
+       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)'"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees", "1",
+               "Print information about sprees in death/kill messages"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees_center", "1",
+               "Show spree information in MSG_CENTER messages... "
+               "0 = off, 1 = target (but only for first victim) and attacker"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees_center_specialonly", "1",
+               "Don't show spree information in MSG_CENTER messages if it isn't an achievement"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees_info", "3",
+               "Show spree information in MSG_INFO messages... "
+               "0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees_info_newline", "1",
+               "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
+       );
+       
+       NOTIF_WRITE_HARDCODED(
+               "show_sprees_info_specialonly", "1",
+               "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
+       );
 
 
-       print(sprintf("Notification counts: MSG_INFO = %d, MSG_CENTER = %d, MSG_WEAPON = %d, MSG_DEATH = %d\n",
-               MSG_INFO_NOTIFS, MSG_CENTER_NOTIFS, MSG_WEAPON_NOTIFS, MSG_DEATH_NOTIFS));
+       NOTIF_WRITE(sprintf(
+               strcat(
+                       "\n// Notification counts (total = %d): ",
+                       "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
+               ),
+               (
+                       NOTIF_ANNCE_COUNT +
+                       NOTIF_INFO_COUNT +
+                       NOTIF_CENTER_COUNT +
+                       NOTIF_MULTI_COUNT
+               ),
+               NOTIF_ANNCE_COUNT,
+               NOTIF_INFO_COUNT,
+               NOTIF_CENTER_COUNT,
+               NOTIF_MULTI_COUNT
+       ));
        
        return;
        
        return;
+       #undef NOTIF_WRITE_HARDCODED
+       #undef NOTIF_WRITE_ENTITY
        #undef NOTIF_WRITE
 }
 
        #undef NOTIF_WRITE
 }
 
-#ifndef MENUQC
-#ifdef CSQC
-void HUD_Notify_Push(string icon, string attacker, string victim)
+#ifdef SVQC
+void Notification_GetCvars()
 {
 {
-       if(icon != "")
-       {
-               --kn_index;
-               if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
-               killnotify_times[kn_index] = time;
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
+}
+#endif
 
 
-               // icon
-               if(killnotify_icon[kn_index]) { strunzone(killnotify_icon[kn_index]); }
-               killnotify_icon[kn_index] = strzone(icon);
 
 
-               // attacker
-               if(killnotify_attackers[kn_index]) { strunzone(killnotify_attackers[kn_index]); }
-               killnotify_attackers[kn_index] = strzone(attacker);
+// ===============================
+//  Frontend Notification Pushing
+// ===============================
 
 
-               // victim
-               if(killnotify_victims[kn_index]) { strunzone(killnotify_victims[kn_index]); }
-               killnotify_victims[kn_index] = strzone(victim);
+#ifdef NOTIFICATIONS_DEBUG
+void Debug_Notification(string input)
+{
+       switch(autocvar_notification_debug)
+       {
+               case 1: { dprint(input); break; }
+               case 2: { print(input); break; }
        }
 }
        }
 }
-#endif // ifdef CSQC
-
+#endif
 
 
-string Local_Notification_sprintf(string input, string args, 
+string Local_Notification_sprintf(
+       string input, string args, 
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
        #ifdef NOTIFICATIONS_DEBUG
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
        #ifdef NOTIFICATIONS_DEBUG
-       dprint(
-               sprintf("Local_Notification_sprintf('%s', '%s', %s, %s);\n",
-               input, args,
-               sprintf("%s, %s, %s, %s", s1, s2, s3, s4),
-               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)));
+       Debug_Notification(sprintf(
+               "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
+               MakeConsoleSafe(input),
+               args,
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
        #endif
        
        #endif
        
-       string selected, remaining = args;
-       float sel_num = 0;
+       string selected;
+       float sel_num;
+       for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
 
 
-       #ifdef CSQC
        string tmp_s;
        string tmp_s;
-       #endif
-       
-       if((remaining != "") && (input != ""))
+
+       for(sel_num = 0;(args != "");)
        {
        {
-               for(;remaining;)
+               selected = car(args); args = cdr(args);
+               NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
+               switch(strtolower(selected))
                {
                {
-                       selected = car(remaining); remaining = cdr(remaining);
-
-                       switch(strtolower(selected))
-                       {
-                               #define ADD_ARG_CASE(selected,result) case selected: { arg_slot[sel_num] = result; ++sel_num; break; }
-                               
-                               ADD_ARG_CASE("s1", s1)
-                               ADD_ARG_CASE("s2", s2)
-                               ADD_ARG_CASE("s3", s3)
-                               ADD_ARG_CASE("s4", s4)
-                               ADD_ARG_CASE("f1", ftos(f1))
-                               ADD_ARG_CASE("f2", ftos(f2))
-                               ADD_ARG_CASE("f3", ftos(f3))
-                               ADD_ARG_CASE("f4", ftos(f4))
-
-                               #ifdef CSQC // CSQC replacements
-                               ADD_ARG_CASE("pass_key",                ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : ""))
-                               ADD_ARG_CASE("frag_ping",               ((f2 != BOT_PING) ? sprintf(CCR(_("\n(Ping ^2%d^BG)")), f2) : ""))
-                               ADD_ARG_CASE("frag_stats",              sprintf(CCR(_("\n(Health ^1%d^BG / Armor ^2%d^BG)%s")), f1, f2, ((f3 != BOT_PING) ? sprintf(CCR(_(" (Ping ^2%d^BG)")), f3) : "")))
-                               //ADD_ARG_CASE("frag_pos",              ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))
-                               ADD_ARG_CASE("spree_cen",               (((f1 == 3) || (f1 == 5) || (f1 == 10) || (f1 == 15) || (f1 == 20) || (f1 == 25) || (f1 == 30)) ? sprintf(normal_or_gentle(_("%d frag spree! "), _("%d score spree! ")), f1) : ""))
-                               ADD_ARG_CASE("spree_inf",               (((f1 == 3) || (f1 == 5) || (f1 == 10) || (f1 == 15) || (f1 == 20) || (f1 == 25) || (f1 == 30)) ? sprintf(normal_or_gentle(_("%d frag spree! "), _("%d score spree! ")), f1) : ""))
-                               ADD_ARG_CASE("spree_end",               ((f1 >= 3) ? sprintf(normal_or_gentle(_(", ending their %d frag spree"), _(", ending their %d score spree")), f1) : ""))
-                               ADD_ARG_CASE("spree_lost",              ((f1 >= 3) ? sprintf(normal_or_gentle(_(", losing their %d frag spree"), _(", losing their %d score spree")), f1) : ""))
-                               ADD_ARG_CASE("death_team",              Team_ColoredFullName(f1 - 1))
-                               ADD_ARG_CASE("weapon_name",     ftos(f1)) // weaponorder[f1].netname
-
-                               #else // SVQC replacements
-                               ADD_ARG_CASE("spree_inf",               (((f1 == 3) || (f1 == 5) || (f1 == 10) || (f1 == 15) || (f1 == 20) || (f1 == 25) || (f1 == 30)) ? sprintf(normal_or_gentle(_("%d frag spree! "), _("%d score spree! ")), f1) : ""))
-                               ADD_ARG_CASE("spree_end",               ((f1 >= 3) ? sprintf(normal_or_gentle(_(", ending their %d frag spree"), _(", ending their %d score spree")), f1) : ""))
-                               ADD_ARG_CASE("spree_lost",              ((f1 >= 3) ? sprintf(normal_or_gentle(_(", losing their %d frag spree"), _(", losing their %d score spree")), f1) : ""))
-                               ADD_ARG_CASE("death_team",              Team_ColoredFullName(f1))
-                               ADD_ARG_CASE("weapon_name",             ftos(f1)) // weaponorder[f1].netname
+                       #define ARG_CASE(prog,selected,result) \
+                               #ifdef CSQC \
+                                       #if (prog != ARG_SV) && (prog != ARG_DC) \
+                                               case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
+                                       #endif \
+                               #else \
+                                       #if (prog != ARG_CS) && (prog != ARG_DC) \
+                                               case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
+                                       #endif \
                                #endif
                                #endif
-
-                               #undef ADD_ARG_CASE
-                               default: { backtrace(sprintf("Hit unknown token in selected string! '%s'\n", selected)); break; }
-                       }
-                       if(sel_num == 6) { backtrace("Hit maximum arguments!\n"); break; }
+                       NOTIF_ARGUMENT_LIST
+                       #undef ARG_CASE
+                       default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
                }
                }
-
-               return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
        }
        }
+       return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
+}
 
 
-       return "";
+#ifdef CSQC
+void Local_Notification_sound(
+       float soundchannel, string soundfile,
+       float soundvolume, float soundposition)
+{
+       if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition
+               ));
+               #endif
+               
+               sound(
+                       world,
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition
+               );
+               
+               if(prev_soundfile) { strunzone(prev_soundfile); }
+               prev_soundfile = strzone(soundfile);
+               prev_soundtime = time;
+       }
+       else
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       strcat(
+                               "Local_Notification_sound(world, %f, '%s', %f, %f) ",
+                               "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
+                        ),
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition,
+                       prev_soundfile,
+                       (time - prev_soundtime),
+                       autocvar_cl_announcer_antispam
+               ));
+               #endif
+       }
 }
 
 }
 
-void Local_Notification(float net_type, float net_name, ...count)
+void Local_Notification_HUD_Notify_Push(
+       string icon, string hudargs,
+       string s1, string s2, string s3, string s4)
 {
 {
-       // check supplied type and name for errors
-       #ifdef NOTIFICATIONS_DEBUG
+       string selected;
+       float sel_num;
+       arg_slot[0] = ""; arg_slot[1] = "";
+
+       for(sel_num = 0;(hudargs != "");)
        {
        {
-               #define CHECKARG_TYPENAME(type) case MSG_##type##: \
-                       { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
-                       { checkargs = sprintf("Improper name: %d!", net_name); } break; }
-                       
-               string checkargs = "";
-               switch(net_type)
+               selected = car(hudargs); hudargs = cdr(hudargs);
+               NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
+               switch(strtolower(selected))
                {
                {
-                       CHECKARG_TYPENAME(INFO)
-                       CHECKARG_TYPENAME(CENTER)
-                       CHECKARG_TYPENAME(WEAPON)
-                       CHECKARG_TYPENAME(DEATH)
-                       
-                       default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
+                       #define ARG_CASE(prog,selected,result) \
+                               #if (prog == ARG_CS_SV_HA) \
+                                       case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
+                               #endif
+                       NOTIF_ARGUMENT_LIST
+                       #undef ARG_CASE
+                       default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
                }
                }
-               #undef CHECKARG_TYPENAME
-
-               if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
        }
        }
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
+               icon,
+               hudargs,
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
+       ));
        #endif
        #endif
+       HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
+}
 
 
-       entity notif = Get_Notif_Ent(net_type, net_name);
-       if not(notif) { backtrace("Local_Notification: Could not find notification entity! (This wasn't caught by usage check?...)\n"); return; }
-       if not(notif.nent_enabled) { print("entity notification was disabled...\n"); return; }
-
-       string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : NO_STR_ARG);
-       string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : NO_STR_ARG);
-       string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : NO_STR_ARG);
-       string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : NO_STR_ARG);
-       float f1 = ((notif.nent_stringcount < count) ? ...(notif.nent_stringcount, float) : NO_FL_ARG);
-       float f2 = (((notif.nent_stringcount + 1) < count) ? ...((notif.nent_stringcount + 1), float) : NO_FL_ARG);
-       float f3 = (((notif.nent_stringcount + 2) < count) ? ...((notif.nent_stringcount + 2), float) : NO_FL_ARG);
-       float f4 = (((notif.nent_stringcount + 3) < count) ? ...((notif.nent_stringcount + 3), float) : NO_FL_ARG);
+void Local_Notification_centerprint_generic(
+       string input, string durcnt,
+       float cpid, float f1, float f2)
+{
+       string selected;
+       float sel_num;
+       arg_slot[0] = ""; arg_slot[1] = "";
 
 
-       #ifdef NOTIFICATIONS_DEBUG
+       for(sel_num = 0;(durcnt != "");)
        {
        {
-               dprint(sprintf("Local_Notification(%d, %s, %s, %s, %s, %s, %d, %d, %d, %d);\n",
-                       net_type, notif.nent_name,
-                       s1, s2, s3, s4, f1, f2, f3, f4));
+               selected = car(durcnt); durcnt = cdr(durcnt);
+               NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
+               switch(strtolower(selected))
+               {
+                       #define ARG_CASE(prog,selected,result) \
+                               #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
+                                       case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
+                               #endif
+                       NOTIF_ARGUMENT_LIST
+                       #undef ARG_CASE
+                       default:
+                       {
+                               if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
+                               else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
+                               break;
+                       }
+               }
+       }
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
+               MakeConsoleSafe(input),
+               durcnt,
+               f1, f2,
+               stof(arg_slot[0]), stof(arg_slot[1])
+       ));
+       #endif
+       centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
+}
+#endif
+
+void Local_Notification(float net_type, float net_name, ...count)
+{
+       // check supplied type and name for errors
+       string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
+       if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
 
 
-               if((notif.nent_stringcount + notif.nent_floatcount) > count)
-                       { backtrace(sprintf(strcat("Not enough arguments for Send_Notification! stringcount(%d) + floatcount(%d) > count(%d)\n", 
-                       "Check the notification definition and function call for accuracy...?\n"), notif.nent_stringcount, notif.nent_floatcount, count)); return; }
-               else if((notif.nent_stringcount + notif.nent_floatcount) < count)
-                       { backtrace(sprintf(strcat("Too many arguments for Send_Notification! stringcount(%d) + floatcount(%d) < count(%d)\n",
-                       "Check the notification definition and function call for accuracy...?\n"), notif.nent_stringcount, notif.nent_floatcount, count)); return; }
+       entity notif = Get_Notif_Ent(net_type, net_name);
+       if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
+       if not(notif.nent_enabled)
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Local_Notification(%s, %s): Entity was disabled...\n",
+                       Get_Notif_TypeName(net_type),
+                       notif.nent_name
+               ));
+               #endif
+               return;
+       }
+       
+       if((notif.nent_stringcount + notif.nent_floatcount) > count)
+       {
+               backtrace(sprintf(
+                       strcat(
+                               "Not enough arguments for Local_Notification(%s, %s, ...)! ",
+                               "stringcount(%d) + floatcount(%d) > count(%d)\n", 
+                               "Check the definition and function call for accuracy...?\n"
+                       ),
+                       Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
+               return;
        }
        }
+       else if((notif.nent_stringcount + notif.nent_floatcount) < count)
+       {
+               backtrace(sprintf(
+                       strcat(
+                               "Too many arguments for Local_Notification(%s, %s, ...)! ",
+                               "stringcount(%d) + floatcount(%d) < count(%d)\n",
+                               "Check the definition and function call for accuracy...?\n"
+                       ),
+                       Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
+               return;
+       }
+
+       string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
+       string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
+       string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
+       string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
+       float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
+       float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
+       float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
+       float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
+
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Local_Notification(%s, %s, %s, %s);\n",
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
        #endif
        
        switch(net_type)
        {
        #endif
        
        switch(net_type)
        {
+               case MSG_ANNCE:
+               {
+                       #ifdef CSQC
+                       Local_Notification_sound(
+                               notif.nent_channel,
+                               notif.nent_snd,
+                               notif.nent_vol,
+                               notif.nent_position
+                       );
+                       #else
+                       backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
+                       #endif
+                       break;
+               }
+               
                case MSG_INFO:
                {
                case MSG_INFO:
                {
-                       print(Local_Notification_sprintf(notif.nent_string, notif.nent_args, 
-                               s1, s2, s3, s4, f1, f2, f3, f4));
-                               
+                       print(
+                               Local_Notification_sprintf(
+                                       notif.nent_string,
+                                       notif.nent_args, 
+                                       s1, s2, s3, s4,
+                                       f1, f2, f3, f4)
+                       );
                        #ifdef CSQC 
                        #ifdef CSQC 
-                       if(notif.nent_icon != "") { HUD_Notify_Push(notif.nent_icon, s1, s2); } 
+                       if(notif.nent_icon != "")
+                       {
+                               Local_Notification_HUD_Notify_Push(
+                                       notif.nent_icon,
+                                       notif.nent_hudargs,
+                                       s1, s2, s3, s4);
+                       } 
                        #endif 
                        break;
                }
                        #endif 
                        break;
                }
@@ -273,48 +1199,72 @@ void Local_Notification(float net_type, float net_name, ...count)
                #ifdef CSQC
                case MSG_CENTER:
                {
                #ifdef CSQC
                case MSG_CENTER:
                {
-                       centerprint_generic(notif.nent_cpid,
-                               Local_Notification_sprintf(notif.nent_string, notif.nent_args, 
-                               s1, s2, s3, s4, f1, f2, f3, f4), 0, 0);
+                       Local_Notification_centerprint_generic(
+                               Local_Notification_sprintf(
+                                       notif.nent_string,
+                                       notif.nent_args, 
+                                       s1, s2, s3, s4,
+                                       f1, f2, f3, f4),
+                               notif.nent_durcnt,
+                               notif.nent_cpid,
+                               f1, f2);
                        break;
                }
                #endif
                
                        break;
                }
                #endif
                
-               case MSG_WEAPON:
-               case MSG_DEATH:
+               case MSG_MULTI:
                {
                {
-                       if(notif.nent_infoname)
-                       if(msg_info_notifs[notif.nent_infoname - 1].nent_enabled)
+                       if(notif.nent_msginfo)
+                       if(notif.nent_msginfo.nent_enabled)
+                       {
+                               Local_Notification_WOVA(
+                                       MSG_INFO,
+                                       notif.nent_msginfo.nent_id, 
+                                       notif.nent_msginfo.nent_stringcount, 
+                                       notif.nent_msginfo.nent_floatcount, 
+                                       s1, s2, s3, s4,
+                                       f1, f2, f3, f4);
+                       }
+                       #ifdef CSQC
+                       if(notif.nent_msgannce)
+                       if(notif.nent_msgannce.nent_enabled)
                        {
                        {
-                               Local_Notification_Without_VarArgs(MSG_INFO, notif.nent_infoname, 
-                                       Get_Notif_Strnum(MSG_INFO, notif.nent_infoname), 
-                                       Get_Notif_Flnum(MSG_INFO, notif.nent_infoname), 
-                                       s1, s2, s3, s4, f1, f2, f3, f4);
+                               Local_Notification_WOVA(
+                                       MSG_ANNCE,
+                                       notif.nent_msgannce.nent_id, 
+                                       0, 0, 
+                                       "", "", "", "",
+                                       0, 0, 0, 0);
                        }
                        }
-                       #ifdef CSQC \
-                       if(notif.nent_centername)
-                       if(msg_center_notifs[notif.nent_centername - 1].nent_enabled)
+                       if(notif.nent_msgcenter)
+                       if(notif.nent_msgcenter.nent_enabled)
                        {
                        {
-                               Local_Notification_Without_VarArgs(MSG_CENTER, notif.nent_centername, 
-                                       Get_Notif_Strnum(MSG_CENTER, notif.nent_centername), 
-                                       Get_Notif_Flnum(MSG_CENTER, notif.nent_centername), 
-                                       s1, s2, s3, s4, f1, f2, f3, f4); 
+                               Local_Notification_WOVA(
+                                       MSG_CENTER,
+                                       notif.nent_msgcenter.nent_id, 
+                                       notif.nent_msgcenter.nent_stringcount, 
+                                       notif.nent_msgcenter.nent_floatcount, 
+                                       s1, s2, s3, s4,
+                                       f1, f2, f3, f4); 
                        }
                        }
-                       #endif \
+                       #endif
                        break;
                }
        }
 }
 
                        break;
                }
        }
 }
 
-void Local_Notification_Without_VarArgs(float net_type, float net_name,
+// WOVA = Without Variable Arguments 
+void Local_Notification_WOVA(
+       float net_type, float net_name,
        float stringcount, float floatcount,
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
        float stringcount, float floatcount,
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
-       #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) { Local_Notification(net_type, net_name, args); return; }
+       #define VARITEM(stringc,floatc,args) \
+               if((stringcount == stringc) && (floatcount == floatc)) \
+                       { Local_Notification(net_type, net_name, args); return; }
        EIGHT_VARS_TO_VARARGS_VARLIST
        #undef VARITEM
        EIGHT_VARS_TO_VARARGS_VARLIST
        #undef VARITEM
-
        Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
 }
 
        Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
 }
 
@@ -329,46 +1279,171 @@ void Read_Notification(float is_new)
        float net_type = ReadByte();
        float net_name = ReadShort();
 
        float net_type = ReadByte();
        float net_name = ReadShort();
 
-       float stringcount = Get_Notif_Strnum(net_type, net_name);
-       float floatcount = Get_Notif_Flnum(net_type, net_name);
+       entity notif;
 
 
-       string s1 = ((stringcount >= 1) ? ReadString() : NO_STR_ARG);
-       string s2 = ((stringcount >= 2) ? ReadString() : NO_STR_ARG);
-       string s3 = ((stringcount >= 3) ? ReadString() : NO_STR_ARG);
-       string s4 = ((stringcount == 4) ? ReadString() : NO_STR_ARG);
-       float f1 = ((floatcount >= 1) ? ReadLong() : NO_FL_ARG);
-       float f2 = ((floatcount >= 2) ? ReadLong() : NO_FL_ARG);
-       float f3 = ((floatcount >= 3) ? ReadLong() : NO_FL_ARG);
-       float f4 = ((floatcount == 4) ? ReadLong() : NO_FL_ARG);
+       if(net_type == MSG_CENTER_CPID)
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
+                       is_new,
+                       time,
+                       Get_Notif_TypeName(net_type),
+                       net_name
+               ));
+               #endif
+               
+               if(is_new)
+               {
+                       if(net_name == 0) { reset_centerprint_messages(); }
+                       else if(net_name != NO_CPID)
+                       {
+                               // in this case, net_name IS the cpid we want to kill
+                               centerprint_generic(net_name, "", 0, 0);
+                       }
+                       else
+                       {
+                               backtrace(sprintf(
+                                       "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
+                                       is_new,
+                                       time
+                               ));
+                       } 
+               }
+       }
+       else
+       {
+               notif = Get_Notif_Ent(net_type, net_name);
+               if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
 
 
-       #ifdef NOTIFICATIONS_DEBUG
-       dprint(sprintf("Read_Notification(%d) at %f: net_name = %s.\n", is_new, time, Get_Notif_Name(net_type, net_name)));
-       #endif
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
+                       is_new,
+                       time,
+                       Get_Notif_TypeName(net_type),
+                       notif.nent_name
+               ));
+               #endif
+
+               string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
+               string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
+               string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
+               string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
+               float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
+               float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
+               float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
+               float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
        
        
-       if(is_new) { Local_Notification_Without_VarArgs(net_type, net_name, stringcount, floatcount, s1, s2, s3, s4, f1, f2, f3, f4); }
+               if(is_new)
+               {
+                       Local_Notification_WOVA(
+                               net_type, net_name,
+                               notif.nent_stringcount,
+                               notif.nent_floatcount,
+                               s1, s2, s3, s4,
+                               f1, f2, f3, f4);
+               }
+       }
 }
 #endif
 
 #ifdef SVQC
 }
 #endif
 
 #ifdef SVQC
-void Notification_Remove()
+void Net_Notification_Remove()
 {
 {
+       if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
+       
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
+               time,
+               ((self.nent_net_name == -1) ? "Killed" : "Removed"),
+               Get_Notif_TypeName(self.nent_net_type),
+               self.owner.nent_name
+       ));
+       #endif
+       
        float i;
        for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
        remove(self);
 }
 
        float i;
        for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
        remove(self);
 }
 
-float Write_Notification(entity client, float sf)
+float Net_Write_Notification(entity client, float sf)
 {
        float i, send = FALSE;
        
        switch(self.nent_broadcast)
        {
 {
        float i, send = FALSE;
        
        switch(self.nent_broadcast)
        {
-               case NOTIF_ONE: { if((client == self.nent_client) || (client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
-               case NOTIF_ONE_ONLY: { if(client == self.nent_client) { send = TRUE; } break; }
-               case NOTIF_TEAM: { if((client.team == self.nent_client.team) || (client.classname == STR_SPECTATOR && client.enemy.team == self.nent_client.team)) { send = TRUE; } break; }
-               case NOTIF_TEAM_EXCEPT: { if(((client != self.nent_client) && (client.team == self.nent_client.team) && !(client.classname == STR_SPECTATOR && client.enemy == self.nent_client))) { send = TRUE; } break; }
-               case NOTIF_ANY: { send = TRUE; break; }
-               case NOTIF_ANY_EXCEPT: { if((client != self.nent_client) && !(client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
+               case NOTIF_ONE: // send to one client and their spectator
+               {
+                       if(
+                               (client == self.nent_client)
+                               ||
+                               (
+                                       IS_SPEC(client)
+                                       &&
+                                       (client.enemy == self.nent_client)
+                               )
+                       ) { send = TRUE; }
+                       break;
+               }
+               case NOTIF_ONE_ONLY: // send ONLY to one client
+               {
+                       if(client == self.nent_client) { send = TRUE; }
+                       break;
+               }
+               case NOTIF_TEAM: // send only to X team and their spectators
+               {
+                       if(
+                               (client.team == self.nent_client.team)
+                               ||
+                               (
+                                       IS_SPEC(client)
+                                       &&
+                                       (client.enemy.team == self.nent_client.team)
+                               )
+                       ) { send = TRUE; }
+                       break;
+               }
+               case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
+               {
+                       if(
+                               (client != self.nent_client)
+                               &&
+                               (
+                                       (client.team == self.nent_client.team)
+                                       ||
+                                       (
+                                               IS_SPEC(client)
+                                               &&
+                                               (
+                                                       (client.enemy != self.nent_client)
+                                                       &&
+                                                       (client.enemy.team == self.nent_client.team)
+                                               )
+                                       )
+                               )
+                       ) { send = TRUE; }
+                       break;
+               }
+               case NOTIF_ALL: // send to everyone
+               {
+                       send = TRUE;
+                       break;
+               }
+               case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
+               {
+                       if(
+                               (client != self.nent_client)
+                               &&
+                               !(
+                                       IS_SPEC(client)
+                                       &&
+                                       (client.enemy == self.nent_client)
+                               )
+                       ) { send = TRUE; }
+                       break;
+               }
                default: { send = FALSE; break; }
        }
 
                default: { send = FALSE; break; }
        }
 
@@ -384,145 +1459,235 @@ float Write_Notification(entity client, float sf)
        return send; 
 }
 
        return send; 
 }
 
-void Send_Notification(float broadcast, entity client,
-       float net_type, float net_name, ...count)
+void Kill_Notification(
+       float broadcast, entity client,
+       float net_type, float net_name)
 {
 {
-       // check supplied broadcast, target, type, and name for errors
+       string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
+       if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
+
        #ifdef NOTIFICATIONS_DEBUG
        #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Kill_Notification(%d, '%s', %s, %d);\n",
+               broadcast,
+               client.netname,
+               (net_type ? Get_Notif_TypeName(net_type) : "0"),
+               net_name
+       ));
+       #endif
+
+       entity notif, net_notif;
+       float killed_cpid = NO_CPID;
+       
+       switch(net_type)
        {
        {
-               #define CHECKARG_TYPENAME(type) case MSG_##type##: \
-                       { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
-                       { checkargs = sprintf("Improper name: %d!", net_name); } break; }
-                       
-               string checkargs = "";
-               switch(net_type)
+               case 0:
                {
                {
-                       CHECKARG_TYPENAME(INFO)
-                       CHECKARG_TYPENAME(CENTER)
-                       CHECKARG_TYPENAME(WEAPON)
-                       CHECKARG_TYPENAME(DEATH)
-                       
-                       default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
+                       killed_cpid = 0; // kill ALL centerprints
+                       break;
                }
                }
-               #undef CHECKARG_TYPENAME
                
                
-               if(checkargs != "") { checkargs = strcat(checkargs, " "); }
-               switch(broadcast)
+               case MSG_CENTER:
                {
                {
-                       case NOTIF_ONE:
-                       case NOTIF_ONE_ONLY: { if(clienttype(client) == CLIENTTYPE_NOTACLIENT) { checkargs = sprintf("%sNo client provided!", checkargs); } break; }
-                       case NOTIF_ANY_EXCEPT: { if(clienttype(client) == CLIENTTYPE_NOTACLIENT) { checkargs = sprintf("%sException can't be a non-client!", checkargs); } break; }
-                       case NOTIF_ANY: { if(client) { checkargs = sprintf("%sEntity provided when world was required!", checkargs); } break; }
-                       
-                       case NOTIF_TEAM:
-                       case NOTIF_TEAM_EXCEPT:
+                       if(net_name)
+                       {
+                               entity notif = Get_Notif_Ent(net_type, net_name);
+                               if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
+                               
+                               if(notif.nent_cpid)
+                                       killed_cpid = notif.nent_cpid;
+                               else
+                                       killed_cpid = NO_CPID;
+                       }
+                       else
                        {
                        {
-                               if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
-                               else if(clienttype(client) == CLIENTTYPE_NOTACLIENT)
+                               killed_cpid = 0; // kill ALL centerprints
+                       }
+                       break;
+               }
+
+               case MSG_CENTER_CPID:
+               {
+                       killed_cpid = net_name;
+                       break;
+               }
+       }
+
+       if(killed_cpid != NO_CPID)
+       {
+               net_notif = spawn();
+               net_notif.classname = "net_kill_notification";
+               net_notif.nent_broadcast = broadcast;
+               net_notif.nent_client = client;
+               net_notif.nent_net_type = MSG_CENTER_CPID;
+               net_notif.nent_net_name = killed_cpid;
+               Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
+       }
+
+       for(notif = world; (notif = find(notif, classname, "net_notification"));)
+       {
+               if(net_type)
+               {
+                       if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
+                       {
+                               if(notif.owner.nent_cpid == killed_cpid)
                                {
                                {
-                                       if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
-                                       else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
+                                       notif.nent_net_name = -1;
+                                       notif.nextthink = time;
                                }
                                }
-                               break;
+                               else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
                        }
                        }
-                       
-                       default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
+                       else if(notif.nent_net_type == net_type)
+                       {
+                               if(net_name)
+                               {
+                                       if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
+                                       else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
+                               }
+                               else { notif.nent_net_name = -1; notif.nextthink = time; }
+                       }
+                       else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
                }
                }
-               
-               if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
+               else { notif.nent_net_name = -1; notif.nextthink = time; }
        }
        }
-       #endif
+}
+
+void Send_Notification(
+       float broadcast, entity client,
+       float net_type, float net_name,
+       ...count)
+{
+       // check supplied broadcast, target, type, and name for errors
+       string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
+       if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
 
        // retreive counts for the arguments of this notification
 
        // retreive counts for the arguments of this notification
-       float stringcount = Get_Notif_Strnum(net_type, net_name);
-       float floatcount = Get_Notif_Flnum(net_type, net_name);
+       entity notif = Get_Notif_Ent(net_type, net_name);
+       if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
 
 
-       #ifdef NOTIFICATIONS_DEBUG
+       if((notif.nent_stringcount + notif.nent_floatcount) > count)
        {
        {
-               dprint(sprintf("Send_Notification(%d, %d, %s, stringcount: %d, floatcount: %d, varargs: %d);\n",
-                       broadcast, net_type, Get_Notif_Name(net_type, net_name), stringcount, floatcount, count));
-
-               if((stringcount + floatcount) > count)
-                       { backtrace(sprintf(strcat("Not enough arguments for Send_Notification! stringcount(%d) + floatcount(%d) > count(%d)\n", 
-                       "Check the notification definition and function call for accuracy...?\n"), stringcount, floatcount, count)); return; }
-               else if((stringcount + floatcount) < count)
-                       { backtrace(sprintf(strcat("Too many arguments for Send_Notification! stringcount(%d) + floatcount(%d) < count(%d)\n",
-                       "Check the notification definition and function call for accuracy...?\n"), stringcount, floatcount, count)); return; }
+               backtrace(sprintf(
+                       strcat(
+                               "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
+                               "stringcount(%d) + floatcount(%d) > count(%d)\n", 
+                               "Check the definition and function call for accuracy...?\n"
+                       ),
+                       broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
+               return;
        }
        }
+       else if((notif.nent_stringcount + notif.nent_floatcount) < count)
+       {
+               backtrace(sprintf(
+                       strcat(
+                               "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
+                               "stringcount(%d) + floatcount(%d) < count(%d)\n",
+                               "Check the definition and function call for accuracy...?\n"
+                       ),
+                       broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
+               return;
+       }
+
+       #ifdef NOTIFICATIONS_DEBUG
+       string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
+       string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
+       string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
+       string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
+       float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
+       float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
+       float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
+       float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
+       Debug_Notification(sprintf(
+               "Send_Notification(%d, %s, %s, %s, %s);\n",
+               broadcast,
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
        #endif
 
        #endif
 
-       entity notif = spawn();
-       notif.nent_broadcast = broadcast;
-       notif.nent_client = client;
-       notif.nent_net_type = net_type;
-       notif.nent_net_name = net_name;
-       notif.nent_stringcount = stringcount;
-       notif.nent_floatcount = floatcount;
+       entity net_notif = spawn();
+       net_notif.owner = notif;
+       net_notif.classname = "net_notification";
+       net_notif.nent_broadcast = broadcast;
+       net_notif.nent_client = client;
+       net_notif.nent_net_type = net_type;
+       net_notif.nent_net_name = net_name;
+       net_notif.nent_stringcount = notif.nent_stringcount;
+       net_notif.nent_floatcount = notif.nent_floatcount;
        
        float i;
        
        float i;
-       
-       for(i = 0; i < stringcount; ++i) { notif.nent_strings[i] = strzone(...(i, string)); }
-       for(i = 0; i < floatcount; ++i) { notif.nent_floats[i] = ...((stringcount + i), float); }
-       
-       notif.think = Notification_Remove;
-       notif.nextthink = (time + 0.5); 
+       for(i = 0; i < net_notif.nent_stringcount; ++i)
+               { net_notif.nent_strings[i] = strzone(...(i, string)); }
+       for(i = 0; i < net_notif.nent_floatcount; ++i)
+               { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
+
+       net_notif.think = Net_Notification_Remove;
+       net_notif.nextthink =
+               ((time > autocvar_notification_lifetime_mapload)
+               ?
+                       (time + autocvar_notification_lifetime_runtime)
+                       :
+                       autocvar_notification_lifetime_mapload
+               ); 
 
 
-       Net_LinkEntity(notif, FALSE, 0, Write_Notification);
+       Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
 
 
-       if((!server_is_local) && (broadcast == NOTIF_ANY || broadcast == NOTIF_ANY_EXCEPT) && (net_type != MSG_CENTER))
-               { Local_Notification_Without_VarArgs(net_type, net_name, stringcount, floatcount,
-                       IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3), IFFL(0), IFFL(1), IFFL(2), IFFL(3)); }
+       if(
+               server_is_dedicated
+               &&
+               (
+                       broadcast == NOTIF_ALL
+                       ||
+                       broadcast == NOTIF_ALL_EXCEPT
+               )
+               &&
+               !(
+                       net_type == MSG_ANNCE
+                       ||
+                       net_type == MSG_CENTER
+               )
+       )
+       {
+               Local_Notification_WOVA(
+                       net_type, net_name,
+                       notif.nent_stringcount,
+                       notif.nent_floatcount,
+                       IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
+                       IFFL(0), IFFL(1), IFFL(2), IFFL(3));
+       }
 }
 
 }
 
-void Send_Notification_Without_VarArgs(float broadcast, entity client,
+// WOVA = Without Variable Arguments 
+void Send_Notification_WOVA(
+       float broadcast, entity client,
        float net_type, float net_name,
        float net_type, float net_name,
-       float stringcount, float floatcount,
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
-{              
-       #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) { Send_Notification(broadcast, client, net_type, net_name, args); return; }
+{
+       entity notif = Get_Notif_Ent(net_type, net_name);
+       
+       #ifdef NOTIFICATIONS_DEBUG
+       Debug_Notification(sprintf(
+               "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n",
+               broadcast,
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
+       #endif
+       
+       #define VARITEM(stringc,floatc,args) \
+               if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
+                       { Send_Notification(broadcast, client, net_type, net_name, args); return; }
        EIGHT_VARS_TO_VARARGS_VARLIST
        #undef VARITEM
        EIGHT_VARS_TO_VARARGS_VARLIST
        #undef VARITEM
-
        Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
 }
        Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
 }
-
-void Send_Notification_Legacy_Wrapper(float broadcast, entity client,
-       float net_type, float net_name,
-       string s1, string s2,
-       float f1, float f2, float f3)
-{
-       float stringcount = Get_Notif_Strnum(net_type, net_name);
-       float floatcount = Get_Notif_Flnum(net_type, net_name);
-       Send_Notification_Without_VarArgs(broadcast, client, net_type, net_name, stringcount, floatcount, s1, s2, NO_STR_ARG, NO_STR_ARG, f1, f2, f3, NO_FL_ARG);
-}
-
-
-// =============================
-//  LEGACY NOTIFICATION SYSTEMS
-// =============================
-
-void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
-{
-       if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
-       {
-               msg_entity = e;
-               WRITESPECTATABLE_MSG_ONE({
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
-                       WriteByte(MSG_ONE, id);
-                       WriteString(MSG_ONE, s);
-                       if (id != 0 && s != "")
-                       {
-                               WriteByte(MSG_ONE, duration);
-                               WriteByte(MSG_ONE, countdown_num);
-                       }
-               });
-       }
-}
-void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
-{
-       Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
-}
 #endif // ifdef SVQC
 #endif // ifdef SVQC
-#endif // ifndef MENUQC