]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Merge remote-tracking branch 'origin/master' into samual/notification_rewrite
[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 // ======================
7 //  Supporting Functions
8 // ======================
9
10 // team code replace
11 string TCR(string input, string teamcolor, string teamtext) // TODO: MOVE TO UTIL.QC
12 {
13         input = strreplace("^TC", teamcolor, input);
14         input = strreplace("^TT", teamtext, input);
15         return input;
16 }
17
18 // color code replace, place inside of sprintf and parse the string
19 string CCR(string input) // TODO: MOVE TO UTIL.QC
20 {
21         // foreground/normal colors
22         input = strreplace("^F1", "^2", input); // primary priority (important names, etc)
23         input = strreplace("^F2", "^3", input); // secondary priority (items, locations, numbers, etc)
24
25         // "kill" colors
26         input = strreplace("^K1", "^1", input); // "bad" or "dangerous" text (death messages against you, kill notifications, etc)
27         input = strreplace("^K2", "^3", input); // similar to above, but less important... OR, a highlight out of above message type
28         input = strreplace("^K3", "^4", input); // "good" or "beneficial" text (you fragging someone, etc)
29
30         // background colors
31         input = strreplace("^BG", "^7", input); // neutral/unimportant text
32         input = strreplace("^N", "^7", input); // "none"-- reset to white...
33         return input;
34 }
35
36 #ifndef MENUQC
37 // select between the normal or the gentle message string based on client (or server) settings
38 string normal_or_gentle(string normal, string gentle)
39 {
40         #ifdef CSQC
41         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
42         #else
43         if(autocvar_sv_gentle)
44         #endif
45                 return ((gentle != "") ? gentle : normal);
46         else
47                 return normal;
48 }
49
50 float notif_checkstring(string input)
51 {
52         if not(input == "") { return TRUE; }
53         else { return FALSE; }
54 }
55
56 // get the actual name of a notification and return it as a string
57 string Get_Field_Value(float field, float net_type, float net_name)
58 {
59         string output = "";
60         
61         #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
62                 switch(field) { \
63                         case F_NAME: { output = VAR_TO_TEXT(name); break; } \
64                         case F_STRNUM: { output = ftos(strnum); break; } \
65                         case F_FLNUM: { output = ftos(flnum); break; } }
66         
67         switch(net_type)
68         {
69                 case MSG_INFO:
70                 {
71                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
72                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
73                         MSG_INFO_NOTIFICATIONS
74                         #undef MSG_INFO_NOTIF
75                         break;
76                 }
77                 case MSG_CENTER:
78                 {
79                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
80                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
81                         MSG_CENTER_NOTIFICATIONS
82                         #undef MSG_CENTER_NOTIF
83                         break;
84                 }
85                 case MSG_WEAPON:
86                 {
87                         #define MSG_WEAPON_NOTIF(name,infoname,centername) \
88                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name, \
89                                 max(stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername))), \
90                                 max(stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)))) } }
91                         MSG_WEAPON_NOTIFICATIONS
92                         #undef MSG_WEAPON_NOTIF
93                         break;
94                 }
95                 case MSG_DEATH:
96                 {
97                         #define MSG_DEATH_NOTIF(name,infoname,centername) \
98                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name, \
99                                 max(stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername))), \
100                                 max(stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)))) } }
101                         MSG_DEATH_NOTIFICATIONS
102                         #undef MSG_DEATH_NOTIF
103                         break;
104                 }
105         }
106
107         #undef GET_FIELD_VALUE_OUTPUT
108         return output;
109 }
110 #endif // ifndef MENUQC
111
112
113 // ===============================
114 //  Frontend Notification Pushing
115 // ===============================
116
117 void Dump_Notifications(float fh, float alsoprint)
118 {
119         float MSG_INFO_NOTIFS = 0, MSG_CENTER_NOTIFS = 0, MSG_WEAPON_NOTIFS = 0, MSG_DEATH_NOTIFS = 0;
120         string notif_msg;
121
122         #define NOTIF_WRITE(type,name,text) notif_msg = sprintf("seta %s 1 // %s - %s\n", name, type, strreplace("\n", "\\n", text)); fputs(fh, notif_msg); if(alsoprint) { print(strreplace("^", "^^", notif_msg)); }
123         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) { ++MSG_INFO_NOTIFS; NOTIF_WRITE("MSG_INFO", VAR_TO_TEXT(name), normal) }
124         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) { ++MSG_CENTER_NOTIFS; NOTIF_WRITE("MSG_CENTER", VAR_TO_TEXT(name), normal) }
125         #define MSG_WEAPON_NOTIF(name,infoname,centername) { ++MSG_WEAPON_NOTIFS; NOTIF_WRITE("MSG_WEAPON", VAR_TO_TEXT(name),sprintf("infoname: %s, centername: %s", VAR_TO_TEXT(infoname), VAR_TO_TEXT(centername))) }
126         #define MSG_DEATH_NOTIF(name,infoname,centername) { ++MSG_DEATH_NOTIFS; NOTIF_WRITE("MSG_DEATH", VAR_TO_TEXT(name), sprintf("infoname: %s, centername: %s", VAR_TO_TEXT(infoname), VAR_TO_TEXT(centername))) }
127         MSG_INFO_NOTIFICATIONS
128         MSG_CENTER_NOTIFICATIONS
129         MSG_WEAPON_NOTIFICATIONS
130         MSG_DEATH_NOTIFICATIONS
131         #undef NOTIF_WRITE
132         #undef MSG_INFO_NOTIF
133         #undef MSG_CENTER_NOTIF
134         #undef MSG_WEAPON_NOTIF
135         #undef MSG_DEATH_NOTIF
136         
137         print(sprintf("Notification counts: MSG_INFO = %d, MSG_CENTER = %d, MSG_WEAPON = %d, MSG_DEATH = %d\n", MSG_INFO_NOTIFS, MSG_CENTER_NOTIFS, MSG_WEAPON_NOTIFS, MSG_DEATH_NOTIFS));
138         return;
139 }
140
141 #ifndef MENUQC
142 #ifdef CSQC
143 void HUD_Notify_Push(string icon, string attacker, string victim)
144 {
145         if(icon != "")
146         {
147                 --kn_index;
148                 if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
149                 killnotify_times[kn_index] = time;
150
151                 // icon
152                 if(killnotify_icon[kn_index]) { strunzone(killnotify_icon[kn_index]); }
153                 killnotify_icon[kn_index] = strzone(icon);
154
155                 // attacker
156                 if(killnotify_attackers[kn_index]) { strunzone(killnotify_attackers[kn_index]); }
157                 killnotify_attackers[kn_index] = strzone(attacker);
158
159                 // victim
160                 if(killnotify_victims[kn_index]) { strunzone(killnotify_victims[kn_index]); }
161                 killnotify_victims[kn_index] = strzone(victim);
162         }
163 }
164 #endif // ifdef CSQC
165
166 void Local_Notification(float net_type, float net_name, ...count)
167 {
168         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
169         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
170
171         string s1 = ((0 < stringcount) ? ...(0, string) : NO_STR_ARG);
172         string s2 = ((1 < stringcount) ? ...(1, string) : NO_STR_ARG);
173         string s3 = ((2 < stringcount) ? ...(2, string) : NO_STR_ARG);
174         string s4 = ((3 < stringcount) ? ...(3, string) : NO_STR_ARG);
175         float f1 = ((stringcount < count) ? ...(stringcount, float) : NO_FL_ARG);
176         float f2 = (((stringcount + 1) < count) ? ...((stringcount + 1), float) : NO_FL_ARG);
177         float f3 = (((stringcount + 2) < count) ? ...((stringcount + 2), float) : NO_FL_ARG);
178         float f4 = (((stringcount + 3) < count) ? ...((stringcount + 3), float) : NO_FL_ARG);
179         
180         dprint("Local_Notification(", ftos(net_type), ", ", Get_Field_Value(F_NAME, net_type, net_name), strcat(", ", s1, ", ", s2, ", ", s3, ", ", s4, ", "), strcat(ftos(f1), strcat(", ", ftos(f2), ", ", ftos(f3), ", ", ftos(f4), ");\n")));
181         dprint("  ^--: stringcount: ", ftos(stringcount), ", floatcount: ", ftos(floatcount), ".\n");
182
183         if((stringcount + floatcount) > count) { backtrace(strcat("Not enough arguments for Local_Notification! ", strcat("stringcount(", ftos(stringcount), ") + floatcount(", ftos(floatcount), ")"), " > count(", ftos(count), ").\nCheck the notification definition and the function call for accuracy...?\n")); return; }
184         else if((stringcount + floatcount) < count) { backtrace(strcat("Too many arguments for Local_Notification! ", strcat("stringcount(", ftos(stringcount), ") + floatcount(", ftos(floatcount), ")"), " < count(", ftos(count), ").\nCheck the notification definition and the function call for accuracy...?\n")); return; }
185
186         switch(net_type)
187         {
188                 case MSG_INFO:
189                 {
190                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
191                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
192                                 { \
193                                         if(notif_checkstring(normal)) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } \
194                                         #ifdef CSQC \
195                                                 if(notif_checkstring(icon)) { HUD_Notify_Push(icon, hudargs); } \
196                                         #endif \
197                                 } }
198                         MSG_INFO_NOTIFICATIONS
199                         #undef MSG_INFO_NOTIF
200                         break;
201                 }
202                 #ifdef CSQC
203                 case MSG_CENTER:
204                 {
205                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
206                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
207                                 { \
208                                         if(notif_checkstring(normal)) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } \
209                                 } }
210                         MSG_CENTER_NOTIFICATIONS
211                         #undef MSG_CENTER_NOTIF
212                         break;
213                 }
214                 #endif
215                 case MSG_WEAPON:
216                 {
217                         #define MSG_WEAPON_NOTIF(name,infoname,centername) \
218                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
219                                 { \
220                                         #if infoname != NO_MSG \
221                                                 Local_Notification_Without_VarArgs(MSG_INFO, infoname, \
222                                                         stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), \
223                                                         stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), \
224                                                         s1, s2, s3, s4, f1, f2, f3, f4); \
225                                         #endif \
226                                         #ifdef CSQC \
227                                                 #if centername != NO_MSG \
228                                                         Local_Notification_Without_VarArgs(MSG_CENTER, centername, \
229                                                                 stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername)), \
230                                                                 stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)), \
231                                                                 s1, s2, s3, s4, f1, f2, f3, f4); \
232                                                 #endif \
233                                         #endif \
234                                 } }
235                         MSG_WEAPON_NOTIFICATIONS
236                         #undef MSG_WEAPON_NOTIF
237                         break;
238                 }
239                 case MSG_DEATH:
240                 {
241                         #define MSG_DEATH_NOTIF(name,infoname,centername) \
242                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
243                                 { \
244                                         #if infoname != NO_MSG \
245                                                 Local_Notification_Without_VarArgs(MSG_INFO, infoname, \
246                                                         stof(Get_Field_Value(F_STRNUM, MSG_INFO, infoname)), \
247                                                         stof(Get_Field_Value(F_FLNUM, MSG_INFO, infoname)), \
248                                                         s1, s2, s3, s4, f1, f2, f3, f4); \
249                                         #endif \
250                                         #ifdef CSQC \
251                                                 #if centername != NO_MSG \
252                                                         Local_Notification_Without_VarArgs(MSG_CENTER, centername, \
253                                                                 stof(Get_Field_Value(F_STRNUM, MSG_CENTER, centername)), \
254                                                                 stof(Get_Field_Value(F_FLNUM, MSG_CENTER, centername)), \
255                                                                 s1, s2, s3, s4, f1, f2, f3, f4); \
256                                                 #endif \
257                                         #endif \
258                                 } }
259                         MSG_DEATH_NOTIFICATIONS
260                         #undef MSG_DEATH_NOTIF
261                         break;
262                 }
263         }
264 }
265
266 void Local_Notification_Without_VarArgs(float net_type, float net_name, float stringcount, float floatcount, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4)
267 {
268         #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) { Local_Notification(net_type, net_name, args); return; }
269         EIGHT_VARS_TO_VARARGS_VARLIST
270         #undef VARITEM
271
272         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
273 }
274
275
276 // =========================
277 //  Notification Networking
278 // =========================
279
280 #ifdef CSQC
281 void Read_Notification(float is_new)
282 {
283         float net_type = ReadByte();
284         float net_name = ReadShort();
285
286         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
287         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
288
289         string s1 = ((stringcount >= 1) ? ReadString() : NO_STR_ARG);
290         string s2 = ((stringcount >= 2) ? ReadString() : NO_STR_ARG);
291         string s3 = ((stringcount >= 3) ? ReadString() : NO_STR_ARG);
292         string s4 = ((stringcount == 4) ? ReadString() : NO_STR_ARG);
293         float f1 = ((floatcount >= 1) ? ReadLong() : NO_FL_ARG);
294         float f2 = ((floatcount >= 2) ? ReadLong() : NO_FL_ARG);
295         float f3 = ((floatcount >= 3) ? ReadLong() : NO_FL_ARG);
296         float f4 = ((floatcount == 4) ? ReadLong() : NO_FL_ARG);
297
298         if(is_new) { Local_Notification_Without_VarArgs(net_type, net_name, stringcount, floatcount, s1, s2, s3, s4, f1, f2, f3, f4); }
299         else { print("received old notification? net_name = ", ftos(net_name), ".\n"); }
300 }
301 #endif
302
303 #ifdef SVQC
304 void Notification_Remove()
305 {
306         float i;
307         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
308         remove(self);
309 }
310
311 float Write_Notification(entity client, float sf)
312 {
313         float i, send = FALSE;
314         
315         switch(self.nent_broadcast)
316         {
317                 case NOTIF_ONE: { if((client == self.nent_client) || (client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
318                 case NOTIF_ONE_ONLY: { if(client == self.nent_client) { send = TRUE; } break; }
319                 case NOTIF_TEAM: { if((client.team == self.nent_client.team) || (client.classname == STR_SPECTATOR && client.enemy.team == self.nent_client.team)) { send = TRUE; } break; }
320                 case NOTIF_TEAM_EXCEPT: { if(((client != self.nent_client) && (client.team == self.nent_client.team) && !(client.classname == STR_SPECTATOR && client.enemy == self.nent_client))) { send = TRUE; } break; }
321                 case NOTIF_ANY: { send = TRUE; break; }
322                 case NOTIF_ANY_EXCEPT: { if((client != self.nent_client) && !(client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
323                 default: { send = FALSE; break; }
324         }
325
326         if(send)
327         {               
328                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
329                 WriteByte(MSG_ENTITY, self.nent_net_type);
330                 WriteShort(MSG_ENTITY, self.nent_net_name);
331                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
332                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
333         }
334
335         return send; 
336 }
337
338 void Send_Notification(float broadcast, entity client, float net_type, float net_name, ...count)
339 {
340         if(broadcast && net_type && net_name)
341         {
342                 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
343                 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
344                 float i;
345
346                 dprint("Send_Notification(", ftos(broadcast), ", ", ftos(net_type), ", ", Get_Field_Value(F_NAME, net_type, net_name), strcat(", ", ftos(count), ");\n"));
347                 dprint("  ^--: stringcount: ", ftos(stringcount), ", floatcount: ", ftos(floatcount), ".\n");
348
349                 if((stringcount + floatcount) > count) { backtrace(strcat("Not enough arguments for Send_Notification! ", strcat("stringcount(", ftos(stringcount), ") + floatcount(", ftos(floatcount), "),"), " > count(", ftos(count), ").\nCheck the notification definition and the function call for accuracy...?\n")); return; }
350                 else if((stringcount + floatcount) < count) { backtrace(strcat("Too many arguments for Send_Notification! ", strcat("stringcount(", ftos(stringcount), ") + floatcount(", ftos(floatcount), "),"), " < count(", ftos(count), ").\nCheck the notification definition and the function call for accuracy...?\n")); return; }
351
352                 entity notif = spawn();
353                 notif.nent_broadcast = broadcast;
354                 notif.nent_client = client;
355                 notif.nent_net_type = net_type;
356                 notif.nent_net_name = net_name;
357                 notif.nent_stringcount = stringcount;
358                 notif.nent_floatcount = floatcount; 
359                 for(i = 0; i < stringcount; ++i) { notif.nent_strings[i] = strzone(...(i, string)); }
360                 for(i = 0; i < floatcount; ++i) { notif.nent_floats[i] = ...((stringcount + i), float); }
361                 
362                 notif.think = Notification_Remove;
363                 notif.nextthink = (time + 0.5); 
364
365                 Net_LinkEntity(notif, FALSE, 0, Write_Notification);
366
367                 if(!server_is_local)
368                 {
369                         Local_Notification_Without_VarArgs(net_type, net_name, stringcount, floatcount, IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3), IFFL(0), IFFL(1), IFFL(2), IFFL(3));
370                 }
371         }
372         else { backtrace("Incorrect usage of Send_Notification!\n"); }
373 }
374
375 void Send_Notification_Without_VarArgs(float broadcast, entity client, float net_type, float net_name, float stringcount, float floatcount, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4)
376 {               
377         #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) { Send_Notification(broadcast, client, net_type, net_name, args); return; }
378         EIGHT_VARS_TO_VARARGS_VARLIST
379         #undef VARITEM
380
381         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
382 }
383
384 void Send_Notification_Legacy_Wrapper(float broadcast, entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
385 {
386         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
387         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
388         Send_Notification_Without_VarArgs(broadcast, client, net_type, net_name, stringcount, floatcount, s1, s2, NO_STR_ARG, NO_STR_ARG, f1, f2, f3, NO_FL_ARG);
389 }
390
391
392 // =============================
393 //  LEGACY NOTIFICATION SYSTEMS
394 // =============================
395
396 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
397 {
398         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
399         {
400                 msg_entity = e;
401                 WRITESPECTATABLE_MSG_ONE({
402                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
403                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
404                         WriteByte(MSG_ONE, id);
405                         WriteString(MSG_ONE, s);
406                         if (id != 0 && s != "")
407                         {
408                                 WriteByte(MSG_ONE, duration);
409                                 WriteByte(MSG_ONE, countdown_num);
410                         }
411                 });
412         }
413 }
414 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
415 {
416         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
417 }
418 #endif // ifdef SVQC
419 #endif // ifndef MENUQC