]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Some more cleanup
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: February, 2013
4 // ================================================
5
6 #ifndef MENUQC
7 entity Get_Notif_Ent(float net_type, float net_name)
8 {
9         switch(net_type)
10         {
11                 case MSG_INFO: return msg_info_notifs[net_name - 1];
12                 case MSG_CENTER: return msg_center_notifs[net_name - 1];
13                 case MSG_WEAPON: return msg_weapon_notifs[net_name - 1];
14                 case MSG_DEATH: return msg_death_notifs[net_name - 1];
15         }
16         backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
17         return world;
18 }
19 #endif // ifndef MENUQC
20
21
22 // ===============================
23 //  Frontend Notification Pushing
24 // ===============================
25
26 void Dump_Notifications(float fh, float alsoprint)
27 {
28         #define NOTIF_WRITE(type,name,text) { \
29                 notif_msg = sprintf("seta %s 1 // %s - %s\n", name, #type, strreplace("\n", "\\n", text)); \
30                 fputs(fh, notif_msg); \
31                 if(alsoprint) { print(strreplace("^", "^^", notif_msg)); } }
32
33         #ifndef MENUQC
34         string notif_msg;
35         float i;
36         entity e;
37
38         for(i = 0; i < NOTIF_INFO_COUNT; ++i) {
39                 e = Get_Notif_Ent(MSG_INFO, i);
40                 NOTIF_WRITE(MSG_INFO, e.nent_name, e.nent_string); }
41                 
42         for(i = 0; i < NOTIF_CENTER_COUNT; ++i) {
43                 e = Get_Notif_Ent(MSG_CENTER, i);
44                 NOTIF_WRITE(MSG_CENTER, e.nent_name, e.nent_string); }
45         
46         for(i = 0; i < NOTIF_WEAPON_COUNT; ++i) {
47                 e = Get_Notif_Ent(MSG_WEAPON, i);
48                 NOTIF_WRITE(MSG_WEAPON, e.nent_name, sprintf("infoname: %s, centername: %s",
49                         e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name)); }
50                 
51         for(i = 0; i < NOTIF_DEATH_COUNT; ++i) {
52                 e = Get_Notif_Ent(MSG_DEATH, i);
53                 NOTIF_WRITE(MSG_DEATH, e.nent_name, sprintf("infoname: %s, centername: %s",
54                         e.nent_msginfo.nent_name, e.nent_msgcenter.nent_name)); }
55
56
57         print(sprintf("Notification counts: MSG_INFO = %d, MSG_CENTER = %d, MSG_WEAPON = %d, MSG_DEATH = %d\n",
58                 NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_WEAPON_COUNT, NOTIF_DEATH_COUNT));
59
60         #endif
61         
62         return;
63         #undef NOTIF_WRITE
64 }
65
66 #ifndef MENUQC
67 #define HIT_MAX(count,funcname) if(sel_num == count) { backtrace(sprintf("%s: Hit maximum arguments!\n", funcname)); break; }
68 #define HIT_UNKNOWN(token,funcname) default: { backtrace(sprintf("%s: Hit unknown token in selected string! '%s'\n", funcname, selected)); break; }
69 string Local_Notification_sprintf(string input, string args, 
70         string s1, string s2, string s3, string s4,
71         float f1, float f2, float f3, float f4)
72 {
73         #ifdef NOTIFICATIONS_DEBUG
74         dprint(
75                 sprintf("Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
76                         strreplace("\n", "\\n", input),
77                         args,
78                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
79                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
80                 )
81         );
82         #endif
83         
84         string selected;
85         float sel_num;
86         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
87
88         #ifdef CSQC
89         string tmp_s;
90         #endif
91
92         for(sel_num = 0;(args != "");)
93         {
94                 selected = car(args); args = cdr(args);
95                 HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
96                 switch(strtolower(selected))
97                 {
98                         #define ARG_CASE(prog,selected,result) \
99                                 #ifdef CSQC \
100                                         #if (prog == ARG_BOTH) || (prog == ARG_TRIPLE) || (prog == ARG_CSQC) \
101                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
102                                         #endif \
103                                 #else \
104                                         #if (prog == ARG_BOTH) || (prog == ARG_TRIPLE) || (prog == ARG_SVQC) \
105                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
106                                         #endif \
107                                 #endif
108                         NOTIF_ARGUMENT_LIST
109                         #undef ARG_CASE
110                         HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
111                 }
112         }
113         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
114 }
115 #ifdef CSQC
116 void Local_Notification_HUD_Notify_Push(string icon, string hudargs, string s1, string s2)
117 {
118         string selected;
119         float sel_num;
120         arg_slot[0] = ""; arg_slot[1] = "";
121
122         for(sel_num = 0;(hudargs != "");)
123         {
124                 selected = car(hudargs); hudargs = cdr(hudargs);
125                 HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
126                 switch(strtolower(selected))
127                 {
128                         #define ARG_CASE(prog,selected,result) \
129                                 #if (prog == ARG_TRIPLE) \
130                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
131                                 #endif
132                         NOTIF_ARGUMENT_LIST
133                         #undef ARG_CASE
134                         HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
135                 }
136         }
137         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
138 }
139 #endif
140 #undef HIT_MAX
141 #undef HIT_UNKNOWN
142
143 void Local_Notification(float net_type, float net_name, ...count)
144 {
145         // check supplied type and name for errors
146         string checkargs = "";
147         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
148                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
149                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
150         switch(net_type)
151         {
152                 CHECKARG_TYPENAME(INFO)
153                 CHECKARG_TYPENAME(CENTER)
154                 CHECKARG_TYPENAME(WEAPON)
155                 CHECKARG_TYPENAME(DEATH)
156                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
157         }
158         #undef CHECKARG_TYPENAME
159         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
160
161         entity notif = Get_Notif_Ent(net_type, net_name);
162         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
163         if not(notif.nent_enabled) { print("Local_Notification: Entity was disabled...\n"); return; }
164
165         if((notif.nent_stringcount + notif.nent_floatcount) > count)
166         {
167                 backtrace(sprintf(
168                         strcat(
169                                 "Not enough arguments for Local_Notification! ",
170                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
171                                 "Check the definition and function call for accuracy...?\n"
172                         ),
173                         notif.nent_stringcount, notif.nent_floatcount, count));
174                 return;
175         }
176         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
177         {
178                 backtrace(sprintf(
179                         strcat(
180                                 "Too many arguments for Local_Notification! ",
181                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
182                                 "Check the definition and function call for accuracy...?\n"
183                         ),
184                         notif.nent_stringcount, notif.nent_floatcount, count));
185                 return;
186         }
187
188         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
189         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
190         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
191         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
192         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
193         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
194         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
195         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
196
197         #ifdef NOTIFICATIONS_DEBUG
198         dprint(
199                 sprintf("Local_Notification(%d, %s, %s, %s);\n",
200                         net_type,
201                         notif.nent_name,
202                         sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
203                         sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
204                 )
205         );
206         #endif
207         
208         switch(net_type)
209         {
210                 case MSG_INFO:
211                 {
212                         print(
213                                 Local_Notification_sprintf(
214                                         notif.nent_string,
215                                         notif.nent_args, 
216                                         s1, s2, s3, s4,
217                                         f1, f2, f3, f4)
218                         );
219                         #ifdef CSQC 
220                         if(notif.nent_icon != "")
221                         {
222                                 Local_Notification_HUD_Notify_Push(
223                                         notif.nent_icon,
224                                         notif.nent_hudargs,
225                                         s1, s2);
226                         } 
227                         #endif 
228                         break;
229                 }
230                 
231                 #ifdef CSQC
232                 case MSG_CENTER:
233                 {
234                         centerprint_generic(
235                                 notif.nent_cpid,
236                                 Local_Notification_sprintf(
237                                         notif.nent_string,
238                                         notif.nent_args, 
239                                         s1, s2, s3, s4,
240                                         f1, f2, f3, f4),
241                                 0, 0);
242                         break;
243                 }
244                 #endif
245                 
246                 case MSG_WEAPON:
247                 case MSG_DEATH:
248                 {
249                         if(notif.nent_msginfo)
250                         if(notif.nent_msginfo.nent_enabled)
251                         {
252                                 Local_Notification_Without_VarArgs(
253                                         MSG_INFO,
254                                         notif.nent_msginfo.nent_id, 
255                                         notif.nent_msginfo.nent_stringcount, 
256                                         notif.nent_msginfo.nent_floatcount, 
257                                         s1, s2, s3, s4,
258                                         f1, f2, f3, f4);
259                         }
260                         #ifdef CSQC
261                         if(notif.nent_msgcenter)
262                         if(notif.nent_msgcenter.nent_enabled)
263                         {
264                                 Local_Notification_Without_VarArgs(
265                                         MSG_CENTER,
266                                         notif.nent_msgcenter.nent_id, 
267                                         notif.nent_msgcenter.nent_stringcount, 
268                                         notif.nent_msgcenter.nent_floatcount, 
269                                         s1, s2, s3, s4,
270                                         f1, f2, f3, f4); 
271                         }
272                         #endif
273                         break;
274                 }
275         }
276 }
277
278 void Local_Notification_Without_VarArgs(float net_type, float net_name,
279         float stringcount, float floatcount,
280         string s1, string s2, string s3, string s4,
281         float f1, float f2, float f3, float f4)
282 {
283         #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) { Local_Notification(net_type, net_name, args); return; }
284         EIGHT_VARS_TO_VARARGS_VARLIST
285         #undef VARITEM
286         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
287 }
288
289
290 // =========================
291 //  Notification Networking
292 // =========================
293
294 #ifdef CSQC
295 void Read_Notification(float is_new)
296 {
297         float net_type = ReadByte();
298         float net_name = ReadShort();
299
300         entity notif = Get_Notif_Ent(net_type, net_name);
301         if not(notif) { print("Read_Notification: Could not find notification entity!\n"); return; }
302
303         string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
304         string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
305         string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
306         string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
307         float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
308         float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
309         float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
310         float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
311
312         #ifdef NOTIFICATIONS_DEBUG
313         dprint(sprintf("Read_Notification(%d) at %f: net_name = %s.\n", is_new, time, notif.nent_name));
314         #endif
315         
316         if(is_new) { Local_Notification_Without_VarArgs(net_type, net_name, notif.nent_stringcount, notif.nent_floatcount, s1, s2, s3, s4, f1, f2, f3, f4); }
317 }
318 #endif
319
320 #ifdef SVQC
321 void Notification_Remove()
322 {
323         float i;
324         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
325         remove(self);
326 }
327
328 float Write_Notification(entity client, float sf)
329 {
330         float i, send = FALSE;
331         
332         switch(self.nent_broadcast)
333         {
334                 case NOTIF_ONE: { if((client == self.nent_client) || (client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
335                 case NOTIF_ONE_ONLY: { if(client == self.nent_client) { send = TRUE; } break; }
336                 case NOTIF_TEAM: { if((client.team == self.nent_client.team) || (client.classname == STR_SPECTATOR && client.enemy.team == self.nent_client.team)) { send = TRUE; } break; }
337                 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; }
338                 case NOTIF_ANY: { send = TRUE; break; }
339                 case NOTIF_ANY_EXCEPT: { if((client != self.nent_client) && !(client.classname == STR_SPECTATOR && client.enemy == self.nent_client)) { send = TRUE; } break; }
340                 default: { send = FALSE; break; }
341         }
342
343         if(send)
344         {               
345                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
346                 WriteByte(MSG_ENTITY, self.nent_net_type);
347                 WriteShort(MSG_ENTITY, self.nent_net_name);
348                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
349                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
350         }
351
352         return send; 
353 }
354
355 void Send_Notification(float broadcast, entity client,
356         float net_type, float net_name, ...count)
357 {
358         // check supplied broadcast, target, type, and name for errors
359         string checkargs = "";
360         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
361                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
362                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
363         switch(net_type)
364         {
365                 CHECKARG_TYPENAME(INFO)
366                 CHECKARG_TYPENAME(CENTER)
367                 CHECKARG_TYPENAME(WEAPON)
368                 CHECKARG_TYPENAME(DEATH)
369                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
370         }
371         #undef CHECKARG_TYPENAME
372         if(checkargs != "") { checkargs = strcat(checkargs, " "); }
373         switch(broadcast)
374         {
375                 case NOTIF_ONE:
376                 case NOTIF_ONE_ONLY: { if(clienttype(client) == CLIENTTYPE_NOTACLIENT) { checkargs = sprintf("%sNo client provided!", checkargs); } break; }
377                 case NOTIF_ANY_EXCEPT: { if(clienttype(client) == CLIENTTYPE_NOTACLIENT) { checkargs = sprintf("%sException can't be a non-client!", checkargs); } break; }
378                 case NOTIF_ANY: { if(client) { checkargs = sprintf("%sEntity provided when world was required!", checkargs); } break; }
379                 case NOTIF_TEAM:
380                 case NOTIF_TEAM_EXCEPT:
381                 {
382                         if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
383                         else if(clienttype(client) == CLIENTTYPE_NOTACLIENT)
384                         {
385                                 if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
386                                 else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
387                         }
388                         break;
389                 }
390                 default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
391         }
392         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
393
394         // retreive counts for the arguments of this notification
395         entity notif = Get_Notif_Ent(net_type, net_name);
396         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
397
398         if((notif.nent_stringcount + notif.nent_floatcount) > count)
399         {
400                 backtrace(sprintf(
401                         strcat(
402                                 "Not enough arguments for Send_Notification! ",
403                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
404                                 "Check the definition and function call for accuracy...?\n"
405                         ),
406                         notif.nent_stringcount, notif.nent_floatcount, count));
407                 return;
408         }
409         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
410         {
411                 backtrace(sprintf(
412                         strcat(
413                                 "Too many arguments for Send_Notification! ",
414                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
415                                 "Check the definition and function call for accuracy...?\n"
416                         ),
417                         notif.nent_stringcount, notif.nent_floatcount, count));
418                 return;
419         }
420         
421         #ifdef NOTIFICATIONS_DEBUG
422         dprint(
423                 sprintf("Send_Notification(%d, %d, %s, stringcount: %d, floatcount: %d, varargs: %d);\n",
424                         broadcast,
425                         net_type,
426                         notif.nent_name,
427                         notif.nent_stringcount,
428                         notif.nent_floatcount,
429                         count
430                 )
431         );
432         #endif
433
434         entity net_notif = spawn();
435         net_notif.nent_broadcast = broadcast;
436         net_notif.nent_client = client;
437         net_notif.nent_net_type = net_type;
438         net_notif.nent_net_name = net_name;
439         net_notif.nent_stringcount = notif.nent_stringcount;
440         net_notif.nent_floatcount = notif.nent_floatcount;
441         
442         float i;
443         for(i = 0; i < net_notif.nent_stringcount; ++i) { net_notif.nent_strings[i] = strzone(...(i, string)); }
444         for(i = 0; i < net_notif.nent_floatcount; ++i) { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
445         
446         net_notif.think = Notification_Remove;
447         net_notif.nextthink = (time + 0.5); 
448
449         Net_LinkEntity(net_notif, FALSE, 0, Write_Notification);
450
451         if((!server_is_local) && (broadcast == NOTIF_ANY || broadcast == NOTIF_ANY_EXCEPT) && (net_type != MSG_CENTER))
452                 { Local_Notification_Without_VarArgs(net_type, net_name, notif.nent_stringcount, notif.nent_floatcount,
453                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3), IFFL(0), IFFL(1), IFFL(2), IFFL(3)); }
454 }
455
456 void Send_Notification_Without_VarArgs(float broadcast, entity client,
457         float net_type, float net_name,
458         float stringcount, float floatcount,
459         string s1, string s2, string s3, string s4,
460         float f1, float f2, float f3, float f4)
461 {
462         #define VARITEM(stringc,floatc,args) if((stringcount == stringc) && (floatcount == floatc)) \
463                 { Send_Notification(broadcast, client, net_type, net_name, args); return; }
464         EIGHT_VARS_TO_VARARGS_VARLIST
465         #undef VARITEM
466         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
467 }
468
469 void Send_Notification_Legacy_Wrapper(float broadcast, entity client,
470         float net_type, float net_name,
471         string s1, string s2,
472         float f1, float f2, float f3)
473 {
474         entity notif = Get_Notif_Ent(net_type, net_name);
475         Send_Notification_Without_VarArgs(broadcast, client, net_type, net_name,
476                 notif.nent_stringcount, notif.nent_floatcount,
477                 s1, s2, "", "", f1, f2, f3, 0);
478 }
479
480
481 // =============================
482 //  LEGACY NOTIFICATION SYSTEMS
483 // =============================
484
485 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
486 {
487         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
488         {
489                 msg_entity = e;
490                 WRITESPECTATABLE_MSG_ONE({
491                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
492                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
493                         WriteByte(MSG_ONE, id);
494                         WriteString(MSG_ONE, s);
495                         if (id != 0 && s != "")
496                         {
497                                 WriteByte(MSG_ONE, duration);
498                                 WriteByte(MSG_ONE, countdown_num);
499                         }
500                 });
501         }
502 }
503 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
504 {
505         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
506 }
507 #endif // ifdef SVQC
508 #endif // ifndef MENUQC