Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_superspec.qc
Go to the documentation of this file.
1#include "sv_superspec.qh"
2
5
6#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
7#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false)
8
9const int ASF_STRENGTH = BIT(0);
10const int ASF_SHIELD = BIT(1);
11const int ASF_MEGA_AR = BIT(2);
12const int ASF_MEGA_HP = BIT(3);
13const int ASF_FLAG_GRAB = BIT(4);
14const int ASF_OBSERVER_ONLY = BIT(5);
15const int ASF_SHOWWHAT = BIT(6);
16const int ASF_SSIM = BIT(7);
17const int ASF_FOLLOWKILLER = BIT(8);
18const int ASF_ALL = 0xFFFFFF;
20
21const int SSF_SILENT = BIT(0);
22const int SSF_VERBOSE = BIT(1);
23const int SSF_ITEMMSG = BIT(2);
25
26.string superspec_itemfilter; //"classname1 classname2 ..."
27
29{
30 Spectate(this, targ);
31 return true;
32}
33
35{
36 string fn = "superspec-local.options";
37 float fh;
38
39 if (!_ISLOCAL(this))
40 {
41 if(this.crypto_idfp == "")
42 return;
43
44 fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp));
45 }
46
47 fh = fopen(fn, FILE_WRITE);
48 if(fh < 0)
49 {
50 LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.");
51 }
52 else
53 {
54 fputs(fh, _SSMAGIX);
55 fputs(fh, "\n");
56 fputs(fh, itos(this.autospec_flags));
57 fputs(fh, "\n");
58 fputs(fh, itos(this.superspec_flags));
59 fputs(fh, "\n");
61 fputs(fh, "\n");
62 fclose(fh);
63 }
64}
65
66void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
67{
68 sprint(_to, strcat(_con_title, _msg));
69
70 if(_to.superspec_flags & SSF_SILENT)
71 return;
72
73 if(_spamlevel > 1)
74 if (!(_to.superspec_flags & SSF_VERBOSE))
75 return;
76
77 centerprint(_to, strcat(_center_title, _msg));
78}
79
81{
82 float i;
83
84 if(_for.superspec_itemfilter == "")
85 return true;
86
87 if(_for.superspec_itemfilter == "")
88 return true;
89
90 float l = tokenize_console(_for.superspec_itemfilter);
91 for(i = 0; i < l; ++i)
92 {
93 if(argv(i) == _item.classname)
94 return true;
95 }
96
97 return false;
98}
99
100MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
101{
102 entity item = M_ARGV(0, entity);
104
105 FOREACH_CLIENT(true, {
106 if(!IS_SPEC(it) && !IS_OBSERVER(it))
107 continue;
108 if(it.superspec_flags & SSF_ITEMMSG)
109 if(superspec_filteritem(it, item))
110 {
111 if(it.superspec_flags & SSF_VERBOSE)
112 superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1);
113 else
114 superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1);
115 if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher)
116 {
119 }
120 }
121
122 if(((it.autospec_flags & ASF_SHIELD) && item.invincible_finished) ||
123 ((it.autospec_flags & ASF_STRENGTH) && item.strength_finished) ||
124 ((it.autospec_flags & ASF_MEGA_AR) && item.itemdef == ITEM_ArmorMega) ||
125 ((it.autospec_flags & ASF_MEGA_HP) && item.itemdef == ITEM_HealthMega) ||
126 ((it.autospec_flags & ASF_FLAG_GRAB) && item.classname == "item_flag_team"))
127 {
128
129 if((it.enemy != toucher) || IS_OBSERVER(it))
130 {
131 if((it.autospec_flags & ASF_OBSERVER_ONLY) && !IS_OBSERVER(it))
132 {
133 if(it.superspec_flags & SSF_VERBOSE)
134 superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2);
135 }
136 else
137 {
138 if(it.autospec_flags & ASF_SHOWWHAT)
139 superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2);
140
142 }
143 }
144 }
145 });
146
148}
149
151{
152#define OPTIONINFO(flag,var,test,text,long,short) \
153 var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \
154 var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
155
156 if(MUTATOR_RETURNVALUE) // command was already handled?
157 return;
158
159 entity player = M_ARGV(0, entity);
160 string cmd_name = M_ARGV(1, string);
161 int cmd_argc = M_ARGV(2, int);
162
163 if(IS_PLAYER(player))
164 return;
165
166 if(cmd_name == "superspec_itemfilter")
167 {
168 if(argv(1) == "help")
169 {
170 string _aspeco;
171 _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n";
172 _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
173 _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
174 superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1);
175 }
176 else if(argv(1) == "clear")
177 {
178 if(player.superspec_itemfilter != "")
179 strunzone(player.superspec_itemfilter);
180
181 player.superspec_itemfilter = "";
182 }
183 else if(argv(1) == "show" || argv(1) == "")
184 {
185 if(player.superspec_itemfilter == "")
186 {
187 superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1);
188 return true;
189 }
190 float i;
191 float l = tokenize_console(player.superspec_itemfilter);
192 string _msg = "";
193 for(i = 0; i < l; ++i)
194 _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
195 //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
196
197 _msg = strcat(_msg,"\n");
198
199 superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1);
200 }
201 else
202 {
203 if(player.superspec_itemfilter != "")
204 strunzone(player.superspec_itemfilter);
205
206 player.superspec_itemfilter = strzone(argv(1));
207 }
208
209 return true;
210 }
211
212 if(cmd_name == "superspec")
213 {
214 string _aspeco;
215
216 if(cmd_argc > 1)
217 {
218 float i, _bits = 0, _start = 1;
219 if(argv(1) == "help")
220 {
221 _aspeco = "use cmd superspec [option] [on|off] to set options\n\n";
222 _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n");
223 _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
224 _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n");
225 _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
226 superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1);
227 return true;
228 }
229
230 if(argv(1) == "clear")
231 {
232 player.superspec_flags = 0;
233 _start = 2;
234 }
235
236 for(i = _start; i < cmd_argc; ++i)
237 {
238 if(argv(i) == "on" || argv(i) == "1")
239 {
240 player.superspec_flags |= _bits;
241 _bits = 0;
242 }
243 else if(argv(i) == "off" || argv(i) == "0")
244 {
245 if(_start == 1)
246 player.superspec_flags &= ~_bits;
247
248 _bits = 0;
249 }
250 else
251 {
252 if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
253 if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
254 if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
255 }
256 }
257 }
258
259 _aspeco = "";
260 OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
261 OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
262 OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
263
264 superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1);
265
266 return true;
267 }
268
270
271 if(cmd_name == "autospec")
272 {
273 string _aspeco;
274 if(cmd_argc > 1)
275 {
276 if(argv(1) == "help")
277 {
278 _aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
279 _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
280 _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
281 _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
282 _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
283 _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
284 _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n");
285 _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n");
286 _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n");
287 _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n");
288 _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n");
289 superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1);
290 return true;
291 }
292
293 float i, _bits = 0, _start = 1;
294 if(argv(1) == "clear")
295 {
296 player.autospec_flags = 0;
297 _start = 2;
298 }
299
300 for(i = _start; i < cmd_argc; ++i)
301 {
302 if(argv(i) == "on" || argv(i) == "1")
303 {
304 player.autospec_flags |= _bits;
305 _bits = 0;
306 }
307 else if(argv(i) == "off" || argv(i) == "0")
308 {
309 if(_start == 1)
310 player.autospec_flags &= ~_bits;
311
312 _bits = 0;
313 }
314 else
315 {
316 if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
317 if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
318 if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
319 if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
320 if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
321 if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
322 if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
323 if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
324 if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER;
325 if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
326 }
327 }
328 }
329
330 _aspeco = "";
331 OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
332 OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
333 OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
334 OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
335 OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
336 OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo");
337 OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
338 OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
339 OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk");
340
341 superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1);
342 return true;
343 }
344
345 if(cmd_name == "followpowerup")
346 {
347 // TODO: somehow cheaply loop through all held powerups
348 FOREACH_CLIENT(IS_PLAYER(it) && (StatusEffects_active(STATUSEFFECT_Strength, it) || StatusEffects_active(STATUSEFFECT_Shield, it)), { return superspec_Spectate(player, it); });
349
350 superspec_msg("", "", player, "No active powerup\n", 1);
351 return true;
352 }
353
354 if(cmd_name == "followstrength")
355 {
356 FOREACH_CLIENT(IS_PLAYER(it) && StatusEffects_active(STATUSEFFECT_Strength, it), { return superspec_Spectate(player, it); });
357
358 superspec_msg("", "", player, "No active Strength\n", 1);
359 return true;
360 }
361
362 if(cmd_name == "followshield")
363 {
364 FOREACH_CLIENT(IS_PLAYER(it) && StatusEffects_active(STATUSEFFECT_Shield, it), { return superspec_Spectate(player, it); });
365
366 superspec_msg("", "", player, "No active Shield\n", 1);
367 return true;
368 }
369#undef OPTIONINFO
370}
371
372MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString)
373{
374 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS");
375}
376
377MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString)
378{
379 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators");
380}
381
383{
384 if(this.enemy.crypto_idfp == "")
385 Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID);
386
387 delete(this);
388}
389
391{
392 entity player = M_ARGV(0, entity);
393
394 if(!IS_REAL_CLIENT(player))
395 return;
396
397 string fn = "superspec-local.options";
398 float fh;
399
400 player.superspec_flags = SSF_VERBOSE;
401 player.superspec_itemfilter = "";
402
403 entity _hello = new_pure(superspec_delayed_hello);
404 _hello.enemy = player;
405 setthink(_hello, superspec_hello);
406 _hello.nextthink = time + 5;
407
408 if (!_ISLOCAL(player))
409 {
410 if(player.crypto_idfp == "")
411 return;
412
413 fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp));
414 }
415
416 fh = fopen(fn, FILE_READ);
417 if(fh < 0)
418 {
419 LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.");
420 }
421 else
422 {
423 string _magic = fgets(fh);
424 if(_magic != _SSMAGIX)
425 {
426 LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic");
427 }
428 else
429 {
430 player.autospec_flags = stoi(fgets(fh));
431 player.superspec_flags = stoi(fgets(fh));
432 player.superspec_itemfilter = strzone(fgets(fh));
433 }
434 fclose(fh);
435 }
436}
437
438MUTATOR_HOOKFUNCTION(superspec, PlayerDies)
439{
440 entity frag_attacker = M_ARGV(1, entity);
442
444 if((it.autospec_flags & ASF_FOLLOWKILLER) && IS_PLAYER(frag_attacker) && it.enemy == frag_target)
445 {
446 if(it.autospec_flags & ASF_SHOWWHAT)
447 superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2);
448
449 superspec_Spectate(it, frag_attacker);
450 }
451 });
452}
453
455{
456 entity player = M_ARGV(0, entity);
457
459}
#define REGISTER_MUTATOR(...)
Definition base.qh:295
#define MUTATOR_HOOKFUNCTION(...)
Definition base.qh:335
#define MUTATOR_RETURNVALUE
Definition base.qh:328
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define M_ARGV(x, type)
Definition events.qh:17
#define IS_PLAYER(s)
Definition player.qh:243
const float FILE_READ
const float FILE_WRITE
float time
ERASEABLE bool expr_evaluate(string s)
Evaluate an expression of the form: [+ | -]?
Definition cvar.qh:48
string crypto_idfp
#define tokenize_console
#define stoi(s)
Definition int.qh:4
#define itos(i)
Definition int.qh:6
#define ClientConnect
Definition _all.inc:238
#define SV_ParseClientCommand
Definition _all.inc:284
#define ClientDisconnect
Definition _all.inc:242
#define LOG_TRACE(...)
Definition log.qh:76
string cmd_name
Definition events.qh:12
int cmd_argc
Definition events.qh:13
string fgets(float fhandle)
void fclose(float fhandle)
void fputs(float fhandle, string s)
void sprint(float clientnum, string text,...)
float fopen(string filename, float mode)
void centerprint(string text,...)
void strunzone(string s)
string ftos(float f)
string strzone(string s)
string argv(float n)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
#define setthink(e, f)
entity entity toucher
Definition self.qh:72
bool Spectate(entity this, entity pl)
Definition client.qc:1971
@ MUT_ITEMTOUCH_CONTINUE
Definition events.qh:734
bool StatusEffects_active(StatusEffect this, entity actor)
entity frag_target
Definition sv_ctf.qc:2321
entity enemy
Definition sv_ctf.qh:153
const int ASF_FOLLOWKILLER
#define _ISLOCAL(ent)
const int ASF_SHOWWHAT
const int ASF_STRENGTH
#define _SSMAGIX
float superspec_filteritem(entity _for, entity _item)
const int ASF_SSIM
string autocvar_g_superspectate
const int ASF_MEGA_HP
const int ASF_SHIELD
const int ASF_ALL
const int ASF_MEGA_AR
void superspec_save_client_conf(entity this)
void superspec_hello(entity this)
int autospec_flags
void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
const int ASF_OBSERVER_ONLY
int superspec_flags
const int SSF_VERBOSE
const int SSF_SILENT
#define OPTIONINFO(flag, var, test, text, long, short)
const int ASF_FLAG_GRAB
const int SSF_ITEMMSG
string superspec_itemfilter
bool superspec_Spectate(entity this, entity targ)
#define IS_OBSERVER(v)
Definition utils.qh:11
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50