]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/notifications.qc
Fix debug print
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
index 30d91adf74f8579eef6bf318c76e28ad517179e3..2072bd6ff8ce04711dcef42f33b9eb878e4a3e74 100644 (file)
 // ================================================
 //  Unified notification system, written by Samual
 // ================================================
 //  Unified notification system, written by Samual
-//  Last updated: February, 2013
+//  Last updated: March, 2013
 // ================================================
 
 // ================================================
 
+string Get_Notif_TypeName(float net_type)
+{
+       switch(net_type)
+       {
+               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)
        {
                case MSG_INFO: return msg_info_notifs[net_name - 1];
                case MSG_CENTER: return msg_center_notifs[net_name - 1];
 entity Get_Notif_Ent(float net_type, float net_name)
 {
        switch(net_type)
        {
                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 Notification_CheckArgs_TypeName(float net_type, float net_name)
+{
+       // 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(INFO)
+               CHECKARG_TYPENAME(CENTER)
+               CHECKARG_TYPENAME(MULTI)
+               default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
+       }
+       #undef CHECKARG_TYPENAME
+       return checkargs;
+}
+
+#ifdef SVQC
+string Notification_CheckArgs(
+       float broadcast, entity client,
+       float net_type, float net_name)
+{
+       // 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
 
 // ===============================
 
 // ===============================
-//  Frontend Notification Pushing
+//  Initialization Core Functions
 // ===============================
 
 // ===============================
 
+// used by restartnotifs command to initialize notifications
+void Destroy_Notification_Entity(entity notif)
+{
+       if(notif.nent_name != "") { strunzone(notif.nent_name); }
+       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);
+}
+
+void Destroy_All_Notifications(void)
+{
+       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
+       #ifdef SVQC
+       Kill_Notification(NOTIF_ALL, world, 0, 0);
+       #endif
+
+       // kill all real notification entities
+       DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
+       DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
+       DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
+       #undef DESTROY_LOOP
+}
+
+string Process_Notif_Line(
+       float msg_is_info,
+       float chat,
+       string input,
+       string notiftype,
+       string notifname,
+       string stringtype)
+{
+       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;
+}
+
+string Process_Notif_Args(
+       float arg_type,
+       string args,
+       string notiftype,
+       string notifname)
+{
+       string selected, remaining = args;
+       float sel_num = 0;
+
+       for(;(remaining != "");)
+       {
+               selected = car(remaining); remaining = cdr(remaining);
+
+               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 infoname,
+       float centername,
+       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)
+{
+       // =====================
+       //  Global Entity Setup
+       // =====================
+       entity notif = spawn();
+       string typestring = "";
+       switch(typeid)
+       {
+               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;
+               }
+
+               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((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
+               {
+                       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
+       {
+               // 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_CPID) { 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); } }
 void Dump_Notifications(float fh, float alsoprint)
 {
        #define NOTIF_WRITE(a) { \
                fputs(fh, a); \
                if(alsoprint) { print(a); } }
-       #define NOTIF_WRITE_SETA(name,default,text) { \
+       #define NOTIF_WRITE_ENTITY(name,default,description) { \
+               notif_msg = \
+                       sprintf( \
+                               "seta notification_%s \"%d\" \"%s\"\n", \
+                               name, default, description \
+                       ); \
+               NOTIF_WRITE(notif_msg) }
+       #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
                notif_msg = \
                        sprintf( \
                notif_msg = \
                        sprintf( \
-                               "seta notification_%s %d \"notif string: %s^7\"\n", \
-                               name, default, strreplace("\{3}", "", strreplace("\n", "\\n", text)) \
+                               "seta notification_%s \"%s\" \"%s\"\n", \
+                               cvar, default, description \
                        ); \
                NOTIF_WRITE(notif_msg) }
 
                        ); \
                NOTIF_WRITE(notif_msg) }
 
@@ -66,7 +656,7 @@ void Dump_Notifications(float fh, float alsoprint)
        {
                e = Get_Notif_Ent(MSG_INFO, i);
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
        {
                e = Get_Notif_Ent(MSG_INFO, i);
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
-               NOTIF_WRITE_SETA(e.nent_name, e.nent_default, e.nent_string);
+               NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)");
        }
 
        NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
        }
 
        NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
@@ -74,50 +664,55 @@ void Dump_Notifications(float fh, float alsoprint)
        {
                e = Get_Notif_Ent(MSG_CENTER, i);
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
        {
                e = Get_Notif_Ent(MSG_CENTER, i);
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
-               NOTIF_WRITE_SETA(e.nent_name, e.nent_default, e.nent_string);
+               NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = centerprint");
        }
 
        }
 
-       NOTIF_WRITE(sprintf("\n// MSG_WEAPON notifications (count = %d):\n", NOTIF_WEAPON_COUNT));
-       for(i = 1; i <= NOTIF_WEAPON_COUNT; ++i)
+       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_WEAPON, i);
+               e = Get_Notif_Ent(MSG_MULTI, i);
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
                if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
-               NOTIF_WRITE_SETA(e.nent_name, e.nent_default, sprintf("infoname: %s, centername: %s",
-                       e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name));
-       }
-
-       NOTIF_WRITE(sprintf("\n// MSG_DEATH notifications (count = %d):\n", NOTIF_DEATH_COUNT));
-       for(i = 1; i <= NOTIF_DEATH_COUNT; ++i)
-       {
-               e = Get_Notif_Ent(MSG_DEATH, i);
-               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
-               NOTIF_WRITE_SETA(e.nent_name, e.nent_default, sprintf("infoname: %s, centername: %s",
-                       e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name));
+               NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "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");
        }
 
        // edit these to match whichever cvars are used for specific notification options
        NOTIF_WRITE("\n// HARD CODED notification variables:\n");
-       NOTIF_WRITE("seta notification_allow_chatboxprint 1 \"Allow notifications to be printed to chat box by setting notification cvar to 2 (You can also set this cvar to 2 to force ALL notifications to be printed to the chatbox)\"\n");
-       NOTIF_WRITE("seta notification_show_location 0 \"Append location information to MSG_INFO death/kill messages\"\n");
-       NOTIF_WRITE("seta notification_show_location_string \"\" \"Replacement string piped into sprintf, so you can do different messages like this: ' at the %s' or ' (near %s)'\"\n");
-       NOTIF_WRITE("seta notification_show_sprees 1 \"Print information about sprees in death/kill messages\"\n");
-       NOTIF_WRITE("seta notification_show_sprees_center 1 \"Show spree information in MSG_CENTER messages... 0 = off, 1 = target (but only for first victim) and attacker\"\n");
-       NOTIF_WRITE("seta notification_show_sprees_center_specialonly 1 \"Don't show spree information in MSG_CENTER messages if it isn't an achievement\"\n");
-       NOTIF_WRITE("seta notification_show_sprees_info 3 \"Show spree information in MSG_INFO messages... 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker\"\n");
-       NOTIF_WRITE("seta notification_show_sprees_info_newline 0 \"Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself\"\n");
-       NOTIF_WRITE("seta notification_show_sprees_info_specialonly 1 \"Don't show attacker spree information in MSG_INFO messages if it isn't an achievement\"\n");
-       NOTIF_WRITE("seta notification_errors_are_fatal 1 \"If a notification fails upon initialization, cause a Host_Error to stop the program\"\n");
-       NOTIF_WRITE("seta notification_ctf_pickup_team_verbose 1 \"Show extra information if a team mate picks up a flag\"\n");
-       NOTIF_WRITE("seta notification_ctf_pickup_enemy_verbose 1 \"Show extra information if an enemy picks up a flag\"\n");
-       NOTIF_WRITE("seta notification_ctf_capture_verbose 1 \"Show extra information when someone captures a flag\"\n");
-       NOTIF_WRITE("seta notification_frag_verbose 1 \"Show extra information when you frag someone (or when you are fragged\"\n");
-
-       NOTIF_WRITE(sprintf("\n// Notification counts (total = %d): MSG_INFO = %d, MSG_CENTER = %d, MSG_WEAPON = %d, MSG_DEATH = %d\n",
-               (NOTIF_INFO_COUNT + NOTIF_CENTER_COUNT + NOTIF_WEAPON_COUNT + NOTIF_DEATH_COUNT), 
-               NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_WEAPON_COUNT, NOTIF_DEATH_COUNT));
+       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("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");
+       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("errors_are_fatal",                                       "1",    "If a notification fails upon initialization, cause a Host_Error to stop the program");
+       NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose",                        "0",    "Show extra information if a team mate picks up a flag");
+       NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose",                       "0",    "Show extra information if an enemy picks up a flag");
+       NOTIF_WRITE_HARDCODED("ctf_capture_verbose",                            "0",    "Show extra information when someone captures a flag");
+       NOTIF_WRITE_HARDCODED("frag_verbose",                                           "1",    "Show extra information when you frag someone (or when you are fragged");
+       NOTIF_WRITE_HARDCODED("lifetime_runtime",                                       "0.5",  "Amount of time that notification entities last on the server during runtime (In seconds)");
+       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(sprintf(
+               strcat(
+                       "\n// Notification counts (total = %d): ",
+                       "MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
+               ),
+               (
+                       NOTIF_INFO_COUNT +
+                       NOTIF_CENTER_COUNT +
+                       NOTIF_MULTI_COUNT
+               ), 
+               NOTIF_INFO_COUNT,
+               NOTIF_CENTER_COUNT,
+               NOTIF_MULTI_COUNT
+       ));
        
        return;
        
        return;
-       #undef NOTIF_WRITE_SETA
+       #undef NOTIF_WRITE_HARDCODED
+       #undef NOTIF_WRITE_ENTITY
        #undef NOTIF_WRITE
 }
 
        #undef NOTIF_WRITE
 }
 
