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