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