]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Tons of cleanup, better comments
[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 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
18 #ifdef SVQC
19 #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
20 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
21 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
22 #endif
23
24 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
25
26 // select between the normal or the gentle message string based on client (or server) settings
27 string normal_or_gentle(string normal, string gentle)
28 {
29         #ifdef CSQC
30         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
31         #else
32         if(autocvar_sv_gentle)
33         #endif
34                 return ((gentle != "") ? gentle : normal);
35         else
36                 return normal;
37 }
38
39 // color code replace, place inside of sprintf and parse the string
40 string CCR(string input)
41 {
42         input = strreplace("^F1", "^3", input);
43         input = strreplace("^F2", "^2", input);
44         input = strreplace("^BG", "^7", input);
45
46         input = strreplace("^N", "^7", input); // "none"-- reset to white
47
48         return input;
49 }
50
51
52 // ====================================
53 //  Notifications List and Information
54 // ====================================
55 /*
56  List of all notifications (including identifiers and display information)
57  Format: name, args, *icon/CPID, *durcnt, normal, gentle
58  Asterisked fields are not present in all notification types.
59  Specifications:
60     Name of notification
61     Arguments for sprintf(string, args), if no args needed then use ""
62     *Icon/CPID:
63       MSG_NOTIFY: STRING: icon string name for the hud notify panel, "" if no icon is used
64       MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
65     *Duration/Countdown:
66       MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
67     Normal message (string for sprintf when gentle messages are NOT enabled)
68     Gentle message (string for sprintf when gentle messages ARE enabled)
69
70  Messages have ^F1, ^F2, and ^BG in them-- these are replaced
71  with colors according to the cvars the user has chosen.
72     ^F1 = highest priority, "primary"
73     ^F2 = next highest priority, "secondary"
74     ^BG = normal/less important priority, "tertiary"
75
76  Guidlines (please try and follow these):
77     ALWAYS start the string with a color, preferably background.
78     ALWAYS end messages with a new line.
79     ALWAYS properly use tab spacing to even out the notifications.
80     NEVER re-declare an event twice.
81     NEVER add or remove fields from the format, it SHOULD already work.
82     ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
83     Be clean and simple with your notification naming, nothing too long.
84     Keep the notifications in alphabetical order.
85 */
86 #define MSG_INFO_NOTIFICATIONS \
87         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"), "") \
88         #undef MSG_INFO_NOTIF
89
90 #define MSG_NOTIFY_NOTIFICATIONS \
91         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"), "") \
92         #undef MSG_NOTIFY_NOTIF
93
94 #define MSG_CENTER_NOTIFICATIONS \
95         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."), "") \
96         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."), "") \
97         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"), "") \
98         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"), "") \
99         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"), "") \
100         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN,                               s1,                                     CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou returned the ^F1%s"), "") \
101         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE,                              s1,                                     NO_CPID,                                XPND2(0, 0), _("^BGYou captured the ^F1%s"), "") \
102         #undef MSG_CENTER_NOTIF
103
104 #define MSG_WEAPON_NOTIFICATIONS \
105         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"), "") \
106         #undef MSG_WEAPON_NOTIF
107
108
109 // ====================================
110 //  Initialization/Create Declarations
111 // ====================================
112
113 #define NOTIF_FIRST 1
114 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
115 float NOTIF_INFO_COUNT;
116 float NOTIF_NOTIFY_COUNT;
117 float NOTIF_CENTER_COUNT;
118 float NOTIF_WEAPON_COUNT;
119 float NOTIF_CPID_COUNT;
120
121 #define CHECK_FIELD_AND_COUNT(field,count) if(!field) { field = (NOTIF_FIRST + count); ++count; }
122 #define CHECK_MAX_NOTIFICATIONS(count) if(count == NOTIF_MAX) { error("Maximum notifications hit!\n"); }
123         
124 #define MSG_INFO_NOTIF(name,args,normal,gentle) \
125         float name; \
126         void DecNotif_##name() \
127         { \
128                 CHECK_FIELD_AND_COUNT(name, NOTIF_INFO_COUNT) \
129                 CHECK_MAX_NOTIFICATIONS(NOTIF_INFO_COUNT) \
130         } \
131         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
132
133 #define MSG_NOTIFY_NOTIF(name,args,icon,normal,gentle) \
134         float name; \
135         void DecNotif_##name() \
136         { \
137                 CHECK_FIELD_AND_COUNT(name, NOTIF_NOTIFY_COUNT) \
138                 CHECK_MAX_NOTIFICATIONS(NOTIF_NOTIFY_COUNT) \
139         } \
140         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
141
142 #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
143         float name; \
144         float cpid; \
145         void DecNotif_##name() \
146         { \
147                 CHECK_FIELD_AND_COUNT(name, NOTIF_CENTER_COUNT) \
148                 CHECK_FIELD_AND_COUNT(cpid, NOTIF_CPID_COUNT) \
149                 CHECK_MAX_NOTIFICATIONS(NOTIF_CENTER_COUNT) \
150         } \
151         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
152
153 #define MSG_WEAPON_NOTIF(name,args,normal,gentle) \
154         float name; \
155         void DecNotif_##name() \
156         { \
157                 CHECK_FIELD_AND_COUNT(name, NOTIF_WEAPON_COUNT) \
158                 CHECK_MAX_NOTIFICATIONS(NOTIF_WEAPON_COUNT) \
159         } \
160         ACCUMULATE_FUNCTION(DecNotifs, DecNotif_##name)
161
162 // NOW we actually activate the declarations
163 MSG_INFO_NOTIFICATIONS
164 MSG_NOTIFY_NOTIFICATIONS
165 MSG_CENTER_NOTIFICATIONS
166 MSG_WEAPON_NOTIFICATIONS
167
168
169 // ===============================
170 //  Frontend Notification Pushing
171 // ===============================
172
173 #ifdef CSQC
174 void Local_Notification(float net_type, float net_name, string s1, string s2, string s3)
175 {
176         switch(net_type)
177         {
178                 case MSG_INFO:
179                 {
180                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
181                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
182
183                         MSG_INFO_NOTIFICATIONS
184                         break;
185                 }
186
187                 case MSG_NOTIFY:
188                 {
189                         break;
190                 }
191
192                 case MSG_CENTER:
193                 {
194                         #define MSG_CENTER_NOTIF(name,args,cpid,durcnt,normal,gentle) \
195                                 { if(min(NOTIF_MAX, name) == net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
196
197                         MSG_CENTER_NOTIFICATIONS
198                         break;
199                 }
200
201                 case MSG_WEAPON:
202                 {
203                         break;
204                 }
205         }
206 }
207 #endif
208
209
210 // =========================
211 //  Notification Networking
212 // =========================
213
214 #ifdef SVQC
215 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
216 {
217         if(net_type && net_name)
218         {
219                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
220                 {
221                         msg_entity = client;
222                         WRITESPECTATABLE_MSG_ONE({
223                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
224                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
225                                 WriteByte(MSG_ONE, net_type);
226                                 WriteCoord(MSG_ONE, net_name);
227                                 WriteString(MSG_ONE, s1);
228                                 WriteString(MSG_ONE, s2);
229                                 WriteString(MSG_ONE, s3);
230                         });
231                 }
232
233                 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
234                 {
235                         switch(net_type)
236                         {
237                                 case MSG_INFO:
238                                 {
239                                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
240                                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
241
242                                         MSG_INFO_NOTIFICATIONS
243                                         break;
244                                 }
245
246                                 case MSG_NOTIFY:
247                                 {
248                                         break;
249                                 }
250                         }
251                 }
252         }
253         else { backtrace("Incorrect usage of Send_Notification!\n"); }
254 }
255
256 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, string s3)
257 {
258         entity tmp_entity;
259         FOR_EACH_REALCLIENT(tmp_entity)
260         {
261                 if(tmp_entity.classname == STR_PLAYER)
262                 if(tmp_entity.team == targetteam)
263                 if(tmp_entity != except)
264                 {
265                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
266                 }
267         }
268 }
269
270 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, string s3)
271 {
272         entity tmp_entity;
273         FOR_EACH_REALCLIENT(tmp_entity)
274         {
275                 if((tmp_entity.classname == STR_PLAYER) || spectators)
276                 if(tmp_entity != except)
277                 {
278                         Send_Notification(net_type, tmp_entity, net_name, s1, s2, s3);
279                 }
280         }
281 }
282
283 // LEGACY NOTIFICATION SYSTEMS
284 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
285 {
286         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
287         {
288                 msg_entity = e;
289                 WRITESPECTATABLE_MSG_ONE({
290                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
291                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
292                         WriteByte(MSG_ONE, id);
293                         WriteString(MSG_ONE, s);
294                         if (id != 0 && s != "")
295                         {
296                                 WriteByte(MSG_ONE, duration);
297                                 WriteByte(MSG_ONE, countdown_num);
298                         }
299                 });
300         }
301 }
302 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
303 {
304         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
305 }
306 #endif