]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into samual/notification_rewrite
authorSamual Lenks <samual@xonotic.org>
Sun, 30 Sep 2012 22:44:39 +0000 (18:44 -0400)
committerSamual Lenks <samual@xonotic.org>
Sun, 30 Sep 2012 22:44:39 +0000 (18:44 -0400)
17 files changed:
gamemodes.cfg
qcsrc/client/Main.qc
qcsrc/client/progs.src
qcsrc/common/constants.qh
qcsrc/common/deathtypes.qh [new file with mode: 0644]
qcsrc/common/notifications.qc [new file with mode: 0644]
qcsrc/common/util.qh
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_ctf.qh
qcsrc/server/progs.src

index 6cff7e5a42666ab32954c8ec8d5abb757b1a09f6..727b0775a9545df500609e79d40e17f589a265bc 100644 (file)
@@ -197,7 +197,6 @@ set g_ctf_flag_collect_delay 1
 set g_ctf_flag_health 0
 set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players"
 set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this velocity"
-set g_ctf_flag_pickup_verbosename 0 "show the name of the person who picked up the flag too"
 set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!"
 set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag"
 set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
index c71c787c6e679cd74ecba9c869f3f7b604ff8b21..368ff4785225c7b8567a4b7a1a7a9e0ecd736bb7 100644 (file)
@@ -154,7 +154,9 @@ void CSQC_Init(void)
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
-
+       CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+       CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       
        WaypointSprite_Load();
 
        // precaches
@@ -1186,6 +1188,10 @@ float CSQC_Parse_TempEntity()
                        cl_notice_read();
                        bHandled = true;
                        break;
+               case TE_CSQC_NOTIFICATION:
+                       Read_Notification();
+                       bHandled = true;
+                       break;
                default:
                        // No special logic for this temporary entity; return 0 so the engine can handle it
                        bHandled = false;
index 0922433eebdecd2d6c3c229636162e1475c6e56b..4e7aca7f63f8ca417d88dd931fc0f75e8eb0ecd9 100644 (file)
@@ -16,6 +16,7 @@ Defs.qc
 
 ../common/util.qh
 ../common/items.qh
+../common/deathtypes.qh
 ../common/explosion_equation.qh
 ../common/mapinfo.qh
 ../common/command/markup.qh
@@ -50,6 +51,8 @@ vehicles/vehicles.qh
 ../csqcmodellib/cl_player.qh
 projectile.qh
 
+../common/notifications.qc
+
 sortlist.qc
 miscfunctions.qc
 teamplay.qc
index 9717905b6e0f61c5d8a863a0467b9ae4826446eb..265b992b1e639334d43b2bc05fc33aa70760e8c9 100644 (file)
@@ -47,6 +47,7 @@ const float TE_CSQC_MINELAYER_MAXMINES = 117;
 const float TE_CSQC_HAGAR_MAXROCKETS = 118;
 const float TE_CSQC_VEHICLESETUP = 119;
 const float TE_CSQC_SVNOTICE = 120;
+const float TE_CSQC_NOTIFICATION = 121;
 
 const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
 const float RACE_NET_CHECKPOINT_CLEAR = 1;
@@ -362,85 +363,6 @@ float SPECIES_ROBOT_RUSTY  =  4;
 float SPECIES_ROBOT_SHINY  =  5;
 float SPECIES_RESERVED     = 15;
 
-// Deathtypes (weapon deathtypes are the IT_* constants below)
-// NOTE: when adding death types, please add an explanation to Docs/spamlog.txt too.
-float DEATH_SPECIAL_START = 10000;
-float DEATH_FALL = 10000;
-float DEATH_TELEFRAG = 10001;
-float DEATH_DROWN = 10002;
-float DEATH_HURTTRIGGER = 10003;
-float DEATH_LAVA = 10004;
-float DEATH_SLIME = 10005;
-float DEATH_KILL = 10006;
-float DEATH_NOAMMO = 10007;
-float DEATH_SWAMP = 10008;
-float DEATH_TEAMCHANGE = 10009;
-float DEATH_AUTOTEAMCHANGE = 10010;
-float DEATH_CAMP = 10011;
-float DEATH_SHOOTING_STAR = 10012;
-float DEATH_ROT = 10013;
-float DEATH_MIRRORDAMAGE = 10014;
-float DEATH_TOUCHEXPLODE = 10015;
-float DEATH_CHEAT = 10016;
-float DEATH_FIRE = 10017;
-float DEATH_QUIET = 10021;
-
-float  DEATH_VHFIRST       = 10030;
-float  DEATH_VHCRUSH       = 10030;
-float  DEATH_SBMINIGUN     = 10031;
-float  DEATH_SBROCKET      = 10032;
-float  DEATH_SBBLOWUP      = 10033;
-float  DEATH_WAKIGUN       = 10034;
-float  DEATH_WAKIROCKET    = 10035;
-float  DEATH_WAKIBLOWUP    = 10036;
-float  DEATH_RAPTOR_CANNON = 10037;
-float  DEATH_RAPTOR_BOMB   = 10038;
-float  DEATH_RAPTOR_BOMB_SPLIT = 10039;
-float  DEATH_RAPTOR_DEATH   = 10040;
-float  DEATH_BUMB_GUN       = 10041;
-float  DEATH_BUMB_RAY       = 10042;
-float  DEATH_BUMB_RAY_HEAL  = 10043;
-float  DEATH_BUMB_DEATH     = 10044;
-float  DEATH_VHLAST         = 10044;
-#define DEATH_ISVEHICLE(t)  ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST)
-
-float DEATH_GENERIC = 10050;
-
-float DEATH_WEAPON = 10100;
-
-float DEATH_CUSTOM = 10300;
-
-float DEATH_TURRET                  = 10500;
-float DEATH_TURRET_EWHEEL           = 10501;
-float DEATH_TURRET_FLAC             = 10502;
-float DEATH_TURRET_MACHINEGUN       = 10503;
-float DEATH_TURRET_WALKER_GUN       = 10504;
-float DEATH_TURRET_WALKER_MEELE     = 10505;
-float DEATH_TURRET_WALKER_ROCKET    = 10506;
-float DEATH_TURRET_HELLION          = 10507;
-float DEATH_TURRET_HK               = 10508;
-float DEATH_TURRET_MLRS             = 10509;
-float DEATH_TURRET_PLASMA           = 10510;
-float DEATH_TURRET_PHASER           = 10511;
-float DEATH_TURRET_TESLA            = 10512;
-float DEATH_TURRET_LAST            = 10512;
-
-float DEATH_WEAPONMASK = 0xFF;
-float DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths
-float HITTYPE_SECONDARY = 0x100;
-float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage
-float HITTYPE_BOUNCE = 0x400;
-float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set)
-float HITTYPE_RESERVED = 0x1000; // unused yet
-
-// macros to access these
-#define DEATH_ISTURRET(t)            ((t) >= DEATH_TURRET && (t) <= DEATH_TURRET_LAST)
-#define DEATH_ISSPECIAL(t)            ((t) >= DEATH_SPECIAL_START)
-#define DEATH_WEAPONOFWEAPONDEATH(t)  ((t) & DEATH_WEAPONMASK)
-#define DEATH_ISWEAPON(t,w)           (!DEATH_ISSPECIAL(t) && DEATH_WEAPONOFWEAPONDEATH(t) == (w))
-#define DEATH_WEAPONOF(t)             (DEATH_ISSPECIAL(t) ? 0 : DEATH_WEAPONOFWEAPONDEATH(t))
-#define WEP_VALID(w)                  ((w) >= WEP_FIRST && (w) <= WEP_LAST)
-
 #define FRAGS_PLAYER 0
 #define FRAGS_SPECTATOR -666
 #define FRAGS_LMS_LOSER -616
