]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/notifications.qc
Rename notif_any to notif_all
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
index bc346965af44b540b029e7e4d838c686240b4f69..3291c48c3b42028b81963ab5084a7cf48a7228c7 100644 (file)
@@ -9,11 +9,10 @@ string Get_Notif_TypeName(float net_type)
        {
                case MSG_INFO: return "MSG_INFO";
                case MSG_CENTER: return "MSG_CENTER";
-               case MSG_WEAPON: return "MSG_WEAPON";
-               case MSG_DEATH: return "MSG_DEATH";
+               case MSG_MULTI: return "MSG_MULTI";
        }
        backtrace(sprintf("Get_Notif_TypeName(%d): Improper net type!\n", net_type));
-       return "your ass";
+       return "";
 }
 
 entity Get_Notif_Ent(float net_type, float net_name)
@@ -22,8 +21,7 @@ entity Get_Notif_Ent(float net_type, float net_name)
        {
                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;
@@ -40,8 +38,7 @@ string Notification_CheckArgs_TypeName(float net_type, float net_name)
        {
                CHECKARG_TYPENAME(INFO)
                CHECKARG_TYPENAME(CENTER)
-               CHECKARG_TYPENAME(WEAPON)
-               CHECKARG_TYPENAME(DEATH)
+               CHECKARG_TYPENAME(MULTI)
                default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
        }
        #undef CHECKARG_TYPENAME
@@ -49,7 +46,9 @@ string Notification_CheckArgs_TypeName(float net_type, float net_name)
 }
 
 #ifdef SVQC
-string Notification_CheckArgs(float broadcast, entity client, float net_type, float net_name)
+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);
@@ -64,14 +63,14 @@ string Notification_CheckArgs(float broadcast, entity client, float net_type, fl
                        break;
                }
                
-               case NOTIF_ANY_EXCEPT:
+               case NOTIF_ALL_EXCEPT:
                {
                        if(IS_NOT_A_CLIENT(client))
                                { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
                        break;
                }
                
-               case NOTIF_ANY:
+               case NOTIF_ALL:
                {
                        if(client)
                                { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
@@ -97,19 +96,528 @@ string Notification_CheckArgs(float broadcast, entity client, float net_type, fl
 #endif
 
 // ===============================
-//  Frontend Notification Pushing
+//  Initialization Core Functions
 // ===============================
 
+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_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
+       }
+}
+
+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
+}
+
+
+// =========================================
+//  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_SETA(name,default,text) { \
+       #define NOTIF_WRITE_ENTITY(name,default,description) { \
                notif_msg = \
                        sprintf( \
-                               "seta notification_%s %d \"notif string: %s^7\"\n", \
-                               name, default, strreplace("\{3}", "", strreplace("\n", "\\n", text)) \
+                               "seta notification_%s \"%d\" \"%s\"\n", \
+                               name, 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) }
 
@@ -145,7 +653,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; }
-               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));
@@ -153,52 +661,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; }
-               NOTIF_WRITE_SETA(e.nent_name, e.nent_default, e.nent_string);
-       }
-
-       NOTIF_WRITE(sprintf("\n// MSG_WEAPON notifications (count = %d):\n", NOTIF_WEAPON_COUNT));
-       for(i = 1; i <= NOTIF_WEAPON_COUNT; ++i)
-       {
-               e = Get_Notif_Ent(MSG_WEAPON, 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 = centerprint");
        }
 
-       NOTIF_WRITE(sprintf("\n// MSG_DEATH notifications (count = %d):\n", NOTIF_DEATH_COUNT));
-       for(i = 1; i <= NOTIF_DEATH_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_DEATH, i);
+               e = Get_Notif_Ent(MSG_MULTI, 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");
-       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("seta notification_lifetime_runtime 0.5 \"Amount of time that notification entities last on the server during runtime (In seconds)\"\n");
-       NOTIF_WRITE("seta notification_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)\"\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;
-       #undef NOTIF_WRITE_SETA
+       #undef NOTIF_WRITE_HARDCODED
+       #undef NOTIF_WRITE_ENTITY
        #undef NOTIF_WRITE
 }
 
@@ -209,19 +720,24 @@ void Notification_GetCvars()
 }
 #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
-       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;
@@ -238,11 +754,11 @@ string Local_Notification_sprintf(string input, string args,
                {
                        #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 \
-                                       #if (prog != ARG_CS) \
+                                       #if (prog != ARG_CS) && (prog != ARG_DC) \
                                                case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
                                        #endif \
                                #endif
@@ -255,7 +771,9 @@ string Local_Notification_sprintf(string input, string args,
 }
 
 #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;
@@ -276,10 +794,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")
                }
        }
+       #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]);
 }
 
