]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/notifications.qc
Merge remote-tracking branch 'origin/master' into terencehill/ca_arena_mutators
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
index 75a25b1033b64e80471e253382415137ab59ca07..b72380cceec19db94a27e807bb0f869e91f551eb 100644 (file)
@@ -1,29 +1,30 @@
 // ================================================
 //  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_ANNCE: return "MSG_ANNCE";
                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_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 "your ass";
+       return "";
 }
 
 entity Get_Notif_Ent(float net_type, float net_name)
 {
        switch(net_type)
        {
+               case MSG_ANNCE: return msg_annce_notifs[net_name - 1];
                case MSG_INFO: return msg_info_notifs[net_name - 1];
                case MSG_CENTER: return msg_center_notifs[net_name - 1];
-               case MSG_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;
@@ -38,10 +39,10 @@ string Notification_CheckArgs_TypeName(float net_type, float net_name)
                { checkargs = sprintf("Improper name: %d!", net_name); } break; }
        switch(net_type)
        {
+               CHECKARG_TYPENAME(ANNCE)
                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 +50,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 +67,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); }
@@ -100,6 +103,47 @@ string Notification_CheckArgs(float broadcast, entity client, float net_type, fl
 //  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_snd != "") { strunzone(notif.nent_snd); }
+       if(notif.nent_args != "") { strunzone(notif.nent_args); }
+       if(notif.nent_hudargs != "") { strunzone(notif.nent_hudargs); }
+       if(notif.nent_icon != "") { strunzone(notif.nent_icon); }
+       if(notif.nent_durcnt != "") { strunzone(notif.nent_durcnt); }
+       if(notif.nent_string != "") { strunzone(notif.nent_string); }
+       remove(notif);
+}
+
+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 and centerprints
+       #ifdef SVQC
+       Kill_Notification(NOTIF_ALL, world, 0, 0);
+       #else
+       reset_centerprint_messages();
+       #endif
+
+       // kill all real notification entities
+       DESTROY_LOOP(MSG_ANNCE, NOTIF_ANNCE_COUNT)
+       DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
+       DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
+       DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
+       #undef DESTROY_LOOP
+}
+
 string Process_Notif_Line(
        float msg_is_info,
        float chat,
@@ -131,7 +175,7 @@ string Process_Notif_Line(
                        print(sprintf(
                                strcat(
                                        "^1MISSING/BROKEN NEW LINE AT END OF NOTIFICATION: ",
-                                       "^7net_type = MSG_%s, net_name = %s, string = %s.\n"
+                                       "^7net_type = %s, net_name = %s, string = %s.\n"
                                ),
                                notiftype,
                                notifname,
@@ -166,7 +210,7 @@ string Process_Notif_Args(
                                        print(sprintf(
                                                strcat(
                                                        "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
-                                                       "^7net_type = MSG_%s, net_name = %s, max args = %d.\n"
+                                                       "^7net_type = %s, net_name = %s, max args = %d.\n"
                                                ),
                                                notiftype,
                                                notifname,
@@ -178,7 +222,10 @@ string Process_Notif_Args(
 
                                switch(strtolower(selected))
                                {
-                                       #define ARG_CASE(prog,selected,result) case selected: { ++sel_num; break; }
+                                       #define ARG_CASE(prog,selected,result) \
+                                               #if (prog != ARG_DC) \
+                                                       case selected: { ++sel_num; break; } \
+                                               #endif
                                        NOTIF_ARGUMENT_LIST
                                        #undef ARG_CASE
                                        default:
@@ -186,7 +233,7 @@ string Process_Notif_Args(
                                                print(sprintf(
                                                        strcat(
                                                                "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
-                                                               "^7net_type = MSG_%s, net_name = %s, args arg = '%s'.\n"
+                                                               "^7net_type = %s, net_name = %s, args arg = '%s'.\n"
                                                        ),
                                                        notiftype,
                                                        notifname,
@@ -205,7 +252,7 @@ string Process_Notif_Args(
                                        print(sprintf(
                                                strcat(
                                                        "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
-                                                       "^7net_type = MSG_%s, net_name = %s, max hudargs = %d.\n"
+                                                       "^7net_type = %s, net_name = %s, max hudargs = %d.\n"
                                                ),
                                                notiftype,
                                                notifname,
@@ -228,7 +275,7 @@ string Process_Notif_Args(
                                                print(sprintf(
                                                        strcat(
                                                                "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
-                                                               "^7net_type = MSG_%s, net_name = %s, hudargs arg = '%s'.\n"
+                                                               "^7net_type = %s, net_name = %s, hudargs arg = '%s'.\n"
                                                        ),
                                                        notiftype,
                                                        notifname,
@@ -247,7 +294,7 @@ string Process_Notif_Args(
                                        print(sprintf(
                                                strcat(
                                                        "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
-                                                       "^7net_type = MSG_%s, net_name = %s, max durcnt = %d.\n"
+                                                       "^7net_type = %s, net_name = %s, max durcnt = %d.\n"
                                                ),
                                                notiftype,
                                                notifname,
@@ -273,7 +320,7 @@ string Process_Notif_Args(
                                                        print(sprintf(
                                                                strcat(
                                                                        "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
-                                                                       "^7net_type = MSG_%s, net_name = %s, durcnt arg = '%s'.\n"
+                                                                       "^7net_type = %s, net_name = %s, durcnt arg = '%s'.\n"
                                                                ),
                                                                notiftype,
                                                                notifname,
@@ -297,8 +344,13 @@ void Create_Notification_Entity(
        float typeid,
        float nameid,
        string namestring,
+       float anncename,
        float infoname,
        float centername,
+       float channel,
+       string snd,
+       float vol,
+       float position,
        float strnum,
        float flnum,
        string args,
@@ -318,6 +370,13 @@ void Create_Notification_Entity(
        string typestring = "";
        switch(typeid)
        {
+               case MSG_ANNCE:
+               {
+                       typestring = "MSG_ANNCE";
+                       msg_annce_notifs[nameid - 1] = notif;
+                       notif.classname = "msg_annce_notification";
+                       break;
+               }
                case MSG_INFO:
                {
                        typestring = "MSG_INFO";
@@ -332,20 +391,14 @@ void Create_Notification_Entity(
                        notif.classname = "msg_center_notification";
                        break;
                }
-               case MSG_WEAPON:
-               {
-                       typestring = "MSG_WEAPON";
-                       msg_weapon_notifs[nameid - 1] = notif;
-                       notif.classname = "msg_weapon_notification";
-                       break;
-               }
-               case MSG_DEATH:
+               case MSG_MULTI:
                {
-                       typestring = "MSG_DEATH";
-                       msg_death_notifs[nameid - 1] = notif;
-                       notif.classname = "msg_death_notification";
+                       typestring = "MSG_MULTI";
+                       msg_multi_notifs[nameid - 1] = notif;
+                       notif.classname = "MSG_MULTI_notification";
                        break;
                }
+
                default:
                {
                        error(sprintf(
@@ -372,8 +425,8 @@ void Create_Notification_Entity(
        // ====================
        if(msg_is_multi)
        {
-               // Set MSG_WEAPON and MSG_DEATH string/float counts
-               if((infoname == NO_MSG) && (centername == NO_MSG))
+               // Set MSG_MULTI string/float counts
+               if((anncename == NO_MSG) && (infoname == NO_MSG) && (centername == NO_MSG))
                {
                        print(sprintf(
                                strcat(
@@ -387,6 +440,9 @@ void Create_Notification_Entity(
                }
                else
                {
+                       // announcements don't actually need any arguments, so lets not even count them.
+                       if(anncename != NO_MSG) { notif.nent_msgannce = msg_annce_notifs[anncename - 1]; }
+                       
                        float infoname_stringcount = 0, infoname_floatcount = 0;
                        float centername_stringcount = 0, centername_floatcount = 0;
                        
@@ -409,6 +465,41 @@ void Create_Notification_Entity(
                        notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
                }
        }
+       else if(typeid == MSG_ANNCE)
+       {
+               // Set MSG_ANNCE information and handle precaching
+               #ifdef CSQC
+               if not(GENTLE && (var_cvar == 1))
+               {
+                       if(snd != "")
+                       {
+                               if(notif.nent_enabled)
+                               {
+                                       precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
+                                       notif.nent_channel = channel;
+                                       notif.nent_snd = strzone(snd);
+                                       notif.nent_vol = vol;
+                                       notif.nent_position = position;
+                               }
+                       }
+                       else
+                       {
+                               print(sprintf(
+                                       strcat(
+                                               "^1NOTIFICATION WITH NO SOUND: ",
+                                               "^7net_type = %s, net_name = %s.\n"
+                                       ),
+                                       typestring,
+                                       namestring
+                               ));
+                               notif_error = TRUE;
+                       }
+               }
+               else { notif.nent_enabled = FALSE; }
+               #else
+               notif.nent_enabled = FALSE;
+               #endif
+       }
        else
        {
                // Set MSG_INFO and MSG_CENTER string/float counts
@@ -473,7 +564,7 @@ void Create_Notification_Entity(
                                        print(sprintf(
                                                strcat(
                                                        "^1NOTIFICATION HAS HUDARGS BUT NO ICON: ",
-                                                       "^7net_type = MSG_%s, net_name = %s.\n"
+                                                       "^7net_type = %s, net_name = %s.\n"
                                                ),
                                                typestring,
                                                namestring
@@ -486,7 +577,7 @@ void Create_Notification_Entity(
                                print(sprintf(
                                        strcat(
                                                "^1NOTIFICATION HAS ICON BUT NO HUDARGS: ",
-                                               "^7net_type = MSG_%s, net_name = %s.\n"
+                                               "^7net_type = %s, net_name = %s.\n"
                                        ),
                                        typestring,
                                        namestring
@@ -505,7 +596,7 @@ void Create_Notification_Entity(
                                        print(sprintf(
                                                strcat(
                                                        "^1NOTIFICATION HAS DURCNT BUT NO CPID: ",
-                                                       "^7net_type = MSG_%s, net_name = %s.\n"
+                                                       "^7net_type = %s, net_name = %s.\n"
                                                ),
                                                typestring,
                                                namestring
@@ -547,7 +638,7 @@ void Create_Notification_Entity(
                                print(sprintf(
                                        strcat(
                                                "^1EMPTY NOTIFICATION: ",
-                                               "^7net_type = MSG_%s, net_name = %s.\n"
+                                               "^7net_type = %s, net_name = %s.\n"
                                        ),
                                        typestring,
                                        namestring
@@ -565,43 +656,6 @@ void Create_Notification_Entity(
        }
 }
 
-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_ANY, 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_WEAPON, NOTIF_WEAPON_COUNT)
-       DESTROY_LOOP(MSG_DEATH, NOTIF_DEATH_COUNT)
-
-       //print("Destroyed all huma-- I mean... notifications.. successfully.\n");
-}
-
 
 // =========================================
 //  Cvar Handling With 'dumpnotifs' Command
@@ -654,6 +708,14 @@ void Dump_Notifications(float fh, float alsoprint)
        // This is not necessary, and does not matter if they vary between config versions,
        // it is just a semi-helpful tool for those who want to manually change their user settings.
 
+       NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
+       for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i)
+       {
+               e = Get_Notif_Ent(MSG_ANNCE, i);
+               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
+               NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)");
+       }
+
        NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
        for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
        {
@@ -670,45 +732,53 @@ void Dump_Notifications(float fh, float alsoprint)
                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)
-       {
-               e = Get_Notif_Ent(MSG_WEAPON, i);
-               if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
-               NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = trigger subcalls");
-       }
-
-       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_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_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",                       "0",    "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("\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("ctf_capture_verbose",             "0",    "Show extra information when someone captures a flag");
+       NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose",        "0",    "Show extra information if an enemy picks up a flag");
+       NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose",         "0",    "Show extra information if a team mate picks up a flag");
+       NOTIF_WRITE_HARDCODED("debug",                           "0",    "Print extra debug information on all notification function calls (Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... 0 = disabled, 1 = dprint, 2 = print");
+       NOTIF_WRITE_HARDCODED("errors_are_fatal",                "1",    "If a notification fails upon initialization, cause a Host_Error to stop the program");
+       NOTIF_WRITE_HARDCODED("frag_verbose",                    "1",    "Show extra information when you frag someone (or when you are fragged");
+       NOTIF_WRITE_HARDCODED("item_centerprinttime",            "1.5",  "How long to show item information centerprint messages (like 'You got the Electro' or such)");
+       NOTIF_WRITE_HARDCODED("lifetime_mapload",                "10",   "Amount of time that notification entities last immediately at mapload (in seconds) to help prevent notifications from being lost on early init (like gamestart countdown)");
+       NOTIF_WRITE_HARDCODED("lifetime_runtime",                "0.5",  "Amount of time that notification entities last on the server during runtime (In seconds)");
+       NOTIF_WRITE_HARDCODED("server_allows_frag_verbose",      "1",    "Server side cvar for showing extra information in frag messages... 0 = no extra frag information, 1 = frag information only in warmup, 2 = frag information allowed all the time");
+       NOTIF_WRITE_HARDCODED("server_allows_location",          "1",    "Server side cvar for allowing death messages to show location information too");
+       NOTIF_WRITE_HARDCODED("show_location",                   "0",    "Append location information to MSG_INFO death/kill messages");
+       NOTIF_WRITE_HARDCODED("show_location_string",            "",     "Replacement string piped into sprintf, so you can do different messages like this: ' at the %s' or ' (near %s)'");
+       NOTIF_WRITE_HARDCODED("show_sprees",                     "1",    "Print information about sprees in death/kill messages");
+       NOTIF_WRITE_HARDCODED("show_sprees_center",              "1",    "Show spree information in MSG_CENTER messages... 0 = off, 1 = target (but only for first victim) and attacker");
+       NOTIF_WRITE_HARDCODED("show_sprees_center_specialonly",  "1",    "Don't show spree information in MSG_CENTER messages if it isn't an achievement");
+       NOTIF_WRITE_HARDCODED("show_sprees_info",                "3",    "Show spree information in MSG_INFO messages... 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker");
+       NOTIF_WRITE_HARDCODED("show_sprees_info_newline",        "1",    "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself");
+       NOTIF_WRITE_HARDCODED("show_sprees_info_specialonly",    "1",    "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement");
+
+       NOTIF_WRITE(sprintf(
+               strcat(
+                       "\n// Notification counts (total = %d): ",
+                       "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
+               ),
+               (
+                       NOTIF_ANNCE_COUNT +
+                       NOTIF_INFO_COUNT +
+                       NOTIF_CENTER_COUNT +
+                       NOTIF_MULTI_COUNT
+               ),
+               NOTIF_ANNCE_COUNT,
+               NOTIF_INFO_COUNT,
+               NOTIF_CENTER_COUNT,
+               NOTIF_MULTI_COUNT
+       ));
        
        return;
        #undef NOTIF_WRITE_HARDCODED
@@ -728,16 +798,28 @@ void Notification_GetCvars()
 //  Frontend Notification Pushing
 // ===============================
 
-string Local_Notification_sprintf(string input, string args, 
+#ifdef NOTIFICATIONS_DEBUG
+void Debug_Notification(string input)
+{
+       switch(autocvar_notification_debug)
+       {
+               case 1: { dprint(input); break; }
+               case 2: { print(input); break; }
+       }
+}
+#endif
+
+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(
+       Debug_Notification(sprintf(
                "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
-               strreplace("\n", "\\n", input),
+               MakeConsoleSafe(input),
                args,
-               strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
                sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
        ));
        #endif
@@ -773,7 +855,65 @@ 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_sound(
+       float soundchannel, string soundfile,
+       float soundvolume, float soundposition)
+{
+       if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition
+               ));
+               #endif
+               
+               sound(
+                       world,
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition
+               );
+               
+               if(prev_soundfile) { strunzone(prev_soundfile); }
+               prev_soundfile = strzone(soundfile);
+               prev_soundtime = time;
+       }
+       else
+       {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Local_Notification_sound(world, %f, '%s', %f, %f) ^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', time/prevtime: %f, limit: %f\n",
+                       soundchannel,
+                       sprintf(
+                               "announcer/%s/%s.wav",
+                               autocvar_cl_announcer,
+                               soundfile
+                       ),
+                       soundvolume,
+                       soundposition,
+                       prev_soundfile,
+                       (time - prev_soundtime),
+                       autocvar_cl_announcer_antispam
+               ));
+               #endif
+       }
+}
+void Local_Notification_HUD_Notify_Push(
+       string icon, string hudargs,
+       string s1, string s2, string s3, string s4)
 {
        string selected;
        float sel_num;
@@ -795,18 +935,20 @@ void Local_Notification_HUD_Notify_Push(string icon, string hudargs, string s1,
                }
        }
        #ifdef NOTIFICATIONS_DEBUG
-       dprint(sprintf(
+       Debug_Notification(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])))
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
        ));
        #endif
        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;
@@ -833,9 +975,9 @@ void Local_Notification_centerprint_generic(string input, string durcnt, float c
                }
        }
        #ifdef NOTIFICATIONS_DEBUG
-       dprint(sprintf(
+       Debug_Notification(sprintf(
                "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
-               strreplace("\n", "\\n", input),
+               MakeConsoleSafe(input),
                durcnt,
                f1, f2,
                stof(arg_slot[0]), stof(arg_slot[1])
@@ -853,18 +995,17 @@ 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; }
-
-       #ifdef NOTIFICATIONS_DEBUG
        if not(notif.nent_enabled)
        {
-               dprint(sprintf(
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
                        "Local_Notification(%s, %s): Entity was disabled...\n",
                        Get_Notif_TypeName(net_type),
                        notif.nent_name
                ));
+               #endif
                return;
        }
-       #endif
        
        if((notif.nent_stringcount + notif.nent_floatcount) > count)
        {
@@ -903,17 +1044,32 @@ 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(
+       Debug_Notification(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)),
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
                sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
        ));
        #endif
        
        switch(net_type)
        {
+               case MSG_ANNCE:
+               {
+                       #ifdef CSQC
+                       Local_Notification_sound(
+                               notif.nent_channel,
+                               notif.nent_snd,
+                               notif.nent_vol,
+                               notif.nent_position
+                       );
+                       #else
+                       backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
+                       #endif
+                       break;
+               }
+               
                case MSG_INFO:
                {
                        print(
@@ -951,8 +1107,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)
@@ -966,6 +1121,16 @@ void Local_Notification(float net_type, float net_name, ...count)
                                        f1, f2, f3, f4);
                        }
                        #ifdef CSQC
+                       if(notif.nent_msgannce)
+                       if(notif.nent_msgannce.nent_enabled)
+                       {
+                               Local_Notification_WOVA(
+                                       MSG_ANNCE,
+                                       notif.nent_msgannce.nent_id, 
+                                       0, 0, 
+                                       "", "", "", "",
+                                       0, 0, 0, 0);
+                       }
                        if(notif.nent_msgcenter)
                        if(notif.nent_msgcenter.nent_enabled)
                        {
@@ -984,7 +1149,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)
@@ -1010,17 +1176,34 @@ void Read_Notification(float is_new)
 
        entity notif;
 
-       if(net_type == MSG_CENTER_KILL)
+       if(net_type == MSG_CENTER_CPID)
        {
+               #ifdef NOTIFICATIONS_DEBUG
+               Debug_Notification(sprintf(
+                       "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
+                       is_new,
+                       time,
+                       Get_Notif_TypeName(net_type),
+                       net_name
+               ));
+               #endif
+               
                if(is_new)
                {
                        if(net_name == 0) { reset_centerprint_messages(); }
-                       else
+                       else if(net_name != NO_CPID)
                        {
-                               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);
+                               // in this case, net_name IS the cpid we want to kill
+                               centerprint_generic(net_name, "", 0, 0);
                        }
+                       else
+                       {
+                               backtrace(sprintf(
+                                       "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
+                                       is_new,
+                                       time
+                               ));
+                       } 
                }
        }
        else
@@ -1029,7 +1212,7 @@ void Read_Notification(float is_new)
                if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
 
                #ifdef NOTIFICATIONS_DEBUG
-               dprint(sprintf(
+               Debug_Notification(sprintf(
                        "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
                        is_new,
                        time,
@@ -1063,32 +1246,17 @@ void Read_Notification(float is_new)
 #ifdef SVQC
 void Net_Notification_Remove()
 {
+       if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
+       
        #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
+       Debug_Notification(sprintf(
+               "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
+               time,
+               ((self.nent_net_name == -1) ? "Killed" : "Removed"),
+               Get_Notif_TypeName(self.nent_net_type),
+               self.owner.nent_name
+       ));
        #endif
-       {
-               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]); } }
@@ -1107,7 +1275,7 @@ float Net_Write_Notification(entity client, float sf)
                                (client == self.nent_client)
                                ||
                                (
-                                       (client.classname == STR_SPECTATOR)
+                                       IS_SPEC(client)
                                        &&
                                        (client.enemy == self.nent_client)
                                )
@@ -1125,7 +1293,7 @@ float Net_Write_Notification(entity client, float sf)
                                (client.team == self.nent_client.team)
                                ||
                                (
-                                       (client.classname == STR_SPECTATOR)
+                                       IS_SPEC(client)
                                        &&
                                        (client.enemy.team == self.nent_client.team)
                                )
@@ -1141,7 +1309,7 @@ float Net_Write_Notification(entity client, float sf)
                                        (client.team == self.nent_client.team)
                                        ||
                                        (
-                                               (client.classname == STR_SPECTATOR)
+                                               IS_SPEC(client)
                                                &&
                                                (
                                                        (client.enemy != self.nent_client)
@@ -1153,18 +1321,18 @@ 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)
                                &&
                                !(
-                                       (client.classname == STR_SPECTATOR)
+                                       IS_SPEC(client)
                                        &&
                                        (client.enemy == self.nent_client)
                                )
@@ -1186,57 +1354,103 @@ 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, 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",
+       Debug_Notification(sprintf(
+               "Kill_Notification(%d, '%s', %s, %d);\n",
                broadcast,
                client.netname,
-               net_type,
+               (net_type ? Get_Notif_TypeName(net_type) : "0"),
                net_name
        ));
        #endif
 
        entity notif, net_notif;
+       float killed_cpid = NO_CPID;
+       
+       switch(net_type)
+       {
+               case 0:
+               {
+                       killed_cpid = 0; // kill ALL centerprints
+                       break;
+               }
+               
+               case MSG_CENTER:
+               {
+                       if(net_name)
+                       {
+                               entity notif = Get_Notif_Ent(net_type, net_name);
+                               if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
+                               
+                               if(notif.nent_cpid)
+                                       killed_cpid = notif.nent_cpid;
+                               else
+                                       killed_cpid = NO_CPID;
+                       }
+                       else
+                       {
+                               killed_cpid = 0; // kill ALL centerprints
+                       }
+                       break;
+               }
 
-       // if no name is provided, just kill ALL the centerprint notifications
-       if(net_type == MSG_CENTER)
+               case MSG_CENTER_CPID:
+               {
+                       killed_cpid = net_name;
+                       break;
+               }
+       }
+
+       if(killed_cpid != NO_CPID)
        {
                net_notif = spawn();
                net_notif.classname = "net_kill_notification";
                net_notif.nent_broadcast = broadcast;
                net_notif.nent_client = client;
-               net_notif.nent_net_type = MSG_CENTER_KILL;
-               net_notif.nent_net_name = net_name;
+               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"));)
        {
-               // now kill the old send notification entity
                if(net_type)
                {
-                       if(notif.nent_net_type == net_type)
+                       if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
+                       {
+                               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
+                       }
+                       else if(notif.nent_net_type == net_type)
                        {
                                if(net_name)
                                {
-                                       if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.think(); }
+                                       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.think(); }
+                               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
                }
-               else { notif.nent_net_name = -1; notif.think(); }
+               else { notif.nent_net_name = -1; notif.nextthink = time; }
        }
 }
 
-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);
@@ -1282,17 +1496,18 @@ 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(
+       Debug_Notification(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)),
+               MakeConsoleSafe(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();
+       net_notif.owner = notif;
        net_notif.classname = "net_notification";
        net_notif.nent_broadcast = broadcast;
        net_notif.nent_client = client;
@@ -1316,7 +1531,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_ANNCE) && (net_type != MSG_CENTER))
        {
                Local_Notification_WOVA(
                        net_type, net_name,
@@ -1328,7 +1543,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)
@@ -1336,14 +1552,13 @@ 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",
+       Debug_Notification(sprintf(
+               "Send_Notification_WOVA(%d, %s, %s, %s, %s);\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
+               MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
+               sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
        ));
        #endif
        
@@ -1354,28 +1569,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
 }
-
-
-// =============================
-//  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