@@ -468,49 +390,6 @@ float CPID_MOTD = 9;
 float CPID_KH_MSG = 10;
 float CPID_PREVENT_JOIN = 11;
 
-// CSQC centerprint/notify message types
-float MSG_SUICIDE = 0;
-float MSG_KILL = 1;
-float MSG_SPREE = 2;
-float MSG_KILL_ACTION = 3;
-float MSG_KILL_ACTION_SPREE = 4;
-float MSG_INFO = 5;
-float MSG_KA = 6;
-float MSG_RACE = 10;
-
-float KILL_TEAM_RED = 12001;
-float KILL_TEAM_BLUE = 12002;
-float KILL_TEAM_SPREE = 12003;
-float KILL_FIRST_BLOOD = 12004;
-float KILL_FIRST_VICTIM = 12005;
-float KILL_TYPEFRAG = 12006;
-float KILL_TYPEFRAGGED = 12007;
-float KILL_FRAG = 12008;
-float KILL_FRAGGED = 12009;
-float KILL_SPREE = 12010;
-float KILL_END_SPREE = 12011;
-float KILL_SPREE_3 = 12012;
-float KILL_SPREE_5 = 12013;
-float KILL_SPREE_10 = 12014;
-float KILL_SPREE_15 = 12015;
-float KILL_SPREE_20 = 12016;
-float KILL_SPREE_25 = 12017;
-float KILL_SPREE_30 = 12018;
-
-float INFO_GOTFLAG = 13001;
-float INFO_PICKUPFLAG = 13002;
-float INFO_LOSTFLAG = 13003;
-float INFO_RETURNFLAG = 13004;
-float INFO_CAPTUREFLAG = 13005;
-
-float KA_PICKUPBALL = 14001;
-float KA_DROPBALL = 14002;
-
-float RACE_SERVER_RECORD = 15001;
-float RACE_NEW_TIME = 15002;
-float RACE_NEW_RANK = 15003;
-float RACE_FAIL = 15004;
-
 // weapon requests
 float WR_SETUP         = 1; // (SVQC) setup weapon data
 float WR_THINK         = 2; // (SVQC) logic to run every frame
