]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
318432a479ac2a30a9e55142342226e64cf5ad49
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: September, 2012
4 // ================================================
5
6 // main types/groups of notifications
7 #define MSG_INFO 1 // "Global" information messages (sent to console)
8 #define MSG_NOTIFY 2 // "Global" events to be sent to the notification panel
9 #define MSG_CENTER 3 // "Personal" centerprint messages
10 #define MSG_WEAPON 4 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
11
12 // expand multiple arguments into one argument
13 #define XPND4(a,b,c,d) a, b, c, d
14 #define XPND3(a,b,c) a, b, c
15 #define XPND2(a,b) a, b
16
17 // accumulate functions for declarations
18 #define NOTIF_FIRST 1
19 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
20 float NOTIF_INFO_COUNT;
21 float NOTIF_NOTIFY_COUNT;
22 float NOTIF_CENTER_COUNT;
23 float NOTIF_WEAPON_COUNT;
24 float NOTIF_CPID_COUNT;
25
26 #define CHECK_MAX_NOTIFICATIONS(count) if(count == NOTIF_MAX) { error("Maximum notifications hit!\n"); }
27
28 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
29         float name; \
30         void DecNotif_##name() { \
31                 if(!name) { \
32                         name = (NOTIF_FIRST + NOTIF_INFO_COUNT); \
33                         ++NOTIF_INFO_COUNT; } \
34                 CHECK_MAX_NOTIFICATIONS(NOTIF_INFO_COUNT) } \
35         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
36
37 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
38         float name; \
39         void DecNotif_##name() { \
40                 if(!name) { \
41                         name = (NOTIF_FIRST + NOTIF_NOTIFY_COUNT); \
42                         ++NOTIF_NOTIFY_COUNT; } \
43                 CHECK_MAX_NOTIFICATIONS(NOTIF_NOTIFY_COUNT) } \
44         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
45
46 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
47         float name; \
48         float cpid; \
49         void DecNotif_##name() { \
50                 if(!name) { \
51                         name = (NOTIF_FIRST + NOTIF_CENTER_COUNT); \
52                         ++NOTIF_CENTER_COUNT; } \
53                 if(!cpid) { \
54                         cpid = (NOTIF_FIRST + NOTIF_CPID_COUNT); \
55                         ++NOTIF_CPID_COUNT; } \
56                 CHECK_MAX_NOTIFICATIONS(NOTIF_CENTER_COUNT) } \
57         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
58
59 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
60         float name; \
61         void DecNotif_##name() { \
62                 if(!name) { \
63                         name = (NOTIF_FIRST + NOTIF_WEAPON_COUNT); \
64                         ++NOTIF_WEAPON_COUNT; } \
65                 CHECK_MAX_NOTIFICATIONS(NOTIF_WEAPON_COUNT) } \
66         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
67
68
69 // ====================================
70 //  Notifications List and Information
71 // ====================================
72 /*
73  List of all notifications (including identifiers and display information)
74  Format: name, number, args, icon/CPID, normal, gentle
75  Specifications:
76     Name of notification
77     ID number of notification
78     Arguments for sprintf(string, args), if no args needed then use ""
79     Icon/CPID:
80       MSG_NOTIFY: STRING: icon string name for the hud notify panel, "" if no icon is used
81       MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
82     Normal message (string for sprintf when gentle messages are NOT enabled)
83     Gentle message (string for sprintf when gentle messages ARE enabled)
84
85  Messages have ^F1, ^F2, and ^BG in them-- these are replaced
86  with colors according to the cvars the user has chosen.
87     ^F1 = highest priority, "primary"
88     ^F2 = next highest priority, "secondary"
89     ^BG = normal/less important priority, "tertiary"
90
91  Guidlines:
92     ALWAYS start the string with a color, preferably background
93     ALWAYS end messages with a new line
94     NEVER re-declare an event twice
95     ARIRE unir frk jvgu lbhe bja zbgure (gvc sbe zvxrrhfn)
96 */
97 #define MSG_INFO_NOTIFICATIONS \
98         MSG_INFO_NOTIF(DEATH_MARBLES_LOST, XPND3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
99         #undef MSG_INFO_NOTIF
100
101 #define MSG_NOTIFY_NOTIFICATIONS \
102         MSG_NOTIFY_NOTIF(DEATH_MARBLES_LOST2, XPND3(s1, s2, s3), "notify_death", _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
103         #undef MSG_NOTIFY_NOTIF
104
105 #define MSG_CENTER_NOTIFICATIONS \
106         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED, "", CPID_CTF_CAPTURESHIELD, 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."), "") \
107         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE, "", CPID_CTF_CAPTURESHIELD, 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."), "") \
108         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS, XPND2(s1, s2, s3), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s passed the ^F1%s^BG to %s"), "") \
109         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_SENT, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^F1%s^BG to %s"), "") \
110         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_RECEIVED, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^F1%s^BG from %s"), "") \
111         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
112         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, s1, NO_CPID, XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
113         #undef MSG_CENTER_NOTIF
114
115 #define MSG_WEAPON_NOTIFICATIONS \
116         MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, XPND3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
117         #undef MSG_WEAPON_NOTIF
118
119 // NOW we actually activate the declarations
120 MSG_INFO_NOTIFICATIONS
121 MSG_NOTIFY_NOTIFICATIONS
122 MSG_CENTER_NOTIFICATIONS
123 MSG_WEAPON_NOTIFICATIONS
124
125
126 // ======================
127 //  Supporting Functions
128 // ======================
129
130 #ifdef SVQC
131 #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
132 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
133 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
134 #endif
135
136 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
137
138 string normal_or_gentle(string normal, string gentle)
139 {
140         #ifdef CSQC
141         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
142         #else
143         if(autocvar_sv_gentle)
144         #endif
145                 return ((gentle != "") ? gentle : normal);
146         else
147                 return normal;
148 }
149
150 string CCR(string input) // color code replace, place inside of sprintf and parse the string
151 {
152         input = strreplace("^F1", "^3", input);
153         input = strreplace("^F2", "^2", input);
154         input = strreplace("^BG", "^7", input);
155
156         input = strreplace("^N", "^7", input); // "none"-- reset to white
157
158         return input;
159 }
160
161
162 // ===============================
163 //  Frontend Notification Pushing
164 // ===============================
165
166
167 // =========================
168 //  Notification Networking
169 // =========================
170
171 #ifdef CSQC
172 void Read_Notification()
173 {
174         float net_type = ReadByte();
175         float net_name = ReadCoord(); // byte only has 256 selections, we need more than that
176         string s1 = ReadString();
177         string s2 = ReadString();
178         string s3 = ReadString();
179
180         switch(net_type)
181         {
182                 case MSG_INFO:
183                 {
184                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
185                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
186
187                         MSG_INFO_NOTIFICATIONS
188                         break;
189                 }
190
191                 case MSG_NOTIFY:
192                 {
193                         break;
194                 }
195
196                 case MSG_CENTER:
197                 {
198                         #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
199                                 { if(min(NOTIF_MAX, name) == net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
200
201                         MSG_CENTER_NOTIFICATIONS
202                         break;
203                 }
204
205                 case MSG_WEAPON:
206                 {
207                         break;
208                 }
209         }
210 }
211 #endif
212 #ifdef SVQC
213 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
214 {
215         if(net_type && net_name)
216         {
217                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
218                 {
219                         msg_entity = client;
220                         WRITESPECTATABLE_MSG_ONE({
221                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
222                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
223                                 WriteByte(MSG_ONE, net_type);
224                                 WriteCoord(MSG_ONE, net_name);
225                                 WriteString(MSG_ONE, s1);
226                                 WriteString(MSG_ONE, s2);
227                                 WriteString(MSG_ONE, s3);
228                         });
229                 }
230
231                 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
232                 {
233                         switch(net_type)
234                         {
235                                 case MSG_INFO:
236                                 {
237                                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
238                                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
239
240                                         MSG_INFO_NOTIFICATIONS
241                                         break;
242                                 }
243
244                                 case MSG_NOTIFY:
245                                 {
246                                         break;
247                                 }
248                         }
249                 }
250         }
251         else { backtrace("Incorrect usage of Send_Notification!\n"); }
252 }
253
254 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
255 {
256         entity tmp_entity;
257         FOR_EACH_REALCLIENT(tmp_entity)
258         {
259                 if(tmp_entity.classname == STR_PLAYER)
260                 if(tmp_entity.team == targetteam)
261                 if(tmp_entity != except)
262                 {
263                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
264                 }
265         }
266 }
267
268 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
269 {
270         entity tmp_entity;
271         FOR_EACH_REALCLIENT(tmp_entity)
272         {
273                 if((tmp_entity.classname == STR_PLAYER) || spectators)
274                 if(tmp_entity != except)
275                 {
276                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
277                 }
278         }
279 }
280
281 // LEGACY NOTIFICATION SYSTEMS
282 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
283 {
284         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
285         {
286                 msg_entity = e;
287                 WRITESPECTATABLE_MSG_ONE({
288                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
289                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
290                         WriteByte(MSG_ONE, id);
291                         WriteString(MSG_ONE, s);
292                         if (id != 0 && s != "")
293                         {
294                                 WriteByte(MSG_ONE, duration);
295                                 WriteByte(MSG_ONE, countdown_num);
296                         }
297                 });
298         }
299 }
300 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
301 {
302         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
303 }
304 #endif