@@ -128,19 +723,24 @@ void Notification_GetCvars()
 }
 #endif
 
 }
 #endif
 
-string Local_Notification_sprintf(string input, string args, 
+
+// ===============================
+//  Frontend Notification Pushing
+// ===============================
+
+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^7', '%s', %s, %s);\n",
-                       strreplace("\n", "\\n", input),
-                       args,
-                       sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
-                       sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
-               )
-       );
+       dprint(sprintf(
+               "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
+               strreplace("\n", "\\n", input),
+               args,
+               strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
        #endif
        
        string selected;
        #endif
        
        string selected;
@@ -157,11 +757,11 @@ string Local_Notification_sprintf(string input, string args,
                {
                        #define ARG_CASE(prog,selected,result) \
                                #ifdef CSQC \
                {
                        #define ARG_CASE(prog,selected,result) \
                                #ifdef CSQC \
-                                       #if (prog != ARG_SV) \
+                                       #if (prog != ARG_SV) && (prog != ARG_DC) \
                                                case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                        #endif \
                                #else \
                                                case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                        #endif \
                                #else \
-                                       #if (prog != ARG_CS) \
+                                       #if (prog != ARG_CS) && (prog != ARG_DC) \
                                                case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                        #endif \
                                #endif
                                                case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                        #endif \
                                #endif
@@ -174,7 +774,9 @@ string Local_Notification_sprintf(string input, string args,
 }
 
 #ifdef CSQC
 }
 
 #ifdef CSQC
-void Local_Notification_HUD_Notify_Push(string icon, string hudargs, string s1, string s2, string s3, string s4)
+void Local_Notification_HUD_Notify_Push(
+       string icon, string hudargs,
+       string s1, string s2, string s3, string s4)
 {
        string selected;
        float sel_num;
 {
        string selected;
        float sel_num;
@@ -195,10 +797,21 @@ void Local_Notification_HUD_Notify_Push(string icon, string hudargs, string s1,
                        default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
                }
        }
                        default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
                }
        }
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(sprintf(
+               "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
+               icon,
+               hudargs,
+               strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               strreplace("\n", "\\n", sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
+       ));
+       #endif
        HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
 }
 
        HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
 }
 
-void Local_Notification_centerprint_generic(string input, string durcnt, float cpid, float f1, float f2)
+void Local_Notification_centerprint_generic(
+       string input, string durcnt,
+       float cpid, float f1, float f2)
 {
        string selected;
        float sel_num;
 {
        string selected;
        float sel_num;
@@ -211,7 +824,7 @@ void Local_Notification_centerprint_generic(string input, string durcnt, float c
                switch(strtolower(selected))
                {
                        #define ARG_CASE(prog,selected,result) \
                switch(strtolower(selected))
                {
                        #define ARG_CASE(prog,selected,result) \
-                               #if (prog == ARG_CS_SV_DC) \
+                               #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
                                        case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                #endif
                        NOTIF_ARGUMENT_LIST
                                        case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                #endif
                        NOTIF_ARGUMENT_LIST
@@ -224,6 +837,15 @@ void Local_Notification_centerprint_generic(string input, string durcnt, float c
                        }
                }
        }
                        }
                }
        }
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(sprintf(
+               "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
+               strreplace("\n", "\\n", 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
        centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
 }
 #endif
@@ -231,45 +853,48 @@ void Local_Notification_centerprint_generic(string input, string durcnt, float c
 void Local_Notification(float net_type, float net_name, ...count)
 {
        // check supplied type and name for errors
 void Local_Notification(float net_type, float net_name, ...count)
 {
        // 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(INFO)
-               CHECKARG_TYPENAME(CENTER)
-               CHECKARG_TYPENAME(WEAPON)
-               CHECKARG_TYPENAME(DEATH)
-               default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
-       }
-       #undef CHECKARG_TYPENAME
+       string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
        if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
 
        entity notif = Get_Notif_Ent(net_type, net_name);
        if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
        if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); 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) { print("Local_Notification: Entity was disabled...\n"); return; }
 
 
+       #ifdef NOTIFICATIONS_DEBUG
+       if not(notif.nent_enabled)
+       {
+               dprint(sprintf(
+                       "Local_Notification(%s, %s): Entity was disabled...\n",
+                       Get_Notif_TypeName(net_type),
+                       notif.nent_name
+               ));
+               return;
+       }
+       #endif
+       
        if((notif.nent_stringcount + notif.nent_floatcount) > count)
        {
                backtrace(sprintf(
                        strcat(
        if((notif.nent_stringcount + notif.nent_floatcount) > count)
        {
                backtrace(sprintf(
                        strcat(
-                               "Not enough arguments for Local_Notification(%d, %s, ...)! ",
+                               "Not enough arguments for Local_Notification(%s, %s, ...)! ",
                                "stringcount(%d) + floatcount(%d) > count(%d)\n", 
                                "Check the definition and function call for accuracy...?\n"
                        ),
                                "stringcount(%d) + floatcount(%d) > count(%d)\n", 
                                "Check the definition and function call for accuracy...?\n"
                        ),
-                       net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
+                       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(
                return;
        }
        else if((notif.nent_stringcount + notif.nent_floatcount) < count)
        {
                backtrace(sprintf(
                        strcat(
-                               "Too many arguments for Local_Notification(%d, %s, ...)! ",
+                               "Too many arguments for Local_Notification(%s, %s, ...)! ",
                                "stringcount(%d) + floatcount(%d) < count(%d)\n",
                                "Check the definition and function call for accuracy...?\n"
                        ),
                                "stringcount(%d) + floatcount(%d) < count(%d)\n",
                                "Check the definition and function call for accuracy...?\n"
                        ),
-                       net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
+                       Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
                return;
        }
 
                return;
        }
 
@@ -283,14 +908,13 @@ void Local_Notification(float net_type, float net_name, ...count)
        float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
 
        #ifdef NOTIFICATIONS_DEBUG
        float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
 
        #ifdef NOTIFICATIONS_DEBUG
-       dprint(
-               sprintf("Local_Notification(%d, %s, %s, %s);\n",
-                       net_type,
-                       notif.nent_name,
-                       sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
-                       sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
-               )
-       );
+       dprint(sprintf(
+               "Local_Notification(%s, %s, %s, %s);\n",
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               strreplace("\n", "\\n", 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)
@@ -332,8 +956,7 @@ void Local_Notification(float net_type, float net_name, ...count)
                }
                #endif
                
                }
                #endif
                
-               case MSG_WEAPON:
-               case MSG_DEATH:
+               case MSG_MULTI:
                {
                        if(notif.nent_msginfo)
                        if(notif.nent_msginfo.nent_enabled)
                {
                        if(notif.nent_msginfo)
                        if(notif.nent_msginfo.nent_enabled)
@@ -365,7 +988,8 @@ void Local_Notification(float net_type, float net_name, ...count)
 }
 
 // WOVA = Without Variable Arguments 
 }
 
 // WOVA = Without Variable Arguments 
-void Local_Notification_WOVA(float net_type, float net_name,
+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)
@@ -389,30 +1013,71 @@ void Read_Notification(float is_new)
        float net_type = ReadByte();
        float net_name = ReadShort();
 
        float net_type = ReadByte();
        float net_name = ReadShort();
 
-       entity notif = Get_Notif_Ent(net_type, net_name);
-       if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
+       entity notif;
 
 
-       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(net_type == MSG_CENTER_CPID)
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               dprint(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
+                       {
+                               print(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, notif.nent_name));
-       #endif
+               #ifdef NOTIFICATIONS_DEBUG
+               dprint(sprintf(
+                       "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
+                       is_new,
+                       time,
+                       Get_Notif_TypeName(net_type),
+                       notif.nent_name
+               ));
+               #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_WOVA(
-                       net_type, net_name,
-                       notif.nent_stringcount,
-                       notif.nent_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
        }
 }
 #endif
@@ -420,6 +1085,18 @@ void Read_Notification(float is_new)
 #ifdef SVQC
 void Net_Notification_Remove()
 {
 #ifdef SVQC
 void Net_Notification_Remove()
 {
+       if not(self) { dprint(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
+       
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(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);
@@ -483,12 +1160,12 @@ float Net_Write_Notification(entity client, float sf)
                        ) { send = TRUE; }
                        break;
                }
                        ) { send = TRUE; }
                        break;
                }
-               case NOTIF_ANY: // send to everyone
+               case NOTIF_ALL: // send to everyone
                {
                        send = TRUE;
                        break;
                }
                {
                        send = TRUE;
                        break;
                }
-               case NOTIF_ANY_EXCEPT: // send to everyone except X person and their spectators
+               case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
                {
                        if(
                                (client != self.nent_client)
                {
                        if(
                                (client != self.nent_client)
@@ -516,62 +1193,102 @@ float Net_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 = "";
-       #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 = Notification_CheckArgs(broadcast, client, 1, 1);
+       if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
+
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(sprintf(
+               "Kill_Notification(%d, '%s', %d, %d);\n",
+               broadcast,
+               client.netname,
+               net_type,
+               net_name
+       ));
+       #endif
+
+       entity notif, net_notif;
+       float killed_cpid = NO_CPID;
+       
        switch(net_type)
        {
        switch(net_type)
        {
-               CHECKARG_TYPENAME(INFO)
-               CHECKARG_TYPENAME(CENTER)
-               CHECKARG_TYPENAME(WEAPON)
-               CHECKARG_TYPENAME(DEATH)
-               default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
-       }
-       #undef CHECKARG_TYPENAME
-       if(checkargs != "") { checkargs = strcat(checkargs, " "); }
-       switch(broadcast)
-       {
-               case NOTIF_ONE:
-               case NOTIF_ONE_ONLY:
+               case 0:
                {
                {
-                       if(IS_NOT_A_CLIENT(client))
-                               { checkargs = sprintf("%sNo client provided!", checkargs); }
+                       killed_cpid = 0; // kill ALL centerprints
                        break;
                }
                
                        break;
                }
                
-               case NOTIF_ANY_EXCEPT:
+               case MSG_CENTER:
                {
                {
-                       if(IS_NOT_A_CLIENT(client))
-                               { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
+                       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; }
+                               killed_cpid = notif.nent_cpid;
+                       }
+                       else
+                       {
+                               killed_cpid = 0; // kill ALL centerprints
+                       }
                        break;
                }
                        break;
                }
-               
-               case NOTIF_ANY:
+
+               case MSG_CENTER_CPID:
                {
                {
-                       if(client)
-                               { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
+                       killed_cpid = net_name;
                        break;
                }
                        break;
                }
-               
-               case NOTIF_TEAM:
-               case NOTIF_TEAM_EXCEPT:
+       }
+
+       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 not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
-                       else if(IS_NOT_A_CLIENT(client))
+                       if(killed_cpid != NO_CPID)
                        {
                        {
-                               if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
-                               else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
+                               if(notif.owner.nent_cpid == killed_cpid)
+                               {
+                                       notif.nent_net_name = -1;
+                                       notif.nextthink = time;
+                               }
+                               else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
                        }
                        }
-                       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
                }
                }
-               
-               default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
+               else { notif.nent_net_name = -1; notif.nextthink = time; }
        }
        }
+}
+
+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
        if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
 
        // retreive counts for the arguments of this notification
@@ -582,22 +1299,26 @@ void Send_Notification(float broadcast, entity client,
        {
                backtrace(sprintf(
                        strcat(
        {
                backtrace(sprintf(
                        strcat(
-                               "Not enough arguments for Send_Notification(%d, %d, %s, ...)! ",
+                               "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"
                        ),
                                "stringcount(%d) + floatcount(%d) > count(%d)\n", 
                                "Check the definition and function call for accuracy...?\n"
                        ),
-                       broadcast, net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
+                       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(
                return;
        }
        else if((notif.nent_stringcount + notif.nent_floatcount) < count)
        {
                backtrace(sprintf(
                        strcat(
-                               "Too many arguments for Send_Notification(%d, %d, %s, ...)! ",
+                               "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"
                        ),
                                "stringcount(%d) + floatcount(%d) < count(%d)\n",
                                "Check the definition and function call for accuracy...?\n"
                        ),
-                       broadcast, net_type, notif.nent_name, notif.nent_stringcount, notif.nent_floatcount, count));
+                       broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
                return;
        }
 
                return;
        }
 
@@ -610,18 +1331,19 @@ void Send_Notification(float broadcast, entity client,
        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);
        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);
-       dprint(
-               sprintf("Send_Notification(%d, %d, %s, %s, %s);\n",
-                       broadcast,
-                       net_type,
-                       notif.nent_name,
-                       sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
-                       sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
-               )
-       );
+       dprint(sprintf(
+               "Send_Notification(%d, %s, %s, %s, %s);\n",
+               broadcast,
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
+       ));
        #endif
 
        entity net_notif = spawn();
        #endif
 
        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_broadcast = broadcast;
        net_notif.nent_client = client;
        net_notif.nent_net_type = net_type;
@@ -632,13 +1354,19 @@ void Send_Notification(float broadcast, entity client,
        float i;
        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); }
        float i;
        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.think = Net_Notification_Remove;
-       net_notif.nextthink = (time + 0.5); 
+       net_notif.nextthink =
+               ((time > autocvar_notification_lifetime_mapload)
+               ?
+                       (time + autocvar_notification_lifetime_runtime)
+                       :
+                       autocvar_notification_lifetime_mapload
+               ); 
 
        Net_LinkEntity(net_notif, FALSE, 0, Net_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))
+       if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_CENTER))
        {
                Local_Notification_WOVA(
                        net_type, net_name,
        {
                Local_Notification_WOVA(
                        net_type, net_name,
@@ -650,12 +1378,26 @@ void Send_Notification(float broadcast, entity client,
 }
 
 // WOVA = Without Variable Arguments 
 }
 
 // WOVA = Without Variable Arguments 
-void Send_Notification_WOVA(float broadcast, entity client,
+void Send_Notification_WOVA(
+       float broadcast, entity client,
        float net_type, float net_name,
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
        entity notif = Get_Notif_Ent(net_type, net_name);
        float net_type, float net_name,
        string s1, string s2, string s3, string s4,
        float f1, float f2, float f3, float f4)
 {
        entity notif = Get_Notif_Ent(net_type, net_name);
+       
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(sprintf(
+               "Send_Notification_WOVA(%d, %s, %s, %s, %s - %d %d);\n",
+               broadcast,
+               Get_Notif_TypeName(net_type),
+               notif.nent_name,
+               sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
+               notif.nent_stringcount, notif.nent_floatcount
+       ));
+       #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; }
        #define VARITEM(stringc,floatc,args) \
                if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
                        { Send_Notification(broadcast, client, net_type, net_name, args); return; }
@@ -663,28 +1405,4 @@ void Send_Notification_WOVA(float broadcast, entity client,
        #undef VARITEM
        Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
 }
        #undef VARITEM
        Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
 }
-
-
-// =============================
-//  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);
-                       }
-               });
-       }
-}
 #endif // ifdef SVQC
 #endif // ifdef SVQC