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