]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Keep track of "Maxmimum notifications" limit
[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 // collapse multiple arguments into one argument
13 #define CLPS4(s1,s2,s3,s4) s1, s2, s3, s4
14 #define CLPS3(s1,s2,s3) s1, s2, s3
15 #define CLPS2(s1,s2) s1, s2
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,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     ARIRE unir frk jvgu lbhe bja zbgure (gvc sbe zvxrrhfn)
95 */
96 #define MSG_INFO_NOTIFICATIONS \
97         MSG_INFO_NOTIF(DEATH_MARBLES_LOST, CLPS3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
98         #undef MSG_INFO_NOTIF
99
100 #define MSG_NOTIFY_NOTIFICATIONS \
101         MSG_NOTIFY_NOTIF(DEATH_MARBLES_LOST2, CLPS3(s1, s2, s3), "notify_death", _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
102         #undef MSG_NOTIFY_NOTIF
103
104 #define MSG_CENTER_NOTIFICATIONS \
105         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED, "", CPID_CTF_CAPTURESHIELD, _("^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."), "") \
106         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE, "", CPID_CTF_CAPTURESHIELD, _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
107         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS, CLPS2(s1, s2, s3), CPID_CTF_PASS, _("^BG%s passed the ^F1%s^BG to %s"), "") \
108         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_SENT, CLPS2(s1, s2), CPID_CTF_PASS, _("^BGYou passed the ^F1%s^BG to %s"), "") \
109         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_PASS_RECEIVED, CLPS2(s1, s2), CPID_CTF_PASS, _("^BGYou received the ^F1%s^BG from %s"), "") \
110         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_RETURN, s1, CPID_CTF_LOWPRIO, _("^BGYou returned the ^F1%s"), "") \
111         MSG_CENTER_NOTIF(CENTER_CTF_EVENT_CAPTURE, s1, NO_CPID, _("^BGYou captured the ^F1%s"), "") \
112         #undef MSG_CENTER_NOTIF
113
114 #define MSG_WEAPON_NOTIFICATIONS \
115         MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, CLPS3(s1, s2, s3), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
116         #undef MSG_WEAPON_NOTIF
117
118 // NOW we actually activate the declarations
119 MSG_INFO_NOTIFICATIONS
120 MSG_NOTIFY_NOTIFICATIONS
121 MSG_CENTER_NOTIFICATIONS
122 MSG_WEAPON_NOTIFICATIONS
123
124
125 // ======================
126 //  Supporting Functions
127 // ======================
128
129 #ifdef SVQC
130 #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
131 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
132 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
133 #endif
134
135 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
136
137 string normal_or_gentle(string normal, string gentle)
138 {
139         #ifdef CSQC
140         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
141         #else
142         if(autocvar_sv_gentle)
143         #endif
144                 return ((gentle != "") ? gentle : normal);
145         else
146                 return normal;
147 }
148
149 string CCR(string input) // color code replace, place inside of sprintf and parse the string
150 {
151         input = strreplace("^F1", "^3", input);
152         input = strreplace("^F2", "^2", input);
153         input = strreplace("^BG", "^7", input);
154
155         input = strreplace("^N", "^7", input); // "none"-- reset to white
156
157         return input;
158 }
159
160
161 // ===============================
162 //  Frontend Notification Pushing
163 // ===============================
164
165
166 // =========================
167 //  Notification Networking
168 // =========================
169
170 #ifdef CSQC
171 void Read_Notification()
172 {
173         float net_type = ReadByte();
174         float net_name = ReadCoord(); // byte only has 256 selections, we need more than that
175         string s1 = ReadString();
176         string s2 = ReadString();
177         string s3 = ReadString();
178
179         switch(net_type)
180         {
181                 case MSG_INFO:
182                 {
183                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
184                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
185
186                         MSG_INFO_NOTIFICATIONS
187                         break;
188                 }
189
190                 case MSG_NOTIFY:
191                 {
192                         break;
193                 }
194
195                 case MSG_CENTER:
196                 {
197                         #define MSG_CENTER_NOTIF(name,args,cpid,normal,gentle) \
198                                 { if(min(NOTIF_MAX, name) == net_name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), 0, 0); } }
199
200                         MSG_CENTER_NOTIFICATIONS
201                         break;
202                 }
203
204                 case MSG_WEAPON:
205                 {
206                         break;
207                 }
208         }
209 }
210 #endif
211 #ifdef SVQC
212 void Send_Notification(float net_type, entity client, float net_name, string s1, string s2, string s3)
213 {
214         if(net_type && net_name)
215         {
216                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
217                 {
218                         msg_entity = client;
219                         WRITESPECTATABLE_MSG_ONE({
220                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
221                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
222                                 WriteByte(MSG_ONE, net_type);
223                                 WriteCoord(MSG_ONE, net_name);
224                                 WriteString(MSG_ONE, s1);
225                                 WriteString(MSG_ONE, s2);
226                                 WriteString(MSG_ONE, s3);
227                         });
228                 }
229
230                 if(!server_is_local && ((net_type == MSG_INFO || net_type == MSG_NOTIFY) || client == world))
231                 {
232                         switch(net_type)
233                         {
234                                 case MSG_INFO:
235                                 {
236                                         #define MSG_INFO_NOTIF(name,args,normal,gentle) \
237                                                 { if(min(NOTIF_MAX, name) == net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
238
239                                         MSG_INFO_NOTIFICATIONS
240                                         break;
241                                 }
242
243                                 case MSG_NOTIFY:
244                                 {
245                                         break;
246                                 }
247                         }
248                 }
249         }
250         else { backtrace("Incorrect usage of Send_Notification!\n"); }
251 }
252
253 // LEGACY NOTIFICATION SYSTEMS
254 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
255 {
256         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
257         {
258                 msg_entity = e;
259                 WRITESPECTATABLE_MSG_ONE({
260                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
261                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
262                         WriteByte(MSG_ONE, id);
263                         WriteString(MSG_ONE, s);
264                         if (id != 0 && s != "")
265                         {
266                                 WriteByte(MSG_ONE, duration);
267                                 WriteByte(MSG_ONE, countdown_num);
268                         }
269                 });
270         }
271 }
272 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
273 {
274         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
275 }
276 #endif