diff --git a/qcsrc/common/deathtypes.qh b/qcsrc/common/deathtypes.qh
new file mode 100644 (file)
index 0000000..dc243b3
--- /dev/null
@@ -0,0 +1,173 @@
+// Deathtypes (weapon deathtypes are the IT_* constants below)
+// NOTE: when adding death types, please add an explanation to Docs/spamlog.txt too.
+#define DT_FIRST 10000
+#define DT_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
+float DT_COUNT;
+
+#define DT_MATCH(a,b) if(min(DT_MAX, a) == b)
+
+#define DEATHTYPE(name,msg_info,msg_center,position) \
+       float name; \
+       float position; \
+       void RegisterDeathtype_##name() \
+       { \
+               SET_FIRST_OR_LAST(position, DT_FIRST, DT_COUNT) \
+               SET_FIELD_COUNT(name, DT_FIRST, DT_COUNT) \
+               CHECK_MAX_COUNT(name, DT_MAX, DT_COUNT, "deathtypes") \
+       } \
+       ACCUMULATE_FUNCTION(RegisterDeathtypes, RegisterDeathtype_##name)
+
+#define DEATHTYPES \
+       DEATHTYPE(DEATH_FALL, FALSE, FALSE, DEATH_SPECIAL_START) \
+       DEATHTYPE(DEATH_TELEFRAG, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_DROWN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_HURTTRIGGER, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_LAVA, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_SLIME, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_KILL, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_NOAMMO, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_SWAMP, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TEAMCHANGE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_AUTOTEAMCHANGE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_CAMP, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_SHOOTING_STAR, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_ROT, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_MIRRORDAMAGE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TOUCHEXPLODE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_CHEAT, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_FIRE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_QUIET, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_VHCRUSH, FALSE, FALSE, DEATH_VHFIRST) \
+       DEATHTYPE(DEATH_SBMINIGUN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_SBROCKET, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_SBBLOWUP, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_WAKIGUN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_WAKIROCKET, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_WAKIBLOWUP, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_RAPTOR_CANNON, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_RAPTOR_BOMB, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_RAPTOR_BOMB_SPLIT, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_RAPTOR_DEATH, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_BUMB_GUN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_BUMB_RAY, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_BUMB_RAY_HEAL, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_BUMB_DEATH, FALSE, FALSE, DEATH_VHLAST) \
+       DEATHTYPE(DEATH_TURRET, FALSE, FALSE, DEATH_TURRET_FIRST) \
+       DEATHTYPE(DEATH_TURRET_EWHEEL, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_FLAC, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_MACHINEGUN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_WALKER_GUN, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_WALKER_MEELE, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_WALKER_ROCKET, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_HELLION, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_HK, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_MLRS, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_PLASMA, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_PHASER, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_TURRET_TESLA, FALSE, FALSE, DEATH_TURRET_LAST) \
+       DEATHTYPE(DEATH_GENERIC, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_WEAPON, FALSE, FALSE, NORMAL_POS) \
+       DEATHTYPE(DEATH_CUSTOM, FALSE, FALSE, NORMAL_POS) \
+       #undef DEATHTYPE
+
+DEATHTYPES
+
+#define DEATH_ISSPECIAL(t)                             ((t) >= DEATH_SPECIAL_START)
+#define DEATH_ISVEHICLE(t)                             ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST)
+#define DEATH_ISTURRET(t)                              ((t) >= DEATH_TURRET_FIRST && (t) <= DEATH_TURRET_LAST)
+#define DEATH_WEAPONOFWEAPONDEATH(t)   ((t) & DEATH_WEAPONMASK)
+#define DEATH_ISWEAPON(t,w)                            (!DEATH_ISSPECIAL(t) && DEATH_WEAPONOFWEAPONDEATH(t) == (w))
+#define DEATH_WEAPONOF(t)                              (DEATH_ISSPECIAL(t) ? 0 : DEATH_WEAPONOFWEAPONDEATH(t))
+#define WEP_VALID(w)                                   ((w) >= WEP_FIRST && (w) <= WEP_LAST)
+
+float DEATH_WEAPONMASK = 0xFF;
+float DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths
+float HITTYPE_SECONDARY = 0x100;
+float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage
+float HITTYPE_BOUNCE = 0x400;
+float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set)
+float HITTYPE_RESERVED = 0x1000; // unused yet
+
+// macros to access these
+
+// CSQC centerprint/notify message types
+float MSG_SUICIDE = 0;
+float MSG_KILL = 1;
+float MSG_SPREE = 2;
+float MSG_KILL_ACTION = 3;
+float MSG_KILL_ACTION_SPREE = 4;
+float MSG_INFO = 5;
+float MSG_KA = 6;
+float MSG_RACE = 10;
+
+float KILL_TEAM_RED = 12001;
+float KILL_TEAM_BLUE = 12002;
+float KILL_TEAM_SPREE = 12003;
+float KILL_FIRST_BLOOD = 12004;
+float KILL_FIRST_VICTIM = 12005;
+float KILL_TYPEFRAG = 12006;
+float KILL_TYPEFRAGGED = 12007;
+float KILL_FRAG = 12008;
+float KILL_FRAGGED = 12009;
+float KILL_SPREE = 12010;
+float KILL_END_SPREE = 12011;
+float KILL_SPREE_3 = 12012;
+float KILL_SPREE_5 = 12013;
+float KILL_SPREE_10 = 12014;
+float KILL_SPREE_15 = 12015;
+float KILL_SPREE_20 = 12016;
+float KILL_SPREE_25 = 12017;
+float KILL_SPREE_30 = 12018;
+
+
+
+// this shit has got to go
+float INFO_GOTFLAG = 13001;
+float INFO_PICKUPFLAG = 13002;
+float INFO_LOSTFLAG = 13003;
+float INFO_RETURNFLAG = 13004;
+float INFO_CAPTUREFLAG = 13005;
+
+float KA_PICKUPBALL = 14001;
+float KA_DROPBALL = 14002;
+
+float RACE_SERVER_RECORD = 15001;
+float RACE_NEW_TIME = 15002;
+float RACE_NEW_RANK = 15003;
+float RACE_FAIL = 15004;
+
+       /*print("Obituary_Notification(): ", ftos(deathtype), ".\n");
+       switch(deathtype)
+       {
+               // suicide
+               case DEATH_AUTOTEAMCHANGE:
+               case DEATH_CAMP:
+               case DEATH_NOAMMO:
+               case DEATH_ROT:
+               case DEATH_TEAMCHANGE:
+               
+               case KILL_TEAM_SUICIDE_RED:
+               case KILL_TEAM_SUICIDE_BLUE:
+
+               // murder
+               case KILL_TEAM_FRAG_RED:
+               case KILL_TEAM_FRAG_BLUE:
+
+               case KILL_FIRST_BLOOD:
+               case KILL_FIRST_VICTIM:
+               
+               case KILL_FRAG:
+               case KILL_TYPEFRAG:
+               case KILL_FRAGGED:
+               case KILL_TYPEFRAGGED:
+
+               // accident
+               case WATCH_STEP: 
+               
+               case DEATH_QUIET: break;
+
+               // ideally we should have ALL deathtypes listed here
+               default:
+                       backtrace(strcat("Unhandled deathtype: ", ftos(deathtype), ". Please notify Samual!\n"));
+                       break;
+       }*/
diff --git a/qcsrc/common/notifications.qc b/qcsrc/common/notifications.qc
new file mode 100644 (file)
index 0000000..b3fc4c2
--- /dev/null
@@ -0,0 +1,475 @@
+// ================================================
+//  Unified notification system, written by Samual
+//  Last updated: September, 2012
+// ================================================
+
+// main types/groups of notifications
+#define MSG_INFO 1 // "Global" information messages (sent to console, and notify panel if it has an icon)
+#define MSG_CENTER 2 // "Personal" centerprint messages
+#define MSG_WEAPON 3 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
+
+#define NO_STR_ARG ""
+#define NO_FL_ARG -12345
+
+#define F_NAME 1
+#define F_STRNUM 2
+#define F_FLNUM 3
+
+// allow sending of notifications to also pass through to spectators (specifically for centerprints)
+#ifdef SVQC
+#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
+#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
+#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
+#endif
+
+#define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
+#ifdef CSQC
+string got_commandkey;
+#define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
+var float autocvar_notification_ctf_capture_verbose = TRUE;
+var float autocvar_notification_ctf_pickup_team_verbose = TRUE;
+var float autocvar_notification_ctf_pickup_enemy_verbose = TRUE;
+#define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
+#define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
+#define PASS_KEY ((((got_commandkey = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(got_commandkey, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), got_commandkey) : "")
+#else
+#define ADD_CSQC_AUTOCVAR(name)
+#endif
+
+string team_name_red = _("RED");
+string team_name_blue = _("BLUE");
+string team_color_red = _("^1");
+string team_color_blue = _("^5");
+
+
+// ====================================
+//  Notifications List and Information
+// ====================================
+/*
+ List of all notifications (including identifiers and display information)
+ Format: name, strnum, flnum, args, *icon/CPID, *durcnt, normal, gentle
+ Asterisked fields are not present in all notification types.
+ Specifications:
+    Name of notification
+    Number of STRING arguments (so that networking knows how many to send/receive)
+    Number of FLOAT arguments (so that networking knows how many to send/receive)
+    Arguments for sprintf(string, args), if no args needed then use ""
+    *Icon/CPID:
+      MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
+      MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
+    *Duration/Countdown:
+      MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
+    Normal message (string for sprintf when gentle messages are NOT enabled)
+    Gentle message (string for sprintf when gentle messages ARE enabled)
+
+ Messages have ^F1, ^F2, and ^BG in them-- these are replaced
+ with colors according to the cvars the user has chosen.
+    ^F1 = highest priority, "primary"
+    ^F2 = next highest priority, "secondary"
+    ^BG = normal/less important priority, "tertiary"
+
+ Guidlines (please try and follow these):
+    -ALWAYS start the string with a color, preferably background.
+    -ALWAYS reset a color after a name (this way they don't set it for the whole string).
+    -NEVER re-declare an event twice.
+    -NEVER add or remove fields from the format, it SHOULD already work.
+    -MSG_INFO messages must ALWAYS end with a new line: \n
+    -Be clean and simple with your notification naming,
+     nothing too long for the name field... Abbreviations are your friend. :D
+    -Keep the spacing as clean as possible... if the arguments are abnormally long,
+      it's okay to go out of line a bit... but try and keep it clean still.
+    -Keep the notifications in alphabetical order.
+    ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
+*/
+
+// flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
+// weaponorder[f1].netname
+
+#define TWO_TEAMS_INFO(prefix,strnum,flnum,args,icon,normal,gentle) \
+       MSG_INFO_NOTIF(prefix##RED, strnum, flnum, args, sprintf(icon, "red"), TEAM_CCR(normal, team_color_red, team_name_red), TEAM_CCR(gentle, team_color_red, team_name_red)) \
+       MSG_INFO_NOTIF(prefix##BLUE, strnum, flnum, args, sprintf(icon, "blue"), TEAM_CCR(normal, team_color_blue, team_name_blue), TEAM_CCR(gentle, team_color_blue, team_name_blue))
+#define MSG_INFO_NOTIFICATIONS \
+       MSG_INFO_NOTIF(INFO_EMPTY,                                              0, 0, NO_STR_ARG,                                               "", "", "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_DROPPED_,    0, 0, NO_STR_ARG,                                               "", _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_DAMAGED_,    0, 0, NO_STR_ARG,                                               "", _("^BGThe ^TC^TT^BG flag was destroyed and returned to base\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_SPEEDRUN_,   0, 1, f1/100,                                                   "", _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_NEEDKILL_,   0, 0, NO_STR_ARG,                                               "", _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_ABORTRUN_,   0, 0, NO_STR_ARG,                                               "", _("^BGThe ^TC^TT^BG flag was returned to base by its owner\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_TIMEOUT_,    0, 0, NO_STR_ARG,                                               "", _("^BGThe ^TC^TT^BG flag has returned to the base\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_PICKUP_,                                1, 0, s1,                                                               "notify_%s_taken", _("^BG%s^BG got the ^TC^TT^BG flag\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_RETURN_,                                1, 0, s1,                                                               "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_LOST_,                                  1, 0, s1,                                                               "notify_%s_lost", _("^BG%s^BG lost the ^TC^TT^BG flag\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_CAPTURE_,                               1, 0, s1,                                                               "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_CAPTURE_TIME_,                  1, 1, XPND2(s1, f1/100),                                "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_CAPTURE_BROKEN_,                2, 2, XPND4(s1, f1/100, s2, f2/100),    "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds, breaking ^BG%s^BG's previous record of ^F2%.2f^BG seconds\n"), "") \
+       TWO_TEAMS_INFO(INFO_CTF_CAPTURE_UNBROKEN_,              2, 2, XPND4(s1, f1/100, s2, f2/100),    "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%.2f^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%.2f^BG seconds\n"), "") \
+       #undef MSG_INFO_NOTIF
+
+#define TWO_TEAMS_CENTER(prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+       MSG_CENTER_NOTIF(prefix##RED, strnum, flnum, args, cpid, durcnt, TEAM_CCR(normal, team_color_red, team_name_red), TEAM_CCR(gentle, team_color_red, team_name_red)) \
+       MSG_CENTER_NOTIF(prefix##BLUE, strnum, flnum, args, cpid, durcnt, TEAM_CCR(normal, team_color_blue, team_name_blue), TEAM_CCR(gentle, team_color_blue, team_name_blue))
+#define MSG_CENTER_NOTIFICATIONS \
+       MSG_CENTER_NOTIF(CENTER_EMPTY,                                                  0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), "", "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED,             0, 0, NO_STR_ARG,                               CPID_CTF_CAPSHIELD,             XPND2(0, 0), _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE,                 0, 0, NO_STR_ARG,                               CPID_CTF_CAPSHIELD,             XPND2(0, 0), _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_PASS_OTHER_,                                2, 0, XPND2(s1, s2),                    CPID_CTF_PASS,                  XPND2(0, 0), _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_PASS_SENT_,                                 1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_PASS_RECEIVED_,                             1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING,                    1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGRequesting %s^BG to pass you the flag"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTED,                     1, 0, XPND2(s1, PASS_KEY),              CPID_CTF_PASS,                  XPND2(0, 0), _("^BG%s^BG requests you to pass the flag%s"), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_RETURN_,                                    0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou returned the ^TC^TT^BG flag!"), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_CAPTURE_,                                   0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou captured the ^TC^TT^BG flag!"), "") \
+       TWO_TEAMS_CENTER(CENTER_CTF_PICKUP_,                                    0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou got the ^TC^TT^BG flag!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM,                                1, 0, s1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM_VERBOSE,                2, 0, XPND3(s1, s2, s1),                CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY,                               1, 0, s1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY_VERBOSE,               2, 0, XPND3(s1, s2, s1),                CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_CARRIER,                  0, 0, NO_STR_ARG,                               CPID_STALEMATE,                 XPND2(0, 0), _("^BGStalemate! Enemies can now see you on radar!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_OTHER,                    0, 0, NO_STR_ARG,                               CPID_STALEMATE,                 XPND2(0, 0), _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
+       MSG_CENTER_NOTIF(CENTER_CTF_FLAG_THROW_PUNISH,                  0, 1, f1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGToo many flag throws! Throwing disabled for %d seconds."), "") \
+       #undef MSG_CENTER_NOTIF
+
+#define MSG_WEAPON_NOTIFICATIONS \
+       MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, 2, 1, XPND3(s1, s2, f1), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
+       #undef MSG_WEAPON_NOTIF
+
+
+// ====================================
+//  Initialization/Create Declarations
+// ====================================
+
+#define NOTIF_FIRST 1
+#define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
+float NOTIF_INFO_COUNT;
+float NOTIF_CENTER_COUNT;
+float NOTIF_WEAPON_COUNT;
+float NOTIF_CPID_COUNT;
+
+#define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+       ADD_CSQC_AUTOCVAR(name) \
+       float name; \
+       void RegisterNotification_##name() \
+       { \
+               SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
+               CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
+       } \
+       ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+#define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+       ADD_CSQC_AUTOCVAR(name) \
+       float name; \
+       float cpid; \
+       void RegisterNotification_##name() \
+       { \
+               SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
+               SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
+               CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
+       } \
+       ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+#define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+       ADD_CSQC_AUTOCVAR(name) \
+       float name; \
+       void RegisterNotification_##name() \
+       { \
+               SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
+               CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
+       } \
+       ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+// NOW we actually activate the declarations
+MSG_INFO_NOTIFICATIONS
+MSG_CENTER_NOTIFICATIONS
+MSG_WEAPON_NOTIFICATIONS
+
+
+// ======================
+//  Supporting Functions
+// ======================
+
+// select between the normal or the gentle message string based on client (or server) settings
+string normal_or_gentle(string normal, string gentle)
+{
+       #ifdef CSQC
+       if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
+       #else
+       if(autocvar_sv_gentle)
+       #endif
+               return ((gentle != "") ? gentle : normal);
+       else
+               return normal;
+}
+
+float notif_stringcount(string s1, string s2)
+{
+       float stringcount;
+       if(s1 != NO_STR_ARG) ++stringcount;
+       if(s2 != NO_STR_ARG) ++stringcount;
+       return stringcount;
+}
+
+float notif_floatcount(float f1, float f2, float f3)
+{
+       float floatcount;
+       if(f1 != NO_FL_ARG) ++floatcount;
+       if(f2 != NO_FL_ARG) ++floatcount;
+       if(f3 != NO_FL_ARG) ++floatcount;
+       return floatcount;
+}
+
+// get the actual name of a notification and return it as a string
+string Get_Field_Value(float field, float net_type, float net_name)
+{
+       string output;
+       
+       #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
+               if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
+               else if(field == F_STRNUM) { output = ftos(strnum); } \
+               else if(field == F_FLNUM) { output = ftos(flnum); }
+       
+       switch(net_type)
+       {
+               case MSG_INFO:
+               {
+                       #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+                       MSG_INFO_NOTIFICATIONS
+                       break;
+               }
+               case MSG_CENTER:
+               {
+                       #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+                       MSG_CENTER_NOTIFICATIONS
+                       break;
+               }
+               case MSG_WEAPON:
+               {
+                       #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+                       MSG_WEAPON_NOTIFICATIONS
+                       break;
+               }
+       }
+
+       #undef GET_FIELD_VALUE_OUTPUT
+       return output;
+}
+
+string TEAM_CCR(string input, string teamcolor, string teamtext)
+{
+       input = strreplace("^TC", teamcolor, input);
+       input = strreplace("^TT", teamtext, input);
+
+       return input;
+}
+
+// color code replace, place inside of sprintf and parse the string
+string CCR(string input)
+{
+       input = strreplace("^F1", "^2", input); // autocvar_notification_colors_F1 
+       input = strreplace("^F2", "^3", input); // autocvar_notification_colors_F2
+       input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
+       input = strreplace("^K2", "^3", input); // autocvar_notification_colors_K2
+       input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
+
+       input = strreplace("^N", "^7", input); // "none"-- reset to white
+
+       return input;
+}
+
+
+// ===============================
+//  Frontend Notification Pushing
+// ===============================
+
+#ifdef CSQC
+void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+       switch(net_type)
+       {
+               case MSG_INFO:
+               {
+                       #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
+                       MSG_INFO_NOTIFICATIONS
+                       break;
+               }
+               case MSG_CENTER:
+               {
+                       #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
+                       MSG_CENTER_NOTIFICATIONS
+                       break;
+               }
+               case MSG_WEAPON:
+               {
+                       #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print("unhandled\n"); } }
+                       MSG_WEAPON_NOTIFICATIONS
+                       break;
+               }
+       }
+}
+#endif
+
+
+// =========================
+//  Notification Networking
+// =========================
+
+#ifdef CSQC
+void Read_Notification(void)
+{
+       float net_type = ReadByte();
+       float net_name = ReadShort();
+
+       float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
+       float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
+       
+       Local_Notification(net_type, net_name,
+               ((stringcount >= 1) ? ReadString() : ""),
+               ((stringcount == 2) ? ReadString() : ""),
+               ((floatcount >= 1) ? ReadLong() : 0),
+               ((floatcount >= 2) ? ReadLong() : 0),
+               ((floatcount == 3) ? ReadLong() : 0));
+}
+#endif
+
+#ifdef SVQC
+void Send_Notification(entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+       if(net_type && net_name)
+       {
+               print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
+
+               float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
+               float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
+               
+               if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
+               if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
+               
+               if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
+               {
+                       // personal/direct notification sent to ONE person and their spectators
+                       msg_entity = client;
+                       WRITESPECTATABLE_MSG_ONE({
+                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                               WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
+                               WriteByte(MSG_ONE, net_type);
+                               WriteShort(MSG_ONE, net_name);
+                               if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
+                               if(stringcount == 2) { WriteString(MSG_ONE, s2); }
+                               if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
+                               if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
+                               if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
+                       });
+               }
+               else
+               {
+                       // global notification sent to EVERYONE
+                       WriteByte(MSG_ALL, SVC_TEMPENTITY);
+                       WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
+                       WriteByte(MSG_ALL, net_type);
+                       WriteShort(MSG_ALL, net_name);
+                       if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
+                       if(stringcount == 2) { WriteString(MSG_ALL, s2); }
+                       if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
+                       if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
+                       if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
+               }
+
+               if(!server_is_local && (net_type == MSG_INFO))
+               {
+                       #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+                               { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
+                       MSG_INFO_NOTIFICATIONS
+               }
+       }
+       else { backtrace("Incorrect usage of Send_Notification!\n"); }
+}
+
+void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+       entity tmp_entity;
+       FOR_EACH_REALCLIENT(tmp_entity)
+       {
+               if(tmp_entity.classname == STR_PLAYER)
+               if(tmp_entity.team == targetteam)
+               if(tmp_entity != except)
+               {
+                       Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
+               }
+       }
+}
+
+// WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
+void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+       entity tmp_entity;
+       FOR_EACH_REALCLIENT(tmp_entity)
+       {
+               if((tmp_entity.classname == STR_PLAYER) || spectators)
+               if(tmp_entity != except)
+               {
+                       Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
+               }
+       }
+}
+
+
+// =============================
+//  LEGACY NOTIFICATION SYSTEMS
+// =============================
+
+void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
+{
+       WriteByte(MSG_ALL, SVC_TEMPENTITY);
+       WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
+       WriteString(MSG_ALL, s1);
+       WriteString(MSG_ALL, s2);
+       WriteString(MSG_ALL, s3);
+       WriteShort(MSG_ALL, msg);
+       WriteByte(MSG_ALL, type);
+}
+
+// Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
+void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
+{
+       if (clienttype(e) == CLIENTTYPE_REAL)
+       {
+               msg_entity = e;
+               WRITESPECTATABLE_MSG_ONE({
+                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                       WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
+                       WriteString(MSG_ONE, s1);
+                       WriteString(MSG_ONE, s2);
+                       WriteShort(MSG_ONE, msg);
+                       WriteByte(MSG_ONE, type);
+               });
+       }
+}
+
+void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
+{
+       if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
+       {
+               msg_entity = e;
+               WRITESPECTATABLE_MSG_ONE({
+                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                       WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
+                       WriteByte(MSG_ONE, id);
+                       WriteString(MSG_ONE, s);
+                       if (id != 0 && s != "")
+                       {
+                               WriteByte(MSG_ONE, duration);
+                               WriteByte(MSG_ONE, countdown_num);
+                       }
+               });
+       }
+}
+void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
+{
+       Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
+}
+#endif
index faa605f887598c0dba17c7a6d5ae99654eb8e409..2aaf285dec47fcd5ac28d801bca36052e44696ab 100644 (file)
@@ -1,6 +1,9 @@
 // a dummy macro that prevents the "hanging ;" warning
 #define ENDS_WITH_CURLY_BRACE
 
+// return the actual code name of a var as a string
+#define VAR_TO_TEXT(var) #var
+
 #ifdef HAVE_YO_DAWG_CPP
 // TODO make ascii art pic of xzibit
 // YO DAWG!
@@ -37,6 +40,11 @@ void ACCUMULATE_call(string func)
        ACCUMULATE_call(#func)
 #endif
 
+// used for simplifying ACCUMULATE_FUNCTIONs
+#define SET_FIRST_OR_LAST(input,first,count) if(!input) { input = (first + count); }
+#define SET_FIELD_COUNT(field,first,count) if(!field) { field = (first + count); ++count; }
+#define CHECK_MAX_COUNT(name,max,count,type) if(count == max) { error(strcat("Maximum ", type, " hit: ", VAR_TO_TEXT(name), ": ", ftos(count), ".\n")); }
+
 // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline
 // NOTE: s IS allowed to be a tempstring
 string wordwrap(string s, float l);
@@ -361,3 +369,9 @@ float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor);
 typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
 typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
+
+// expand multiple arguments into one argument
+#define XPND5(a,b,c,d,e) a, b, c, d, e
+#define XPND4(a,b,c,d) a, b, c, d
+#define XPND3(a,b,c) a, b, c
+#define XPND2(a,b) a, b
index 16a7193c13710903da8cd9ed2db5143802504508..86688d1814295c17b726ec683f3f0b473c8700a2 100644 (file)
@@ -796,7 +796,6 @@ float autocvar_g_ctf_flag_dropped_waypoint;
 float autocvar_g_ctf_flag_dropped_floatinwater;
 float autocvar_g_ctf_flag_glowtrails;
 float autocvar_g_ctf_flag_health;
-float autocvar_g_ctf_flag_pickup_verbosename;
 string autocvar_g_ctf_flag_red_model;
 float autocvar_g_ctf_flag_red_skin;
 float autocvar_g_ctf_flag_return_time;
@@ -1175,6 +1174,7 @@ float autocvar_sv_fraginfo_stats;
 float autocvar_sv_friction;
 float autocvar_sv_friction_on_land;
 float autocvar_sv_gameplayfix_q2airaccelerate;
+float autocvar_sv_gentle;
 #define autocvar_sv_gravity cvar("sv_gravity")
 string autocvar_sv_intermission_cdtrack;
 string autocvar_sv_jumpspeedcap_max;
index 9d6bcf798d8b3026e6fb542731491923ba99b715..72302d26eb109d25bfaf150d232e5ff3c8f19d11 100644 (file)
@@ -1286,7 +1286,7 @@ void FixClientCvars(entity e)
                stuffcmd(e, "cl_cmd settemp cl_movecliptokeyboard 2\n");
        if(autocvar_g_antilag == 3) // client side hitscan
                stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
-       if(sv_gentle)
+       if(autocvar_sv_gentle)
                stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
        /*
         * we no longer need to stuff this. Remove this comment block if you feel
@@ -1363,6 +1363,16 @@ void ClientConnect (void)
        self.flags = FL_CLIENT;
        self.version_nagtime = time + 10 + random() * 10;
 
+       if(self.netaddress == "local")
+       {
+               print("^3server is local!\n");
+
+               if(server_is_local)
+                       print("Multiple local clients???");
+               else
+                       server_is_local = TRUE;
+       }
+
        if(player_count<0)
        {
                dprint("BUG player count is lower than zero, this cannot happen!\n");
index 3e560bd2452a71020d339d7ed22830a76b212ec8..4b5bca748c54dd5e39597525325f961b9838eef3 100644 (file)
@@ -536,7 +536,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        {
                                self.pain_finished = time + 0.5;        //Supajoe
 
-                               if(sv_gentle < 1) {
+                               if(autocvar_sv_gentle < 1) {
                                        if(self.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
                                        {
                                                if (!self.animstate_override)
@@ -644,7 +644,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                if(valid_damage_for_weaponstats)
                        WeaponStats_LogKill(awep, abot, self.weapon, vbot);
 
-               if(sv_gentle < 1) // TODO make a "gentle" version?
+               if(autocvar_sv_gentle < 1) // TODO make a "gentle" version?
                if(sound_allowed(MSG_BROADCAST, attacker))
                {
                        if(deathtype == DEATH_DROWN)
@@ -786,7 +786,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                // set up to fade out later
                SUB_SetFade (self, time + 6 + random (), 1);
 
-               if(sv_gentle > 0 || autocvar_ekg) {
+               if(autocvar_sv_gentle > 0 || autocvar_ekg) {
                        // remove corpse
                        PlayerCorpseDamage (inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force);
                }
@@ -1237,7 +1237,7 @@ void FakeGlobalSound(string sample, float chan, float voicetype)
                                break;
                        if(!sv_taunt)
                                break;
-                       if(sv_gentle)
+                       if(autocvar_sv_gentle)
                                break;
                        tauntrand = random();
                        msg_entity = self;
@@ -1255,7 +1255,7 @@ void FakeGlobalSound(string sample, float chan, float voicetype)
                                        setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
                        if(!sv_taunt)
                                break;
-                       if(sv_gentle)
+                       if(autocvar_sv_gentle)
                                break;
                        msg_entity = self;
                        if (msg_entity.cvar_cl_voice_directional >= 1)
@@ -1334,7 +1334,7 @@ void GlobalSound(string sample, float chan, float voicetype)
                                break;
                        if(!sv_taunt)
                                break;
-                       if(sv_gentle)
+                       if(autocvar_sv_gentle)
                                break;
                        tauntrand = random();
                        FOR_EACH_REALCLIENT(msg_entity)
@@ -1352,7 +1352,7 @@ void GlobalSound(string sample, float chan, float voicetype)
                                        setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
                        if(!sv_taunt)
                                break;
-                       if(sv_gentle)
+                       if(autocvar_sv_gentle)
                                break;
                        FOR_EACH_REALCLIENT(msg_entity)
                        {
index 9068fa75bf53e95400df5e635e8551faa3ae3a2c..6b3b734ed8580b3ab63d216399a997da850f076a 100644 (file)
@@ -39,7 +39,6 @@ float g_pickup_respawntimejitter_powerup;
 float g_jetpack;
 
 float sv_clones;
-float sv_gentle;
 float sv_foginterval;
 
 entity activator;
@@ -58,6 +57,8 @@ float team1_score, team2_score, team3_score, team4_score;
 
 float maxclients;
 
+float server_is_local; // innocent until proven guilty by ClientConnect() in cl_client.qc
+
 // Fields
 
 .void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) event_damage;
index d2cc61db3346cb3405ca086e9d025657fd2a4c6f..b2a0e99e3bff92990614ca2d77c22699961317c9 100644 (file)
@@ -308,32 +308,16 @@ void LogDeath(string mode, float deathtype, entity killer, entity killed)
        GameLogEcho(s);
 }
 
-void Send_KillNotification (string s1, string s2, string s3, float msg, float type)
+void Obituary_Notification(entity notif_target, string s1, string s2, string s3, float deathtype)
 {
-       WriteByte(MSG_ALL, SVC_TEMPENTITY);
-       WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
-       WriteString(MSG_ALL, s1);
-       WriteString(MSG_ALL, s2);
-       WriteString(MSG_ALL, s3);
-       WriteShort(MSG_ALL, msg);
-       WriteByte(MSG_ALL, type);
-}
-
-// Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
-void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
-{
-       if (clienttype(e) == CLIENTTYPE_REAL)
+       /*if(deathtype)
        {
-               msg_entity = e;
-               WRITESPECTATABLE_MSG_ONE({
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
-                       WriteString(MSG_ONE, s1);
-                       WriteString(MSG_ONE, s2);
-                       WriteShort(MSG_ONE, msg);
-                       WriteByte(MSG_ONE, type);
-               });
-       }
+               #define DEATHTYPE(name,type,notification,first,last) \
+                       { if((deathtype == max(0, name)) && max(0, type) && max(0, notification)) { Send_Notification(type, notif_target, notification, s1, s2, s3); return; } }
+
+               DEATHTYPES
+               backtrace("Unhandled deathtype. Please notify Samual!\n");
+       }*/
 }
 
 void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
@@ -341,6 +325,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
        string  s, a, msg;
        float w, type;
 
+       string s1, s2, s3;
+
        if (targ.classname == "player")
        {
                s = targ.netname;
@@ -348,12 +334,10 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                if (targ == attacker) // suicides
                {
-                       if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
-                               msg = ColoredTeamName(targ.team); // TODO: check if needed?
-                       else
-                               msg = "";
-            if(!g_cts) // no "killed your own dumb self" message in CTS
-                Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE);
+                       s1 = ((deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE) ? ColoredTeamName(targ.team) : "");
+
+                       // no "killed your own dumb self" message in CTS
+            if(!g_cts) { Obituary_Notification(targ, s1, "", "", deathtype); }
 
                        if(deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_QUIET)
                        {
@@ -361,32 +345,24 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                                GiveFrags(attacker, targ, -1, deathtype);
                        }
 
-                       if (targ.killcount > 2)
-                               msg = ftos(targ.killcount);
-                       else
-                               msg = "";
-                       if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
-                       {
-                               if(attacker.team == COLOR_TEAM1)
-                                       deathtype = KILL_TEAM_RED;
-                               else
-                                       deathtype = KILL_TEAM_BLUE;
-                       }
+                       s1 = targ.netname;
+                       s2 = ((targ.killcount > 2) ? ftos(targ.killcount) : "");
+                               
+                       //if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
+                       //      { deathtype = ((attacker.team == COLOR_TEAM1) ? KILL_TEAM_SUICIDE_RED : KILL_TEAM_SUICIDE_BLUE); }
 
-                       Send_KillNotification(s, msg, ftos(w), deathtype, MSG_SUICIDE);
+                       Obituary_Notification(world, s1, s2, "", deathtype);
+                       //Send_KillNotification(s, s2, ftos(w), deathtype, MSG_SUICIDE);
                }
                else if (attacker.classname == "player")
                {
                        if(!IsDifferentTeam(attacker, targ))
                        {
-                               if(attacker.team == COLOR_TEAM1)
-                                       type = KILL_TEAM_RED;
-                               else
-                                       type = KILL_TEAM_BLUE;
+                               //type = ((attacker.team == COLOR_TEAM1) ? KILL_TEAM_FRAG_RED : KILL_TEAM_FRAG_BLUE);
 
                                GiveFrags(attacker, targ, -1, deathtype);
 
-                               Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL);
+                               //Send_CSQC_KillCenterprint(attacker, s, "", type);
 
                                if (targ.killcount > 2)
                                        msg = ftos(targ.killcount);
index 4cd5cc810a1c9f456bc372c0e8106e9b2d274488..f7982cf881ae5bd3a6ca8109a0cfa9d6236db0d4 100644 (file)
@@ -553,6 +553,8 @@ void spawnfunc___init_dedicated_server(void)
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+       CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
 
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@ -599,6 +601,8 @@ void spawnfunc_worldspawn (void)
        // needs to be done so early because of the constants they create
        CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+       CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
 
        ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
 
index 20828800deebfdd3d7d0407269179ec0037489a8..08b06f513804b35f018c41a4e6dcad638091d241 100644 (file)
@@ -1171,7 +1171,6 @@ void readlevelcvars(void)
 #endif
 
        sv_clones = cvar("sv_clones");
-       sv_gentle = cvar("sv_gentle");
        sv_foginterval = cvar("sv_foginterval");
        g_cloaked = cvar("g_cloaked");
     if(g_cts)
@@ -1657,34 +1656,6 @@ void precache()
 #endif
 }
 
-// sorry, but using \ in macros breaks line numbers
-#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
-#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
-#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
-
-
-void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
-{
-       if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
-       {
-               msg_entity = e;
-               WRITESPECTATABLE_MSG_ONE({
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
-                       WriteByte(MSG_ONE, id);
-                       WriteString(MSG_ONE, s);
-                       if (id != 0 && s != "")
-                       {
-                               WriteByte(MSG_ONE, duration);
-                               WriteByte(MSG_ONE, countdown_num);
-                       }
-               });
-       }
-}
-void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
-{
-       Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
-}
 // WARNING: this kills the trace globals
 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
 #define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init()
index f453cc22ad727ea81b78f276ff3958aed207bde2..911c3dee23a0187c3b309b20054cdd4d2726b5c5 100644 (file)
@@ -20,36 +20,39 @@ void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for
                GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
 }
 
-string ctf_CaptureRecord(entity flag, entity player)
+void ctf_CaptureRecord(entity flag, entity player)
 {
-       float cap_time, cap_record, success;
-       string cap_message, refername;
-       
-       if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots)) 
-       {
-               cap_record = ctf_captimerecord;
-               cap_time = (time - flag.ctf_pickuptime);
-
-               refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
-               refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's"));
-
-               if(!ctf_captimerecord) 
-                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; }
-               else if(cap_time < cap_record) 
-                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; }
-               else
-                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; }
+       entity tmp_entity;
+       float notification, success;
+       float cap_record = ctf_captimerecord;
+       float cap_time = (time - flag.ctf_pickuptime);
+       float f1, f2 = NO_FL_ARG;
+       string s1, s2 = NO_STR_ARG;
+       string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
+       
+       // figure shit out
+       if(!ctf_captimerecord) 
+               { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_TIME_); s1 = player.netname; f1 = (cap_time * 100); success = TRUE; }
+       else if(cap_time < cap_record) 
+               { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_BROKEN_); s1 = player.netname; s2 = refername; f1 = (cap_time * 100); f2 = (cap_record * 100); success = TRUE; }
+       else
+               { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_UNBROKEN_); s1 = player.netname; s2 = refername; f1 = (cap_time * 100); f2 = (cap_record * 100); success = FALSE; }
 