-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;
@@ -292,7 +821,7 @@ void Local_Notification_centerprint_generic(string input, string durcnt, float c
                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
@@ -305,6 +834,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
@@ -317,8 +855,19 @@ void Local_Notification(float net_type, float net_name, ...count)
 
        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) { dprint(sprintf("Local_Notification(%s, %s): Entity was disabled...\n", Get_Notif_TypeName(net_type), notif.nent_name)); 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(
@@ -327,7 +876,9 @@ void Local_Notification(float net_type, float net_name, ...count)
                                "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));
+                       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)
@@ -338,7 +889,9 @@ void Local_Notification(float net_type, float net_name, ...count)
                                "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));
+                       Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
                return;
        }
 
@@ -352,14 +905,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
-       dprint(
-               sprintf("Local_Notification(%s, %s, %s, %s);\n",
-                       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)
-               )
-       );
+       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)
@@ -401,8 +953,7 @@ void Local_Notification(float net_type, float net_name, ...count)
                }
                #endif
                
-               case MSG_WEAPON:
-               case MSG_DEATH:
+               case MSG_MULTI:
                {
                        if(notif.nent_msginfo)
                        if(notif.nent_msginfo.nent_enabled)
@@ -434,7 +985,8 @@ void Local_Notification(float net_type, float net_name, ...count)
 }
 
 // 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)
@@ -458,6 +1010,8 @@ void Read_Notification(float is_new)
        float net_type = ReadByte();
        float net_name = ReadShort();
 
+       entity notif;
+
        if(net_type == MSG_CENTER_KILL)
        {
                if(is_new)
@@ -465,19 +1019,25 @@ void Read_Notification(float is_new)
                        if(net_name == 0) { reset_centerprint_messages(); }
                        else
                        {
-                               entity notif = Get_Notif_Ent(MSG_CENTER, net_name);
-                               if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
+                               notif = Get_Notif_Ent(MSG_CENTER, net_name);
+                               if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
                                centerprint_generic(notif.nent_cpid, "", 0, 0);
                        }
                }
        }
        else
        {
-               entity notif = Get_Notif_Ent(net_type, net_name);
-               if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
+               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_type = %s, net_name = %s\n", is_new, time, Get_Notif_TypeName(net_type), notif.nent_name));
+               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() : "");
@@ -505,6 +1065,33 @@ void Read_Notification(float is_new)
 #ifdef SVQC
 void Net_Notification_Remove()
 {
+       #ifdef NOTIFICATIONS_DEBUG
+       if not(self) { dprint(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
+       if(self.nent_net_name == -1)
+       {
+               dprint(sprintf(
+                       "Net_Notification_Remove() at %f: Killed '%s' notification\n",
+                       time,
+                       Get_Notif_TypeName(self.nent_net_type)
+               ));
+       }
+       else
+       #endif
+       {
+               string checkargs = Notification_CheckArgs_TypeName(self.nent_net_type, self.nent_net_name);
+               if(checkargs != "") { dprint(sprintf("Incorrect usage of Net_Notification_Remove() at %f: %s\n", time, checkargs)); return; }
+
+               #ifdef NOTIFICATIONS_DEBUG
+               entity realent = Get_Notif_Ent(self.nent_net_type, self.nent_net_name);
+               dprint(sprintf(
+                       "Net_Notification_Remove() at %f: Removed '%s - %s' notification\n",
+                       time,
+                       Get_Notif_TypeName(self.nent_net_type), 
+                       realent.nent_name
+               ));
+               #endif
+       }
+       
        float i;
        for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
        remove(self);
@@ -568,12 +1155,12 @@ float Net_Write_Notification(entity client, float sf)
                        ) { send = TRUE; }
                        break;
                }
-               case NOTIF_ANY: // send to everyone
+               case NOTIF_ALL: // send to everyone
                {
                        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)
@@ -601,43 +1188,61 @@ float Net_Write_Notification(entity client, float sf)
        return send; 
 }
 
