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