-               if(success) 
-               {
-                       ctf_captimerecord = cap_time;
-                       db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
-                       db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
-                       write_recordmarker(player, (time - cap_time), cap_time); 
-               } 
+       // notify about shit
+       FOR_EACH_REALCLIENT(tmp_entity)
+       {
+               if not(tmp_entity.CAPTURE_VERBOSE) { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_); s2 = NO_STR_ARG; f1 = f2 = NO_FL_ARG; }
+               Send_Notification(tmp_entity, MSG_INFO, notification, s1, s2, f1, f2, NO_FL_ARG);
        }
-       
-       return cap_message;
+
+       // write that shit in the database
+       if(success) 
+       {
+               ctf_captimerecord = cap_time;
+               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
+               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
+               write_recordmarker(player, (time - cap_time), cap_time); 
+       } 
 }
 
 void ctf_FlagcarrierWaypoints(entity player)
@@ -166,9 +169,9 @@ void ctf_CaptureShield_Update(entity player, float wanted_status)
        if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
        {
                if(updated_status) // TODO csqc notifier for this // Samual: How?
-                       Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.", 5, 0);
+                       Send_Notification(player, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                else
-                       Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
+                       Send_Notification(player, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_FREE, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                        
                player.ctf_captureshielded = updated_status;
        }
@@ -191,7 +194,7 @@ void ctf_CaptureShield_Touch()
        vector othermid = (other.absmin + other.absmax) * 0.5;
 
        Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
-       Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
+       Send_Notification(other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
 }
 
 void ctf_CaptureShield_Spawn(entity flag)
@@ -234,7 +237,7 @@ void ctf_Handle_Drop(entity flag, entity player, float droptype)
        flag.ctf_status = FLAG_DROPPED;
        
        // messages and sounds
-       Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
+       Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_LOST_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
        sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTN_NONE);
        ctf_EventLog("dropped", player.team, player);
 
@@ -287,11 +290,11 @@ void ctf_Handle_Retrieve(entity flag, entity player)
        FOR_EACH_REALPLAYER(tmp_player)
        {
                if(tmp_player == sender)
-                       centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", player.netname));
+                       Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_SENT_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                else if(tmp_player == player)
-                       centerprint(tmp_player, strcat("You received the ", flag.netname, " from ", sender.netname));
+                       Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_RECEIVED_), sender.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                else if(!IsDifferentTeam(tmp_player, sender))
-                       centerprint(tmp_player, strcat(sender.netname, " passed the ", flag.netname, " to ", player.netname));
+                       Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
        }
        
        // create new waypoint