-void Kill_Notification(float broadcast, entity client, float net_type, float net_name)
+void Kill_Notification(
+       float broadcast, entity client,
+       float net_type, float net_name)
 {
-       string checkargs = Notification_CheckArgs(broadcast, client, net_type, 1);
+       string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
        if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
 
-       entity notif;
+       #ifdef NOTIFICATIONS_DEBUG
+       dprint(sprintf(
+               "Kill_Notification(%d, '%s', %d, %d);\n",
+               broadcast,
+               client.netname,
+               net_type,
+               net_name
+       ));
+       #endif
 
-       // if this is a centerprint, we must tell the client
-       // to kill the cpid in the centerprint queue
+       entity notif, net_notif;
+
+       // if no name is provided, just kill ALL the centerprint notifications
        if(net_type == MSG_CENTER)
        {
-               notif = spawn();
-               notif.classname = "net_kill_notification";
-               notif.nent_broadcast = broadcast;
-               notif.nent_client = client;
-               notif.nent_net_type = MSG_CENTER_KILL;
-               notif.nent_net_name = net_name;
-               Net_LinkEntity(notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
+               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_KILL;
+               net_notif.nent_net_name = net_name;
+               Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
        }
 
        for(notif = world; (notif = find(notif, classname, "net_notification"));)
        {
                // now kill the old send notification entity
-               if(notif.nent_net_type == net_type)
+               if(net_type)
                {
-                       if(net_name)
-                               { if(notif.nent_net_name == net_name) { notif.think(); } }
-                       else
-                               { notif.think(); }
-                               
-                       print(sprintf("killed '%s'\n", notif.classname));
+                       if(notif.nent_net_type == net_type)
+                       {
+                               if(net_name)
+                               {
+                                       if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.think(); }
+                                       else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
+                               }
+                               else { notif.nent_net_name = -1; notif.think(); }
+                       }
+                       else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
                }
+               else { notif.nent_net_name = -1; notif.think(); }
        }
 }
 
-void Send_Notification(float broadcast, entity client,
-       float net_type, float net_name, ...count)
+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);
@@ -655,7 +1260,9 @@ void Send_Notification(float broadcast, entity client,
                                "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));
+                       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)
@@ -666,7 +1273,9 @@ void Send_Notification(float broadcast, entity client,
                                "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));
+                       broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
+                       notif.nent_stringcount, notif.nent_floatcount, count
+               ));
                return;
        }
 
@@ -679,16 +1288,14 @@ 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);
-       dprint(
-               sprintf("Send_Notification(%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
-               )
-       );
+       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();
@@ -715,7 +1322,7 @@ void Send_Notification(float broadcast, entity client,
 
        Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
 
-       if(server_is_dedicated && (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,
@@ -727,7 +1334,8 @@ void Send_Notification(float broadcast, entity client,
 }
 
 // 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)
@@ -735,16 +1343,15 @@ void Send_Notification_WOVA(float broadcast, entity client,
        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
-               )
-       );
+       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) \