]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
47970c5a5936dc3db90d0cb4e7b214f176f08e25
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: December, 2012
4 // ================================================
5
6 // main types/groups of notifications
7 #define MSG_INFO 1 // "Global" information messages (sent to console, and notify panel if it has an icon)
8 #define MSG_CENTER 2 // "Personal" centerprint messages
9 #define MSG_WEAPON 3 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
10 #define MSG_DEATH 4 // "Personal" AND "Global" death messages 
11
12 #define NO_STR_ARG ""
13 #define NO_FL_ARG -12345
14 #define NO_MSG -12345
15
16 #define F_NAME 1
17 #define F_STRNUM 2
18 #define F_FLNUM 3
19
20 #define BOT_PING -1
21
22 // Since this code uses macro processors to list notifications,
23 // the normal compiler sees these checks as "constant" and throws
24 // a warning. We have to get around this by using another function.
25 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
26
27 #ifdef CSQC
28 // NO_CPID normally has a variable value, so we need to check and see
29 // whether a notification uses it. If so, cancel out the centerprint ID.
30 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
31
32 // client-side handling of cvars
33 #define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
34 #define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
35
36 /*
37  Acquire special information to generate for display in the
38  notification from variables networked to the client.
39  Macro descriptions:
40     PASS_KEY: find the keybind for "passing" or "dropping" in CTF game mode
41     FRAG_PING: show the ping of a player
42     FRAG_STATS: show health/armor/ping of a player
43     FRAG_POS: show score status and position in the match of a player
44     SPREE_FRAG: find out if the player is on a kill spree/how many kills they have
45     SPREE_END: placed at the end of murder messages to show ending of sprees
46     SPREE_LOST: placed at the end of suicide messages to show losing of sprees
47     DEATH_TEAM: show the full name of the team a player is switching from
48     WEAPON_NAME: return the full name of a weapon from a weaponid
49 */
50 // CSQC replacements
51 string got_commandkey;
52 #define PASS_KEY ((((got_commandkey = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(got_commandkey, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), got_commandkey) : "")
53 #define FRAG_PING ((f2 != BOT_PING) ? sprintf(CCR(_("\n(Ping ^2%d^BG)")), f2) : "")
54 #define FRAG_STATS sprintf(CCR(_("\n(Health ^1%d^BG / Armor ^2%d^BG)%s")), f1, f2, ((f3 != BOT_PING) ? sprintf(CCR(_(" (Ping ^2%d^BG)")), f3) : ""))
55 #define FRAG_POS ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : "")
56 #define SPREE_FRAG (((f1 == 3) || (f1 == 5) || (f1 == 10) || (f1 == 15) || (f1 == 20) || (f1 == 25) || (f1 == 30)) ? sprintf(normal_or_gentle(_("%d kill spree! "), _("%d score spree! ")), f1) : "")
57 #define SPREE_END ((f1 >= 3) ? sprintf(normal_or_gentle(_(", ending their %d kill spree"), _(", ending their %d score spree")), f1) : "")
58 #define SPREE_LOST ((f1 >= 3) ? sprintf(normal_or_gentle(_(", losing their %d kill spree"), _(", losing their %d score spree")), f1) : "")
59 #define DEATH_TEAM Team_ColoredFullName(TEAM_SV_TO_CL(f1))
60 #define WEAPON_NAME f1 // weaponorder[f1].netname
61 #else
62 #ifdef SVQC
63 // SVQC replacements
64 #define SPREE_END ((f1 >= 3) ? sprintf(normal_or_gentle(_(", ending their %d kill spree"), _(", ending their %d score spree")), f1) : "")
65 #define SPREE_LOST ((f1 >= 3) ? sprintf(normal_or_gentle(_(", losing their %d kill spree"), _(", losing their %d score spree")), f1) : "")
66 #define DEATH_TEAM Team_ColoredFullName(f1)
67 #define WEAPON_NAME f1 // weaponorder[f1].netname
68
69 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
70 #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
71 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
72 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
73 #endif
74
75 // do nothing for the other programs, they don't need cvars (those are just for the clients)
76 #define ADD_CSQC_AUTOCVAR(name)
77 #endif
78
79
80 // ====================================
81 //  Notifications List and Information
82 // ====================================
83 /*
84  List of all notifications (including identifiers and display information)
85  Possible Tokens: name, infoname, centername, strnum, flnum, args, hudargs, icon, cpid, durcnt, normal, gentle
86  Format Specifications:
87     MSG_INFO:
88       name: VAR: Name of notification
89       strnum: FLOAT: Number of STRING arguments (so that networking knows how many to send/receive)
90       flnum: FLOAT: Number of FLOAT arguments (so that networking knows how many to send/receive)
91       args: MISC: Arguments for sprintf(string, args), if no args needed then use ""
92       hudargs: XPND2(STRING, STRING): arguments for names in notify messages 
93       icon: STRING: icon string name for the hud notify panel, "" if no icon is used
94       normal: STRING: Normal message (string for sprintf when gentle messages are NOT enabled)
95       gentle: STRING: Gentle message (string for sprintf when gentle messages ARE enabled)
96     MSG_CENTER:
97       name: VAR: Name of notification
98       strnum: FLOAT: Number of STRING arguments (so that networking knows how many to send/receive)
99       flnum: FLOAT: Number of FLOAT arguments (so that networking knows how many to send/receive)
100       args: MISC: Arguments for sprintf(string, args), if no args needed then use ""
101       cpid: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
102       durcnt: XPND2(FLOAT, FLOAT): Duration/Countdown: extra arguments for centerprint messages
103       normal: STRING: Normal message (string for sprintf when gentle messages are NOT enabled)
104       gentle: STRING: Gentle message (string for sprintf when gentle messages ARE enabled)
105     MSG_WEAPON:
106     MSG_DEATH:
107       name: VAR: Name of chaining notification
108       infoname: VAR: Name of info notification for reference
109       centername: VAR: Name of centerprint notification for reference
110
111  Messages with ^F1, ^BG, ^TC, etc etc in them will replace those strings
112  with colors according to the cvars the user has chosen. This allows for
113  users to create unique color profiles for their HUD, giving more customization
114  options to HUD designers and end users who want such a feature.
115
116  Check out the function calls for string CCR(...) and
117  string TCR(...) to better understand how these codes work.
118
119  Guidlines (please try and follow these):
120     -ALWAYS start the string with a color, preferably background.
121     -ALWAYS reset a color after a name (this way they don't set it for the whole string).
122     -NEVER re-declare an event twice.
123     -NEVER add or remove fields from the format, it SHOULD already work.
124     -MSG_INFO messages must ALWAYS end with a new line: \n
125     -Be clean and simple with your notification naming,
126      nothing too long for the name field... Abbreviations are your friend. :D
127     -Keep the spacing as clean as possible... if the arguments are abnormally long,
128       it's okay to go out of line a bit... but try and keep it clean still.
129     -Sort the notifications in the most appropriate order for their tasks.
130       TODO: ? centerprint IDs are given priority based on their order (first being highest priority going downwards)
131     -ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
132 */
133
134 #define MULTITEAM_INFO(prefix,teams,strnum,flnum,args,hudargs,icon,normal,gentle) \
135         MSG_INFO_NOTIF(prefix##RED, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_1)), TCR(normal, COL_TEAM_1, strtoupper(STR_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STR_TEAM_1))) \
136         MSG_INFO_NOTIF(prefix##BLUE, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_2)), TCR(normal, COL_TEAM_2, strtoupper(STR_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STR_TEAM_2))) \
137         #if teams >= 3 \
138                 MSG_INFO_NOTIF(prefix##YELLOW, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_3)), TCR(normal, COL_TEAM_3, strtoupper(STR_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STR_TEAM_3))) \
139         #endif \
140         #if teams >= 4 \
141                 MSG_INFO_NOTIF(prefix##PINK, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_4)), TCR(normal, COL_TEAM_4, strtoupper(STR_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STR_TEAM_4))) \
142         #endif
143 #define MSG_INFO_NOTIFICATIONS \
144         MSG_INFO_NOTIF(INFO_EMPTY,                                                      0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             "", "") \
145         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_DROPPED_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself\n"), "") \
146         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_DAMAGED_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^BGThe ^TC^TT^BG flag was destroyed and returned to base\n"), "") \
147         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_SPEEDRUN_, 2,        0, 1, f1/100, XPND2("", ""),                                            "",                                             _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself\n"), "") \
148         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_NEEDKILL_, 2,        0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base\n"), "") \
149         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_ABORTRUN_, 2,        0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^BGThe ^TC^TT^BG flag was returned to base by its owner\n"), "") \
150         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_TIMEOUT_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^BGThe ^TC^TT^BG flag has returned to the base\n"), "") \
151         MULTITEAM_INFO(INFO_CTF_PICKUP_, 2,                                     1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_taken",              _("^BG%s^BG got the ^TC^TT^BG flag\n"), "") \
152         MULTITEAM_INFO(INFO_CTF_RETURN_, 2,                                     1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_returned",   _("^BG%s^BG returned the ^TC^TT^BG flag\n"), "") \
153         MULTITEAM_INFO(INFO_CTF_LOST_, 2,                                       1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_lost",               _("^BG%s^BG lost the ^TC^TT^BG flag\n"), "") \
154         MULTITEAM_INFO(INFO_CTF_CAPTURE_, 2,                            1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag\n"), "") \
155         MULTITEAM_INFO(INFO_CTF_CAPTURE_TIME_, 2,                       1, 1, XPND2(s1, f1/100), XPND2(s1, ""),                         "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds\n"), "") \
156         MULTITEAM_INFO(INFO_CTF_CAPTURE_BROKEN_, 2,                     2, 2, XPND4(s1, f1/100, s2, f2/100), XPND2(s1, ""),     "notify_%s_captured",   _("^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"), "") \
157         MULTITEAM_INFO(INFO_CTF_CAPTURE_UNBROKEN_, 2,           2, 2, XPND4(s1, f1/100, s2, f2/100), XPND2(s1, ""),     "notify_%s_captured",   _("^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"), "") \
158         MULTITEAM_INFO(INFO_DEATH_TEAMKILL_, 4,                         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s\n"), "") \
159         MSG_INFO_NOTIF(INFO_DEATH_FIRSTBLOOD,                           2, 0, s2, XPND2("", ""),                                                        "",                                             _("^BG%s^K1 drew first blood\n"), _("^F1%s^K1 got the first score\n")) \
160         MSG_INFO_NOTIF(INFO_DEATH_SELF_CUSTOM,                          2, 1, XPND3(s1, s2, SPREE_LOST), XPND2(s1, ""),         "notify_void",                  _("^BG%s^K1 %s^K1%s\n"), "") \
161         MSG_INFO_NOTIF(INFO_DEATH_SELF_GENERIC,                         1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_selfkill",              _("^BG%s^K1 died%s\n"), "") \
162         MSG_INFO_NOTIF(INFO_DEATH_SELF_VOID,                            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_void",                  _("^BG%s^K1 was in the wrong place%s\n"), "") \
163         MSG_INFO_NOTIF(INFO_DEATH_SELF_SUICIDE,                         1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_selfkill",              _("^BG%s^K1 couldn't take it anymore%s\n"), "") \
164         MSG_INFO_NOTIF(INFO_DEATH_SELF_NOAMMO,                          1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_outofammo",             _("^BG%s^K1 died%s. What's the point of living without ammo?\n"), _("^F1%s^K1 ran out of ammo%s\n")) \
165         MSG_INFO_NOTIF(INFO_DEATH_SELF_ROT,                                     1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_death",                 _("^BG%s^K1 rotted away%s\n"), "") \
166         MSG_INFO_NOTIF(INFO_DEATH_SELF_CAMP,                            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_camping",               _("^BG%s^K1 thought they found a nice camping ground%s\n"), "") \
167         MSG_INFO_NOTIF(INFO_DEATH_SELF_BETRAYAL,                        1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_teamkill_red",  _("^BG%s^K1 became enemies with the Lord of Teamplay%s\n"), "") \
168         MSG_INFO_NOTIF(INFO_DEATH_SELF_TEAMCHANGE,                      1, 1, XPND2(s1, DEATH_TEAM), XPND2("", ""),                     "",                                             _("^BG%s^K1 switched to the %s\n"), "") \
169         MSG_INFO_NOTIF(INFO_DEATH_SELF_AUTOTEAMCHANGE,          1, 1, XPND2(s1, DEATH_TEAM), XPND2("", ""),                     "",                                             _("^BG%s^K1 was moved into the %s\n"), "") \
170         MSG_INFO_NOTIF(INFO_DEATH_SELF_FALL,                            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_fall",                  _("^BG%s^K1 hit the ground with a crunch%s\n"), _("^F1%s^K1 hit the ground with a bit too much force%s\n")) \
171         MSG_INFO_NOTIF(INFO_DEATH_SELF_DROWN,                           1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_water",                 _("^BG%s^K1 couldn't catch their breath%s\n"), _("^F1%s^K1 was in the water for too long%s\n")) \
172         MSG_INFO_NOTIF(INFO_DEATH_SELF_FIRE,                            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_death",                 _("^BG%s^K1 became a bit too crispy%s\n"), _("^F1%s^K1 felt a little hot%s\n")) \
173         MSG_INFO_NOTIF(INFO_DEATH_SELF_LAVA,                            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_lava",                  _("^BG%s^K1 turned into hot slag%s\n"), _("^F1%s^K1 found a hot place%s\n")) \
174         MSG_INFO_NOTIF(INFO_DEATH_SELF_SLIME,                           1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_slime",                 _("^BG%s^K1 was slimed%s\n"), "") \
175         MSG_INFO_NOTIF(INFO_DEATH_SELF_SHOOTING_STAR,           1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s\n"), "") \
176         MSG_INFO_NOTIF(INFO_DEATH_SELF_SWAMP,                           1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_slime",                 _("^BG%s^K1 is now preserved for centuries to come%s\n"), "") \
177         MSG_INFO_NOTIF(INFO_DEATH_SELF_CHEAT,                           1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_selfkill",              _("^BG%s^K1 unfairly eliminated themself%s\n"), "") \
178         MSG_INFO_NOTIF(INFO_DEATH_SELF_TOUCHEXPLODE,            1, 1, XPND2(s1, SPREE_LOST), XPND2(s1, ""),                     "notify_death",                 _("^BG%s^K1 died in an accident%s\n"), "") \
179         MSG_INFO_NOTIF(INFO_DEATH_MURDER_TELEFRAG,                      2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_telefrag",              _("^BG%s^K1 was telefragged by ^BG%s^K1%s\n"), _("^F1%s^K1 tried to occupy ^BG%s^K1's teleport destination space\n")) \
180         MSG_INFO_NOTIF(INFO_DEATH_MURDER_FALL,                          2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_fall",                  _("^BG%s^K1 was grounded by ^BG%s^K1%s\n"), "") \
181         MSG_INFO_NOTIF(INFO_DEATH_MURDER_DROWN,                         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_water",                 _("^BG%s^K1 was drowned by ^BG%s^K1%s\n"), "") \
182         MSG_INFO_NOTIF(INFO_DEATH_MURDER_LAVA,                          2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_lava",                  _("^BG%s^K1 was cooked by ^BG%s^K1%s\n"), "") \
183         MSG_INFO_NOTIF(INFO_DEATH_MURDER_SLIME,                         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_slime",                 _("^BG%s^K1 was slimed by ^BG%s^K1%s\n"), "") \
184         MSG_INFO_NOTIF(INFO_DEATH_MURDER_SHOOTING_STAR,         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_shootingstar",  _("^BG%s^K1 was shot into space by ^BG%s^K1%s\n"), "") \
185         MSG_INFO_NOTIF(INFO_DEATH_MURDER_SWAMP,                         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_slime",                 _("^BG%s^K1 was preserved by ^BG%s^K1%s\n"), "") \
186         MSG_INFO_NOTIF(INFO_DEATH_MURDER_VOID,                          2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_void",                  _("^BG%s^K1 was thrown into a world of hurt by ^BG%s^K1%s\n"), "") \
187         MSG_INFO_NOTIF(INFO_DEATH_MURDER_TOUCHEXPLODE,          2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_death",                 _("^BG%s^K1 died in an accident with ^BG%s^K1%s\n"), "") \
188         MSG_INFO_NOTIF(INFO_DEATH_MURDER_CHEAT,                         2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_death",                 _("^BG%s^K1 was unfairly eliminated by ^BG%s^K1%s\n"), "") \
189         MSG_INFO_NOTIF(INFO_DEATH_MURDER_FIRE,                          2, 1, XPND3(s1, s2, SPREE_END), XPND2(s1, s2),          "notify_death",                 _("^BG%s^K1 was burnt up into a crisp by ^BG%s^K1%s\n"), _("^F1%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s\n")) \
190         MULTITEAM_INFO(INFO_SCORES_, 4,                                         0, 0, NO_STR_ARG, XPND2("", ""),                                        "",                                             _("^TC^TT ^BGteam scores!\n"), "") \
191         MSG_INFO_NOTIF(INFO_WEAPON_MARBLES_LOST,                        2, 1, XPND3(s1, s2, WEAPON_NAME), XPND2("", ""),        "",                                             _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "")
192
193 #define MULTITEAM_CENTER(prefix,teams,strnum,flnum,args,cpid,durcnt,normal,gentle) \
194         MSG_CENTER_NOTIF(prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(STR_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STR_TEAM_1))) \
195         MSG_CENTER_NOTIF(prefix##BLUE, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_2, strtoupper(STR_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STR_TEAM_2))) \
196         #if teams >= 3 \
197                 MSG_CENTER_NOTIF(prefix##YELLOW, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_3, strtoupper(STR_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STR_TEAM_3))) \
198         #endif \
199         #if teams >= 4 \
200                 MSG_CENTER_NOTIF(prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(STR_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STR_TEAM_4))) \
201         #endif
202 #define MSG_CENTER_NOTIFICATIONS \
203         MSG_CENTER_NOTIF(CENTER_EMPTY,                                                  0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), "", "") \
204         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."), "") \
205         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."), "") \
206         MULTITEAM_CENTER(CENTER_CTF_PASS_OTHER_, 2,                             2, 0, XPND2(s1, s2),                    CPID_CTF_PASS,                  XPND2(0, 0), _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
207         MULTITEAM_CENTER(CENTER_CTF_PASS_SENT_, 2,                              1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
208         MULTITEAM_CENTER(CENTER_CTF_PASS_RECEIVED_, 2,                  1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
209         MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING,                    1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGRequesting %s^BG to pass you the flag"), "") \
210         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"), "") \
211         MULTITEAM_CENTER(CENTER_CTF_RETURN_, 2,                                 0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou returned the ^TC^TT^BG flag!"), "") \
212         MULTITEAM_CENTER(CENTER_CTF_CAPTURE_, 2,                                0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou captured the ^TC^TT^BG flag!"), "") \
213         MULTITEAM_CENTER(CENTER_CTF_PICKUP_, 2,                                 0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou got the ^TC^TT^BG flag!"), "") \
214         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!"), "") \
215         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!"), "") \
216         MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY,                               1, 0, s1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
217         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!"), "") \
218         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!"), "") \
219         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!"), "") \
220         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."), "") \
221         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_CUSTOM,                              2, 0, s2,                                               NO_CPID,                                XPND2(0, 0), _("^K1You were %s"), "") \
222         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_GENERIC,                             0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You killed your own dumb self!"), _("^K1You need to be more careful!")) \
223         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_VOID,                                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1Watch your step!"), "") \
224         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_SUICIDE,                             0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You committed suicide!"), _("^K1You ended it all!")) \
225         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_NOAMMO,                              0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
226         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_ROT,                                 0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
227         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_CAMP,                                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1Die camper!"), _("^K1Reconsider your tactics, camper!")) \
228         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_BETRAYAL,                    0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1Don't shoot your team mates!"), _("^K1Don't go against your team mates!")) \
229         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_TEAMCHANGE,                  0, 1, DEATH_TEAM,                               NO_CPID,                                XPND2(0, 0), _("^BGYou are now on: %s"), "") \
230         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_AUTOTEAMCHANGE,              0, 1, DEATH_TEAM,                               NO_CPID,                                XPND2(0, 0), _("^BGYou have been moved into a different team\nYou are now on: %s"), "") \
231         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_FALL,                                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You hit the ground with a crunch!"), "") \
232         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_DROWN,                               0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You couldn't catch your breath in time!"), "") \
233         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_FIRE,                                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You got a little bit too crispy!"), _("^K1You felt a little too hot!")) \
234         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_LAVA,                                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You couldn't stand the heat!"), "") \
235         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_SLIME,                               0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You melted away in slime!"), "") \
236         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_SHOOTING_STAR,               0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You became a shooting star!"), "") \
237         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_SWAMP,                               0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You got stuck in a swamp!"), "") \
238         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_CHEAT,                               0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You unfairly eliminated yourself!"), "") \
239         MSG_CENTER_NOTIF(CENTER_DEATH_SELF_TOUCHEXPLODE,                0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), _("^K1You died in an accident!"), "") \
240         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAG,                                              1, 1, XPND2(SPREE_FRAG, s1),                                                    NO_CPID, XPND2(0, 0), _("^K3%sYou fragged ^BG%s"), _("^K3%sYou scored against ^BG%s")) \
241         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAGGED,                                   1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1You were fragged by ^BG%s"), _("^K1You were scored against by ^BG%s")) \
242         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAG,                                  1, 1, XPND2(SPREE_FRAG, s1),                                                    NO_CPID, XPND2(0, 0), _("^K1%sYou typefragged ^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing")) \
243         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAGGED,                               1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1You were typefragged by ^BG%s"), _("^K1You were scored against by ^BG%s^K1 while typing!")) \
244         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAG_FIRST,                                1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K3First blood! You fragged ^BG%s"), _("^K3First score! You scored against ^BG%s")) \
245         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAGGED_FIRST,                             1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First victim! You were fragged by ^BG%s"), _("^K1First casualty! You were scored against by ^BG%s")) \
246         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAG_FIRST,                    1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First blood! You typefragged ^BG%s"), _("^K1First score! You scored against ^BG%s^K1 while they were typing")) \
247         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAGGED_FIRST,                 1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First victim! You were typefragged by ^BG%s"), _("^K1First casualty! You were scored against by ^BG%s^K1 while typing!")) \
248         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAG_VERBOSE,                              1, 2, XPND3(SPREE_FRAG, s1, FRAG_PING),                                 NO_CPID, XPND2(0, 0), _("^K3You fragged ^BG%s^BG%s"), _("^K3You scored against ^BG%s^BG%s")) \
249         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAGGED_VERBOSE,                   1, 3, XPND2(s1, FRAG_STATS),                                                    NO_CPID, XPND2(0, 0), _("^K1You were fragged by ^BG%s^BG%s"), _("^K1You were scored against by ^BG%s^BG%s")) \
250         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE,                  1, 2, XPND3(SPREE_FRAG, s1, FRAG_PING),                                 NO_CPID, XPND2(0, 0), _("^K1You typefragged ^BG%s^BG%s"), _("^K1You scored against ^BG%s^K1 while they were typing^BG%s")) \
251         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE,               1, 3, XPND2(s1, FRAG_STATS),                                                    NO_CPID, XPND2(0, 0), _("^K1You were typefragged by ^BG%s^BG%s"), _("^K1You were scored against by ^BG%s^K1 while typing^BG%s")) \
252         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAG_FIRST_VERBOSE,                1, 1, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K3First blood! You fragged ^BG%s"), _("^K3First score! You scored against ^BG%s")) \
253         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_FRAGGED_FIRST_VERBOSE,             1, 3, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First victim! You were fragged by ^BG%s"), _("^K1First casualty! You were scored against by ^BG%s")) \
254         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAG_FIRST_VERBOSE,    1, 1, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First blood! You typefragged ^BG%s"), _("^K1First score! You scored against ^BG%s^K1 while they were typing")) \
255         MSG_CENTER_NOTIF(CENTER_DEATH_MURDER_TYPEFRAGGED_FIRST_VERBOSE, 1, 3, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1First victim! You were typefragged by ^BG%s"), _("^K1First casualty! You were scored against by ^BG%s^K1 while typing!")) \
256         MSG_CENTER_NOTIF(CENTER_DEATH_TEAMKILL_FRAG,                                    1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1Moron! You fragged ^BG%s^K1, a team mate!"), _("^K1Moron! You went against ^BG%sK1, a team mate!")) \
257         MSG_CENTER_NOTIF(CENTER_DEATH_TEAMKILL_FRAGGED,                                 1, 0, s1,                                                                                               NO_CPID, XPND2(0, 0), _("^K1You were fragged by ^BG%s^K1, a team mate"), _("^K1You were scored against by ^BG%s^K1, a team mate")) \
258         MSG_CENTER_NOTIF(CENTER_WEAPON_MARBLES_LOST,                    1, 1, XPND2(s1, WEAPON_NAME),   NO_CPID,                                XPND2(0, 0), _("^K1You lost your marbles against ^BG%s^K1 using the ^BG%s!"), "")
259
260 #define MSG_WEAPON_NOTIFICATIONS \
261         MSG_WEAPON_NOTIF(WEAPON_EMPTY,                                          NO_MSG,                                                                 NO_MSG) \
262         MSG_WEAPON_NOTIF(WEAPON_MARBLES_LOST,                           INFO_WEAPON_MARBLES_LOST,                               CENTER_WEAPON_MARBLES_LOST)
263
264 #define MSG_DEATH_NOTIFICATIONS \
265         MSG_DEATH_NOTIF(DEATH_EMPTY,                                            NO_MSG,                                                                 NO_MSG) \
266         MSG_DEATH_NOTIF(DEATH_SELF_CUSTOM,                                      INFO_DEATH_SELF_GENERIC,                                CENTER_DEATH_SELF_CUSTOM) \
267         MSG_DEATH_NOTIF(DEATH_SELF_GENERIC,                                     INFO_DEATH_SELF_GENERIC,                                CENTER_DEATH_SELF_GENERIC) \
268         MSG_DEATH_NOTIF(DEATH_SELF_VOID,                                        INFO_DEATH_SELF_VOID,                                   CENTER_DEATH_SELF_VOID) \
269         MSG_DEATH_NOTIF(DEATH_SELF_SUICIDE,                                     INFO_DEATH_SELF_SUICIDE,                                CENTER_DEATH_SELF_SUICIDE) \
270         MSG_DEATH_NOTIF(DEATH_SELF_NOAMMO,                                      INFO_DEATH_SELF_NOAMMO,                                 CENTER_DEATH_SELF_NOAMMO) \
271         MSG_DEATH_NOTIF(DEATH_SELF_ROT,                                         INFO_DEATH_SELF_ROT,                                    CENTER_DEATH_SELF_ROT) \
272         MSG_DEATH_NOTIF(DEATH_SELF_CAMP,                                        INFO_DEATH_SELF_CAMP,                                   CENTER_DEATH_SELF_CAMP) \
273         MSG_DEATH_NOTIF(DEATH_SELF_BETRAYAL,                            INFO_DEATH_SELF_BETRAYAL,                               CENTER_DEATH_SELF_BETRAYAL) \
274         MSG_DEATH_NOTIF(DEATH_SELF_TEAMCHANGE,                          INFO_DEATH_SELF_TEAMCHANGE,                             CENTER_DEATH_SELF_TEAMCHANGE) \
275         MSG_DEATH_NOTIF(DEATH_SELF_AUTOTEAMCHANGE,                      INFO_DEATH_SELF_AUTOTEAMCHANGE,                 CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
276         MSG_DEATH_NOTIF(DEATH_SELF_FALL,                                        INFO_DEATH_SELF_FALL,                                   CENTER_DEATH_SELF_FALL) \
277         MSG_DEATH_NOTIF(DEATH_SELF_DROWN,                                       INFO_DEATH_SELF_DROWN,                                  CENTER_DEATH_SELF_DROWN) \
278         MSG_DEATH_NOTIF(DEATH_SELF_FIRE,                                        INFO_DEATH_SELF_FIRE,                                   CENTER_DEATH_SELF_FIRE) \
279         MSG_DEATH_NOTIF(DEATH_SELF_LAVA,                                        INFO_DEATH_SELF_LAVA,                                   CENTER_DEATH_SELF_LAVA) \
280         MSG_DEATH_NOTIF(DEATH_SELF_SLIME,                                       INFO_DEATH_SELF_SLIME,                                  CENTER_DEATH_SELF_SLIME) \
281         MSG_DEATH_NOTIF(DEATH_SELF_SHOOTING_STAR,                       INFO_DEATH_SELF_SHOOTING_STAR,                  CENTER_DEATH_SELF_SHOOTING_STAR) \
282         MSG_DEATH_NOTIF(DEATH_SELF_SWAMP,                                       INFO_DEATH_SELF_SWAMP,                                  CENTER_DEATH_SELF_SWAMP) \
283         MSG_DEATH_NOTIF(DEATH_SELF_CHEAT,                                       INFO_DEATH_SELF_CHEAT,                                  CENTER_DEATH_SELF_CHEAT) \
284         MSG_DEATH_NOTIF(DEATH_SELF_TOUCHEXPLODE,                        INFO_DEATH_SELF_TOUCHEXPLODE,                   CENTER_DEATH_SELF_TOUCHEXPLODE) \
285         MSG_DEATH_NOTIF(DEATH_MURDER_TELEFRAG,                                  INFO_DEATH_MURDER_TELEFRAG,                                     NO_MSG) \
286         MSG_DEATH_NOTIF(DEATH_MURDER_FALL,                                              INFO_DEATH_MURDER_FALL,                                         NO_MSG) \
287         MSG_DEATH_NOTIF(DEATH_MURDER_DROWN,                                             INFO_DEATH_MURDER_DROWN,                                        NO_MSG) \
288         MSG_DEATH_NOTIF(DEATH_MURDER_LAVA,                                              INFO_DEATH_MURDER_LAVA,                                         NO_MSG) \
289         MSG_DEATH_NOTIF(DEATH_MURDER_SLIME,                                             INFO_DEATH_MURDER_SLIME,                                        NO_MSG) \
290         MSG_DEATH_NOTIF(DEATH_MURDER_SHOOTING_STAR,                             INFO_DEATH_MURDER_SHOOTING_STAR,                        NO_MSG) \
291         MSG_DEATH_NOTIF(DEATH_MURDER_SWAMP,                                             INFO_DEATH_MURDER_SWAMP,                                        NO_MSG) \
292         MSG_DEATH_NOTIF(DEATH_MURDER_VOID,                                              INFO_DEATH_MURDER_VOID,                                         NO_MSG) \
293         MSG_DEATH_NOTIF(DEATH_MURDER_TOUCHEXPLODE,                              INFO_DEATH_MURDER_TOUCHEXPLODE,                         NO_MSG) \
294         MSG_DEATH_NOTIF(DEATH_MURDER_CHEAT,                                             INFO_DEATH_MURDER_CHEAT,                                        NO_MSG) \
295         MSG_DEATH_NOTIF(DEATH_MURDER_FIRE,                                              INFO_DEATH_MURDER_FIRE,                                         NO_MSG) \
296         MSG_DEATH_NOTIF(DEATH_MURDER_FRAG,                                                      NO_MSG,                                         CENTER_DEATH_MURDER_FRAG) \
297         MSG_DEATH_NOTIF(DEATH_MURDER_FRAGGED,                                           NO_MSG,                                         CENTER_DEATH_MURDER_FRAGGED) \
298         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAG,                                          NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAG) \
299         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAGGED,                                       NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAGGED) \
300         MSG_DEATH_NOTIF(DEATH_MURDER_FRAG_FIRST,                                        INFO_DEATH_FIRSTBLOOD,          CENTER_DEATH_MURDER_FRAG_FIRST) \
301         MSG_DEATH_NOTIF(DEATH_MURDER_FRAGGED_FIRST,                                     NO_MSG,                                         CENTER_DEATH_MURDER_FRAGGED_FIRST) \
302         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAG_FIRST,                            INFO_DEATH_FIRSTBLOOD,          CENTER_DEATH_MURDER_TYPEFRAG_FIRST) \
303         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAGGED_FIRST,                         NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAGGED_FIRST) \
304         MSG_DEATH_NOTIF(DEATH_MURDER_FRAG_VERBOSE,                                      NO_MSG,                                         CENTER_DEATH_MURDER_FRAG_VERBOSE) \
305         MSG_DEATH_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE,                           NO_MSG,                                         CENTER_DEATH_MURDER_FRAGGED_VERBOSE) \
306         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAG_VERBOSE,                          NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE) \
307         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAGGED_VERBOSE,                       NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE) \
308         MSG_DEATH_NOTIF(DEATH_MURDER_FRAG_FIRST_VERBOSE,                        INFO_DEATH_FIRSTBLOOD,          CENTER_DEATH_MURDER_FRAG_FIRST_VERBOSE) \
309         MSG_DEATH_NOTIF(DEATH_MURDER_FRAGGED_FIRST_VERBOSE,                     NO_MSG,                                         CENTER_DEATH_MURDER_FRAGGED_FIRST_VERBOSE) \
310         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAG_FIRST_VERBOSE,            INFO_DEATH_FIRSTBLOOD,          CENTER_DEATH_MURDER_TYPEFRAG_FIRST_VERBOSE) \
311         MSG_DEATH_NOTIF(DEATH_MURDER_TYPEFRAGGED_FIRST_VERBOSE,         NO_MSG,                                         CENTER_DEATH_MURDER_TYPEFRAGGED_FIRST_VERBOSE) \
312         MSG_DEATH_NOTIF(DEATH_TEAMKILL_FRAG,                                            NO_MSG,                                         CENTER_DEATH_TEAMKILL_FRAG) \
313         MSG_DEATH_NOTIF(DEATH_TEAMKILL_FRAGGED,                                         NO_MSG,                                         CENTER_DEATH_TEAMKILL_FRAGGED)
314
315         
316 // ====================================
317 //  Initialization/Create Declarations
318 // ====================================
319
320 #define NOTIF_FIRST 1
321 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
322 float NOTIF_INFO_COUNT;
323 float NOTIF_CENTER_COUNT;
324 float NOTIF_WEAPON_COUNT;
325 float NOTIF_DEATH_COUNT;
326 float NOTIF_CPID_COUNT;
327
328 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
329         ADD_CSQC_AUTOCVAR(name) \
330         float name; \
331         void RegisterNotification_##name() \
332         { \
333                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
334                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
335         } \
336         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
337
338 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
339         ADD_CSQC_AUTOCVAR(name) \
340         float name; \
341         float cpid; \
342         void RegisterNotification_##name() \
343         { \
344                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
345                 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
346                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
347         } \
348         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
349
350 #define MSG_WEAPON_NOTIF(name,infoname,centername) \
351         ADD_CSQC_AUTOCVAR(name) \
352         float name; \
353         void RegisterNotification_##name() \
354         { \
355                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
356                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
357         } \
358         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
359
360 #define MSG_DEATH_NOTIF(name,infoname,centername) \
361         ADD_CSQC_AUTOCVAR(name) \
362         float name; \
363         void RegisterNotification_##name() \
364         { \
365                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_DEATH_COUNT) \
366                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_DEATH_COUNT, "notifications") \
367         } \
368         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
369
370 // NOW we actually activate the declarations
371 MSG_INFO_NOTIFICATIONS
372 MSG_CENTER_NOTIFICATIONS
373 MSG_WEAPON_NOTIFICATIONS
374 MSG_DEATH_NOTIFICATIONS
375 #undef MSG_INFO_NOTIF
376 #undef MSG_CENTER_NOTIF
377 #undef MSG_WEAPON_NOTIF
378 #undef MSG_DEATH_NOTIF
379
380 // ======================
381 //  Supporting Functions
382 // ======================
383
384 // select between the normal or the gentle message string based on client (or server) settings
385 string normal_or_gentle(string normal, string gentle)
386 {
387         #ifndef MENUQC
388                 #ifdef CSQC
389                 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
390                 #else
391                 if(autocvar_sv_gentle)
392                 #endif
393                         return ((gentle != "") ? gentle : normal);
394                 else
395                         return normal;
396         #else
397                 return normal;
398         #endif
399 }
400
401 float notif_stringcount(string s1, string s2)
402 {
403         float stringcount;
404         if(s1 != NO_STR_ARG) ++stringcount;
405         if(s2 != NO_STR_ARG) ++stringcount;
406         return stringcount;
407 }
408
409 float notif_floatcount(float f1, float f2, float f3)
410 {
411         float floatcount;
412         if(f1 != NO_FL_ARG) ++floatcount;
413         if(f2 != NO_FL_ARG) ++floatcount;
414         if(f3 != NO_FL_ARG) ++floatcount;
415         return floatcount;
416 }
417
418 // get the actual name of a notification and return it as a string
419 string Get_Field_Value(float field, float net_type, float net_name)
420 {
421         string output;
422         
423         #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
424                 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
425                 else if(field == F_STRNUM) { output = ftos(strnum); } \
426                 else if(field == F_FLNUM) { output = ftos(flnum); }
427         
428         switch(net_type)
429         {
430                 case MSG_INFO:
431                 {
432                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
433                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
434                         MSG_INFO_NOTIFICATIONS
435                         #undef MSG_INFO_NOTIF
436                         break;
437                 }
438                 case MSG_CENTER:
439                 {
440                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
441                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
442                         MSG_CENTER_NOTIFICATIONS
443                         #undef MSG_CENTER_NOTIF
444                         break;
445                 }
446                 case MSG_WEAPON:
447                 {
448                         #define MSG_WEAPON_NOTIF(name,infoname,centername) \
449                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name, \
450                                 max(stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername))), \
451                                 max(stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)))) } }
452                         MSG_WEAPON_NOTIFICATIONS
453                         #undef MSG_WEAPON_NOTIF
454                         break;
455                 }
456                 case MSG_DEATH:
457                 {
458                         #define MSG_DEATH_NOTIF(name,infoname,centername) \
459                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name, \
460                                 max(stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername))), \
461                                 max(stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)))) } }
462                         MSG_DEATH_NOTIFICATIONS
463                         #undef MSG_DEATH_NOTIF
464                         break;
465                 }
466         }
467
468         #undef GET_FIELD_VALUE_OUTPUT
469         return output;
470 }
471
472 // team code replace
473 string TCR(string input, string teamcolor, string teamtext)
474
475 {
476         input = strreplace("^TC", teamcolor, input);
477         input = strreplace("^TT", teamtext, input);
478         return input;
479 }
480
481 // color code replace, place inside of sprintf and parse the string
482 string CCR(string input)
483 {
484         // foreground/normal colors
485         input = strreplace("^F1", "^2", input); // primary priority (important names, etc)
486         input = strreplace("^F2", "^3", input); // secondary priority (items, locations, numbers, etc)
487
488         // "kill" colors
489         input = strreplace("^K1", "^1", input); // "bad" or "dangerous" text (death messages against you, kill notifications, etc)
490         input = strreplace("^K2", "^3", input); // similar to above, but less important... OR, a highlight out of above message type
491         input = strreplace("^K3", "^4", input); // "good" or "beneficial" text (you fragging someone, etc)
492
493         // background colors
494         input = strreplace("^BG", "^7", input); // neutral/unimportant text
495         input = strreplace("^N", "^7", input); // "none"-- reset to white...
496         return input;
497 }
498
499
500 // =============================
501 //  Debug/Maintenance Functions
502 // =============================
503
504 #define NOTIF_Write(type,name,text) fputs(fh, (sprintf("seta %s 1 // %s - %s\n", name, type, strreplace("\n", "\\n", text))))
505 void Dump_Notifications(float fh)
506 {
507         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) { NOTIF_Write("MSG_INFO", VAR_TO_TEXT(name), normal); }
508         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) { NOTIF_Write("MSG_CENTER", VAR_TO_TEXT(name), normal); }
509         #define MSG_WEAPON_NOTIF(name,infoname,centername) { NOTIF_Write("MSG_WEAPON", VAR_TO_TEXT(name),sprintf("infoname: %s, centername: %s", VAR_TO_TEXT(infoname), VAR_TO_TEXT(centername))); }
510         #define MSG_DEATH_NOTIF(name,infoname,centername) { NOTIF_Write("MSG_DEATH", VAR_TO_TEXT(name), sprintf("infoname: %s, centername: %s", VAR_TO_TEXT(infoname), VAR_TO_TEXT(centername))); }
511         MSG_INFO_NOTIFICATIONS
512         MSG_CENTER_NOTIFICATIONS
513         MSG_WEAPON_NOTIFICATIONS
514         MSG_DEATH_NOTIFICATIONS
515         #undef MSG_INFO_NOTIF
516         #undef MSG_CENTER_NOTIF
517         #undef MSG_WEAPON_NOTIF
518         #undef MSG_DEATH_NOTIF
519         return;
520 }
521
522
523 // ===============================
524 //  Frontend Notification Pushing
525 // ===============================
526
527 #ifdef CSQC
528 #define KN_MAX_ENTRIES 10
529 float kn_index;
530 float killnotify_times[KN_MAX_ENTRIES];
531 string killnotify_icon[KN_MAX_ENTRIES];
532 string killnotify_attackers[KN_MAX_ENTRIES];
533 string killnotify_victims[KN_MAX_ENTRIES];
534 // 0 = "Y [used by] X", 1 = "X [did action to] Y"
535 void HUD_Notify_Push(string icon, string attacker, string victim)
536 {
537         if(icon != "")
538         {
539                 --kn_index;
540                 if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
541                 killnotify_times[kn_index] = time;
542
543                 // icon
544                 if(killnotify_icon[kn_index]) { strunzone(killnotify_icon[kn_index]); }
545                 killnotify_icon[kn_index] = strzone(icon);
546
547                 // attacker
548                 if(killnotify_attackers[kn_index]) { strunzone(killnotify_attackers[kn_index]); }
549                 killnotify_attackers[kn_index] = strzone(attacker);
550
551                 // victim
552                 if(killnotify_victims[kn_index]) { strunzone(killnotify_victims[kn_index]); }
553                 killnotify_victims[kn_index] = strzone(victim);
554         }
555 }
556
557 void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
558 {
559         //print("Local_Notification(", ftos(net_type), ", ", Get_Field_Value(F_NAME, net_type, net_name), strcat(", ", s1, ", ", s2, ", ", ftos(f1), strcat(", ", ftos(f2), ", ", ftos(f3), ");\n")));
560         switch(net_type)
561         {
562                 case MSG_INFO:
563                 {
564                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
565                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
566                                 { \
567                                         print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); \
568                                         if(strtolower(icon) != "") { HUD_Notify_Push(icon, hudargs); } \
569                                 } }
570                         MSG_INFO_NOTIFICATIONS
571                         #undef MSG_INFO_NOTIF
572                         break;
573                 }
574                 case MSG_CENTER:
575                 {
576                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
577                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
578                                 { \
579                                         centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); \
580                                 } }
581                         MSG_CENTER_NOTIFICATIONS
582                         #undef MSG_CENTER_NOTIF
583                         break;
584                 }
585                 case MSG_WEAPON:
586                 {
587                         #define MSG_WEAPON_NOTIF(name,infoname,centername) \
588                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
589                                 { \
590                                         #if infoname != NO_MSG \
591                                                 Local_Notification(MSG_INFO, infoname, s1, s2, f1, f2, f3); \
592                                         #endif \
593                                         #if centername != NO_MSG \
594                                                 Local_Notification(MSG_CENTER, centername, s1, s2, f1, f2, f3); \
595                                         #endif \
596                                 } }
597                         MSG_WEAPON_NOTIFICATIONS
598                         #undef MSG_WEAPON_NOTIF
599                         break;
600                 }
601                 case MSG_DEATH:
602                 {
603                         #define MSG_DEATH_NOTIF(name,infoname,centername) \
604                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
605                                 { \
606                                         #if infoname != NO_MSG \
607                                                 Local_Notification(MSG_INFO, infoname, s1, s2, f1, f2, f3); \
608                                         #endif \
609                                         #if centername != NO_MSG \
610                                                 Local_Notification(MSG_CENTER, centername, s1, s2, f1, f2, f3); \
611                                         #endif \
612                                 } }
613                         MSG_DEATH_NOTIFICATIONS
614                         #undef MSG_DEATH_NOTIF
615                         break;
616                 }
617         }
618 }
619 #endif
620
621
622 // =========================
623 //  Notification Networking
624 // =========================
625
626 #ifdef CSQC
627 void Read_Notification(void)
628 {
629         float net_type = ReadByte();
630         float net_name = ReadShort();
631
632         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
633         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
634
635         //print("stringcount = ", ftos(stringcount), ", floatcount = ", ftos(floatcount), ".\n");
636         
637         Local_Notification(net_type, net_name,
638                 ((stringcount >= 1) ? ReadString() : ""),
639                 ((stringcount == 2) ? ReadString() : ""),
640                 ((floatcount >= 1) ? ReadLong() : 0),
641                 ((floatcount >= 2) ? ReadLong() : 0),
642                 ((floatcount == 3) ? ReadLong() : 0));
643 }
644 #endif
645
646 #ifdef SVQC
647 void Send_Notification(entity client, float broadcast, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
648 {
649         if(net_type && net_name)
650         {
651                 //print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
652                 dprint("Send_Notification(", ftos(net_type), ", ", Get_Field_Value(F_NAME, net_type, net_name), strcat(", ", s1, ", ", s2, ", ", ftos(f1), strcat(", ", ftos(f2), ", ", ftos(f3), ");\n")));
653
654                 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
655                 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
656                 
657                 if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
658                 if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
659
660                 if(broadcast == MSG_ONE)
661                 {
662                         if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
663                         {
664                                 // personal/direct notification sent to ONE person and their spectators
665                                 msg_entity = client;
666                                 WRITESPECTATABLE_MSG_ONE({
667                                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
668                                         WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
669                                         WriteByte(MSG_ONE, net_type);
670                                         WriteShort(MSG_ONE, net_name);
671                                         if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
672                                         if(stringcount == 2) { WriteString(MSG_ONE, s2); }
673                                         if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
674                                         if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
675                                         if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
676                                 });
677                         }
678                 }
679                 else if(broadcast == MSG_ALL)
680                 {
681                         // global notification sent to EVERYONE
682                         WriteByte(MSG_ALL, SVC_TEMPENTITY);
683                         WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
684                         WriteByte(MSG_ALL, net_type);
685                         WriteShort(MSG_ALL, net_name);
686                         if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
687                         if(stringcount == 2) { WriteString(MSG_ALL, s2); }
688                         if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
689                         if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
690                         if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
691                 }
692                 else { backtrace("Unknown MSG_ type to write with!\n"); }
693
694                 if(!server_is_local && (net_type == MSG_INFO))
695                 {
696                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
697                                 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
698                         MSG_INFO_NOTIFICATIONS
699                         #undef MSG_INFO_NOTIF
700                 }
701         }
702         else { backtrace("Incorrect usage of Send_Notification!\n"); }
703 }
704
705 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
706 {
707         entity tmp_entity;
708         FOR_EACH_REALCLIENT(tmp_entity)
709         {
710                 if(tmp_entity.classname == STR_PLAYER)
711                 if(tmp_entity.team == targetteam)
712                 if(tmp_entity != except)
713                 {
714                         Send_Notification(tmp_entity, MSG_ONE, net_type, net_name, s1, s2, f1, f2, f3);
715                 }
716         }
717 }
718
719 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(world, MSG_ALL, ...)
720 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
721 {
722         entity tmp_entity;
723         FOR_EACH_REALCLIENT(tmp_entity)
724         {
725                 if((tmp_entity.classname == STR_PLAYER) || spectators)
726                 if(tmp_entity != except)
727                 {
728                         Send_Notification(tmp_entity, MSG_ONE, net_type, net_name, s1, s2, f1, f2, f3);
729                 }
730         }
731 }
732
733
734 // =============================
735 //  LEGACY NOTIFICATION SYSTEMS
736 // =============================
737
738 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
739 {
740         WriteByte(MSG_ALL, SVC_TEMPENTITY);
741         WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
742         WriteString(MSG_ALL, s1);
743         WriteString(MSG_ALL, s2);
744         WriteString(MSG_ALL, s3);
745         WriteShort(MSG_ALL, msg);
746         WriteByte(MSG_ALL, type);
747 }
748
749 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
750 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
751 {
752         if (clienttype(e) == CLIENTTYPE_REAL)
753         {
754                 msg_entity = e;
755                 WRITESPECTATABLE_MSG_ONE({
756                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
757                         WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
758                         WriteString(MSG_ONE, s1);
759                         WriteString(MSG_ONE, s2);
760                         WriteShort(MSG_ONE, msg);
761                         WriteByte(MSG_ONE, type);
762                 });
763         }
764 }
765
766 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
767 {
768         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
769         {
770                 msg_entity = e;
771                 WRITESPECTATABLE_MSG_ONE({
772                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
773                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
774                         WriteByte(MSG_ONE, id);
775                         WriteString(MSG_ONE, s);
776                         if (id != 0 && s != "")
777                         {
778                                 WriteByte(MSG_ONE, duration);
779                                 WriteByte(MSG_ONE, countdown_num);
780                         }
781                 });
782         }
783 }
784 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
785 {
786         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
787 }
788 #endif