@@ -404,7 +407,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
        if not(player) { return; } // without someone to give the reward to, we can't possibly cap
        
        // messages and sounds
-       Send_KillNotification(player.netname, enemy_flag.netname, ctf_CaptureRecord(enemy_flag, player), INFO_CAPTUREFLAG, MSG_INFO);
+       ctf_CaptureRecord(enemy_flag, player);
        sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTN_NONE);
        
        switch(capturetype)
@@ -445,8 +448,8 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
 void ctf_Handle_Return(entity flag, entity player)
 {
        // messages and sounds
-       //centerprint(player, strcat("You returned the ", flag.netname));
-       Send_KillNotification(player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO);
+       Send_Notification(player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_RETURN_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+       Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_RETURN_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
        sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE);
        ctf_EventLog("return", flag.team, player);
 
@@ -471,7 +474,6 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
 {
        // declarations
        entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
-       string verbosename; // holds the name of the player OR no name at all for printing in the centerprints
        float pickup_dropped_score; // used to calculate dropped pickup score
        
        // attach the flag to the player
@@ -495,21 +497,20 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
        }
 
        // messages and sounds
-       Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO);
+       Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_PICKUP_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
        sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTN_NONE);
-       verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : "");
-       
+
        FOR_EACH_REALPLAYER(tmp_player)
        {
                if(tmp_player == player)
                {
-                       centerprint(tmp_player, strcat("You got the ", flag.netname, "!"));
-                       //if(ctf_stalemate) { centerprint(tmp_player, "Stalemate! Enemies can see you on radar!"); }
+                       Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PICKUP_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+                       if(ctf_stalemate) { Send_Notification(player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); }
                }
-               //else if(!IsDifferentTeam(tmp_player, player))
-               //      centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!"));
-               else if(!IsDifferentTeam(tmp_player, flag))
-                       centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!"));
+               else if(!IsDifferentTeam(tmp_player, player) && tmp_player != player)
+                       Send_Notification(tmp_player, MSG_CENTER, (tmp_player.PICKUP_TEAM_VERBOSE ? CENTER_CTF_PICKUP_TEAM_VERBOSE : CENTER_CTF_PICKUP_TEAM), Team_ColorCode(player.team), (tmp_player.PICKUP_TEAM_VERBOSE ? player.netname : NO_STR_ARG), NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+               else if(IsDifferentTeam(tmp_player, player))
+                       Send_Notification(tmp_player, MSG_CENTER, (tmp_player.PICKUP_ENEMY_VERBOSE ? CENTER_CTF_PICKUP_ENEMY_VERBOSE : CENTER_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), (tmp_player.PICKUP_ENEMY_VERBOSE ? player.netname : NO_STR_ARG), NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
        }
        
        // scoring
@@ -568,14 +569,14 @@ void ctf_CheckFlagReturn(entity flag, float returntype)
                {
                        switch(returntype)
                        {
-                               case RETURN_DROPPED: bprint("The ", flag.netname, " was dropped in the base and returned itself\n"); break;
-                               case RETURN_DAMAGE: bprint("The ", flag.netname, " was destroyed and returned to base\n"); break;
-                               case RETURN_SPEEDRUN: bprint("The ", flag.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n"); break;
-                               case RETURN_NEEDKILL: bprint("The ", flag.netname, " fell somewhere it couldn't be reached and returned to base\n"); break;
+                               case RETURN_DROPPED: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_DROPPED_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
+                               case RETURN_DAMAGE: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_DAMAGED_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
+                               case RETURN_SPEEDRUN: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), NO_STR_ARG, NO_STR_ARG, ctf_captimerecord, NO_FL_ARG, NO_FL_ARG); break;
+                               case RETURN_NEEDKILL: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_NEEDKILL_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
                                
                                default:
                                case RETURN_TIMEOUT:
-                                       { bprint("The ", flag.netname, " has returned to base\n"); break; }
+                                       { Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_TIMEOUT_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break; }
                        }
                        sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTN_NONE);
                        ctf_EventLog("returned", flag.team, world);
@@ -630,9 +631,9 @@ void ctf_CheckStalemate(void)
                {
                        FOR_EACH_REALPLAYER(tmp_entity)
                                if(tmp_entity.flagcarried)
-                                       centerprint(tmp_entity, "Stalemate! Enemies can now see you on radar!");
+                                       Send_Notification(tmp_entity, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                                else
-                                       centerprint(tmp_entity, "Stalemate! Flag carriers can now be seen by enemies on radar!");
+                                       Send_Notification(tmp_entity, MSG_CENTER, CENTER_CTF_STALEMATE_OTHER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                        
                        wpforenemy_announced = TRUE;
                }
@@ -950,7 +951,7 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag
 
        setattachment(flag, world, ""); 
 
-       flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
+       flag.netname = ((teamnumber) ? "^1REPLACETHIS^7" : "^4REPLACETHIS^7"); // ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
        flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
        flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
        flag.classname = "item_flag_team";
@@ -1842,13 +1843,13 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                                { 
                                                        if(clienttype(head) == CLIENTTYPE_BOT)
                                                        {
-                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                               Send_Notification(player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                                                                ctf_Handle_Throw(head, player, DROP_PASS);
                                                        }
                                                        else
                                                        {
-                                                               centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
-                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                               Send_Notification(head, MSG_CENTER, CENTER_CTF_PASS_REQUESTED, player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+                                                               Send_Notification(player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
                                                        }
                                                        player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
                                                        return TRUE; 
@@ -1885,7 +1886,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
                                }
                                else
                                {
-                                       centerprint(player, strcat("Too many flag throws, throwing disabled for ", ftos(rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)), " seconds."));
+                                       Send_Notification(player, MSG_CENTER, CENTER_CTF_FLAG_THROW_PUNISH, NO_STR_ARG, NO_STR_ARG, rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time), NO_FL_ARG, NO_FL_ARG);
                                        return FALSE;
                                }
                        }
@@ -1960,8 +1961,8 @@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
 {
        if(self.flagcarried)
        {
-               bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
-               ctf_RespawnFlag(self);
+               Send_Notification(world, MSG_INFO, RED_OR_BLUE(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+               ctf_RespawnFlag(self.flagcarried);
                return TRUE;
        }
        
@@ -2008,6 +2009,14 @@ MUTATOR_HOOKFUNCTION(ctf_BotRoles)
        return TRUE;
 }
 
+MUTATOR_HOOKFUNCTION(ctf_GetCvars)
+{
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, CAPTURE_VERBOSE, "notification_ctf_capture_verbose");
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, PICKUP_TEAM_VERBOSE, "notification_ctf_pickup_team_verbose");
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, PICKUP_ENEMY_VERBOSE, "notification_ctf_pickup_enemy_verbose");
+       return TRUE;
+}
+
 
 // ==========
 // Spawnfuncs
@@ -2192,6 +2201,7 @@ MUTATOR_DEFINITION(gamemode_ctf)
        MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
        MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GetCvars, ctf_GetCvars, CBC_ORDER_ANY);
        
        MUTATOR_ONADD
        {
index b6ca033bb3284d34b0e57847e022b21d3a1ea223..bb64ee9eaec42423fba9caef5ff3c01f8ade7c1f 100644 (file)
@@ -130,3 +130,9 @@ vector havocbot_ctf_middlepoint;
 float havocbot_ctf_middlepoint_radius;
 
 void havocbot_role_ctf_setrole(entity bot, float role);
+
+// client notification stuff
+#define RED_OR_BLUE(ent,prefix) ((ent.team == COLOR_TEAM1) ? prefix##RED : prefix##BLUE)
+.float CAPTURE_VERBOSE;
+.float PICKUP_TEAM_VERBOSE;
+.float PICKUP_ENEMY_VERBOSE;
index 028519372f025ae24aa923d472ba0a6593de86b7..e78c3d979704d3c9e8258c5b0cac9044c6f0f3ea 100644 (file)
@@ -15,6 +15,7 @@ sys-post.qh
 ../common/constants.qh
 ../common/util.qh
 ../common/items.qh
+../common/deathtypes.qh
 ../common/explosion_equation.qh
 ../common/urllib.qh
 ../common/command/markup.qh
@@ -86,6 +87,8 @@ scores_rules.qc
 
 miscfunctions.qc
 
+../common/notifications.qc
+
 waypointsprites.qc
 
 bot/bot.qc