097cf592a5653e351761c3cadbcb94866d78d0b6
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / gamecommand.qc
1 // =====================================================
2 //  Server side game commands code, reworked by Samual
3 //  Last updated: July 22nd, 2011
4 // =====================================================
5
6 #define GC_REQUEST_HELP 1
7 #define GC_REQUEST_COMMAND 2
8 #define GC_REQUEST_USAGE 3
9
10 entity radarmapper;
11
12 float RADAR_WIDTH_MAX = 512;
13 float RADAR_HEIGHT_MAX = 512;
14 float sharpen_buffer[RADAR_WIDTH_MAX * 3];
15
16 string GotoMap(string m);
17 string doublehex = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFFFF";
18 // FF is contained twice, to map 256 to FF too
19 // removes the need to bound()
20
21 void race_deleteTime(string map, float pos);
22
23
24 // ============================
25 //  Misc. Supporting Functions
26 // ============================
27
28 // used by GameCommand_radarmap()
29 float FullTraceFraction(vector a, vector mi, vector ma, vector b)
30 {
31         vector c;
32         float white, black;
33
34         white = 0.001;
35         black = 0.001;
36
37         c = a;
38
39         float n, m;
40         n = m = 0;
41
42         while(vlen(c - b) > 1)
43         {
44                 ++m;
45
46                 tracebox(c, mi, ma, b, MOVE_WORLDONLY, world);
47                 ++n;
48
49                 if(!trace_startsolid)
50                 {
51                         black += vlen(trace_endpos - c);
52                         c = trace_endpos;
53                 }
54
55                 n += tracebox_inverted(c, mi, ma, b, MOVE_WORLDONLY, world);
56
57                 white += vlen(trace_endpos - c);
58                 c = trace_endpos;
59         }
60
61         if(n > 200)
62                 dprint("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations\n");
63
64         return white / (black + white);
65 }
66 float RadarMapAtPoint_Trace(float x, float y, float w, float h, float zmin, float zsize, float q)
67 {
68         vector a, b, mi, ma;
69
70         mi = '0 0 0';
71         ma = '1 0 0' * w + '0 1 0' * h;
72         a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
73         b = '1 0 0' * x + '0 1 0' * y + '0 0 1' * (zsize + zmin);
74
75         return FullTraceFraction(a, mi, ma, b);
76 }
77 float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, float zsize, float q)
78 {
79         vector o, mi, ma;
80         float i, r;
81         vector dz;
82
83         q = 256 * q - 1;
84         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
85
86         mi = '0 0 0';
87         dz = (zsize / q) * '0 0 1';
88         ma = '1 0 0' * w + '0 1 0' * h + dz;
89         o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
90
91         if(x < world.absmin_x - w)
92                 return 0;
93         if(y < world.absmin_y - h)
94                 return 0;
95         if(x > world.absmax_x)
96                 return 0;
97         if(y > world.absmax_y)
98                 return 0;
99
100         r = 0;
101         for(i = 0; i < q; ++i)
102         {
103                 vector v1, v2;
104                 v1 = v2 = o + dz * i + mi;
105                 v1_x += random() * (ma_x - mi_x);
106                 v1_y += random() * (ma_y - mi_y);
107                 v1_z += random() * (ma_z - mi_z);
108                 v2_x += random() * (ma_x - mi_x);
109                 v2_y += random() * (ma_y - mi_y);
110                 v2_z += random() * (ma_z - mi_z);
111                 traceline(v1, v2, MOVE_WORLDONLY, world);
112                 if(trace_startsolid || trace_fraction < 1)
113                         ++r;
114         }
115         return r / q;
116 }
117 float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, float zsize, float q)
118 {
119         vector o, mi, ma;
120         float i, r;
121         vector dz;
122
123         q = 256 * q - 1;
124         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
125
126         mi = '0 0 0';
127         dz = (zsize / q) * '0 0 1';
128         ma = '1 0 0' * w + '0 1 0' * h + dz;
129         o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
130
131         if(x < world.absmin_x - w)
132                 return 0;
133         if(y < world.absmin_y - h)
134                 return 0;
135         if(x > world.absmax_x)
136                 return 0;
137         if(y > world.absmax_y)
138                 return 0;
139
140         r = 0;
141         for(i = 0; i < q; ++i)
142         {
143                 tracebox(o + dz * i, mi, ma, o + dz * i, MOVE_WORLDONLY, world);
144                 if(trace_startsolid)
145                         ++r;
146         }
147         return r / q;
148 }
149 float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, float zsize, float q)
150 {
151         vector a, b, mi, ma;
152
153         q *= 4; // choose q so it matches the regular algorithm in speed
154
155         q = 256 * q - 1;
156         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
157
158         mi = '0 0 0';
159         ma = '1 0 0' * w + '0 1 0' * h;
160         a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin;
161         b = '1 0 0' * w + '0 1 0' * h + '0 0 1' * zsize;
162
163         float c, i;
164         c = 0;
165
166         for(i = 0; i < q; ++i)
167         {
168                 vector v;
169                 v_x = a_x + random() * b_x;
170                 v_y = a_y + random() * b_y;
171                 v_z = a_z + random() * b_z;
172                 traceline(v, v, MOVE_WORLDONLY, world);
173                 if(trace_startsolid)
174                         ++c;
175         }
176
177         return c / q;
178 }
179 void sharpen_set(float x, float v)
180 {
181         sharpen_buffer[x + 2 * RADAR_WIDTH_MAX] = v;
182 }
183 float sharpen_getpixel(float x, float y)
184 {
185         if(x < 0)
186                 return 0;
187         if(x >= RADAR_WIDTH_MAX)
188                 return 0;
189         if(y < 0)
190                 return 0;
191         if(y > 2)
192                 return 0;
193         return sharpen_buffer[x + y * RADAR_WIDTH_MAX];
194 }
195 float sharpen_get(float x, float a)
196 {
197         float sum;
198         sum = sharpen_getpixel(x, 1);
199         if(a == 0)
200                 return sum;
201         sum *= (8 + 1/a);
202         sum -= sharpen_getpixel(x - 1, 0);
203         sum -= sharpen_getpixel(x - 1, 1);
204         sum -= sharpen_getpixel(x - 1, 2);
205         sum -= sharpen_getpixel(x + 1, 0);
206         sum -= sharpen_getpixel(x + 1, 1);
207         sum -= sharpen_getpixel(x + 1, 2);
208         sum -= sharpen_getpixel(x, 0);
209         sum -= sharpen_getpixel(x, 2);
210         return bound(0, sum * a, 1);
211 }
212 void sharpen_shift(float w)
213 {
214         float i;
215         for(i = 0; i < w; ++i)
216         {
217                 sharpen_buffer[i] = sharpen_buffer[i + RADAR_WIDTH_MAX];
218                 sharpen_buffer[i + RADAR_WIDTH_MAX] = sharpen_buffer[i + 2 * RADAR_WIDTH_MAX];
219                 sharpen_buffer[i + 2 * RADAR_WIDTH_MAX] = 0;
220         }
221 }
222 void sharpen_init(float w)
223 {
224         float i;
225         for(i = 0; i < w; ++i)
226         {
227                 sharpen_buffer[i] = 0;
228                 sharpen_buffer[i + RADAR_WIDTH_MAX] = 0;
229                 sharpen_buffer[i + 2 * RADAR_WIDTH_MAX] = 0;
230         }
231 }
232 void RadarMap_Next()
233 {
234         if(radarmapper.count & 4)
235         {
236                 localcmd("quit\n");
237         }
238         else if(radarmapper.count & 2)
239         {
240                 localcmd(strcat("defer 1 \"sv_cmd radarmap --flags ", ftos(radarmapper.count), strcat(" --res ", ftos(radarmapper.size_x), " ", ftos(radarmapper.size_y), " --sharpen ", ftos(radarmapper.ltime), " --qual ", ftos(radarmapper.size_z)), "\"\n"));
241                 GotoNextMap();
242         }
243         remove(radarmapper);
244         radarmapper = world;
245 }
246 void RadarMap_Think()
247 {
248         // rough map entity
249         //   cnt: current line
250         //   size: pixel width/height
251         //   maxs: cell width/height
252         //   frame: counter
253         
254         float i, x, l;
255         string si;
256
257         if(self.frame == 0)
258         {
259                 // initialize
260                 get_mi_min_max_texcoords(1);
261                 self.mins = mi_picmin;
262                 self.maxs_x = (mi_picmax_x - mi_picmin_x) / self.size_x;
263                 self.maxs_y = (mi_picmax_y - mi_picmin_y) / self.size_y;
264                 self.maxs_z = mi_max_z - mi_min_z;
265                 print("Picture mins/maxs: ", ftos(self.maxs_x), " and ", ftos(self.maxs_y), " should match\n");
266                 self.netname = strzone(strcat("gfx/", mi_shortname, "_radar.xpm"));
267                 if(!(self.count & 1))
268                 {
269                         self.cnt = fopen(self.netname, FILE_READ);
270                         if(self.cnt < 0)
271                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.tga"), FILE_READ);
272                         if(self.cnt < 0)
273                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.png"), FILE_READ);
274                         if(self.cnt < 0)
275                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.jpg"), FILE_READ);
276                         if(self.cnt < 0)
277                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.tga"), FILE_READ);
278                         if(self.cnt < 0)
279                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.png"), FILE_READ);
280                         if(self.cnt < 0)
281                                 self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.jpg"), FILE_READ);
282                         if(self.cnt >= 0)
283                         {
284                                 fclose(self.cnt);
285
286                                 print(self.netname, " already exists, aborting (you may want to specify --force)\n");
287                                 RadarMap_Next();
288                                 return;
289                         }
290                 }
291                 self.cnt = fopen(self.netname, FILE_WRITE);
292                 if(self.cnt < 0)
293                 {
294                         print("Error writing ", self.netname, "\n");
295                         remove(self);
296                         radarmapper = world;
297                         return;
298                 }
299                 print("Writing to ", self.netname, "...\n");
300                 fputs(self.cnt, "/* XPM */\n");
301                 fputs(self.cnt, "static char *RadarMap[] = {\n");
302                 fputs(self.cnt, "/* columns rows colors chars-per-pixel */\n");
303                 fputs(self.cnt, strcat("\"", ftos(self.size_x), " ", ftos(self.size_y), " 256 2\",\n"));
304                 for(i = 0; i < 256; ++i)
305                 {
306                         si = substring(doublehex, i*2, 2);
307                         fputs(self.cnt, strcat("\"", si, " c #", si, si, si, "\",\n"));
308                 }
309                 self.frame += 1;
310                 self.nextthink = time;
311                 sharpen_init(self.size_x);
312         }
313         else if(self.frame <= self.size_y)
314         {
315                 // fill the sharpen buffer with this line
316                 sharpen_shift(self.size_x);
317                 i = self.count & 24;
318
319                 switch(i)
320                 {
321                         case 0:
322                         default:
323                                 for(x = 0; x < self.size_x; ++x)
324                                 {
325                                         l = RadarMapAtPoint_Block(self.mins_x + x * self.maxs_x, self.mins_y + (self.size_y - self.frame) * self.maxs_y, self.maxs_x, self.maxs_y, self.mins_z, self.maxs_z, self.size_z);
326                                         sharpen_set(x, l);
327                                 }
328                                 break;
329                         case 8:
330                                 for(x = 0; x < self.size_x; ++x)
331                                 {
332                                         l = RadarMapAtPoint_Trace(self.mins_x + x * self.maxs_x, self.mins_y + (self.size_y - self.frame) * self.maxs_y, self.maxs_x, self.maxs_y, self.mins_z, self.maxs_z, self.size_z);
333                                         sharpen_set(x, l);
334                                 }
335                                 break;
336                         case 16:
337                                 for(x = 0; x < self.size_x; ++x)
338                                 {
339                                         l = RadarMapAtPoint_Sample(self.mins_x + x * self.maxs_x, self.mins_y + (self.size_y - self.frame) * self.maxs_y, self.maxs_x, self.maxs_y, self.mins_z, self.maxs_z, self.size_z);
340                                         sharpen_set(x, l);
341                                 }
342                                 break;
343                         case 24:
344                                 for(x = 0; x < self.size_x; ++x)
345                                 {
346                                         l = RadarMapAtPoint_LineBlock(self.mins_x + x * self.maxs_x, self.mins_y + (self.size_y - self.frame) * self.maxs_y, self.maxs_x, self.maxs_y, self.mins_z, self.maxs_z, self.size_z);
347                                         sharpen_set(x, l);
348                                 }
349                                 break;
350                 }
351
352                 // do we have enough lines?
353                 if(self.frame >= 2)
354                 {
355                         // write a pixel line
356                         fputs(self.cnt, "\"");
357                         for(x = 0; x < self.size_x; ++x)
358                         {
359                                 l = sharpen_get(x, self.ltime);
360                                 fputs(self.cnt, substring(doublehex, 2 * floor(l * 256.0), 2));
361                         }
362                         if(self.frame == self.size_y)
363                                 fputs(self.cnt, "\"\n");
364                         else
365                         {
366                                 fputs(self.cnt, "\",\n");
367                                 print(ftos(self.size_y - self.frame), " lines left\n");
368                         }
369                 }
370
371                 // is this the last line? then write back the missing line
372                 if(self.frame == self.size_y)
373                 {
374                         sharpen_shift(self.size_x);
375                         // write a pixel line
376                         fputs(self.cnt, "\"");
377                         for(x = 0; x < self.size_x; ++x)
378                         {
379                                 l = sharpen_get(x, self.ltime);
380                                 fputs(self.cnt, substring(doublehex, 2 * floor(l * 256.0), 2));
381                         }
382                         if(self.frame == self.size_y)
383                                 fputs(self.cnt, "\"\n");
384                         else
385                         {
386                                 fputs(self.cnt, "\",\n");
387                                 print(ftos(self.size_y - self.frame), " lines left\n");
388                         }
389                 }
390
391                 self.frame += 1;
392                 self.nextthink = time;
393         }
394         else
395         {
396                 // close the file
397                 fputs(self.cnt, "};\n");
398                 fclose(self.cnt);
399                 print("Finished. Please edit data/", self.netname, " with an image editing application and place it in the TGA format in the gfx folder.\n");
400                 RadarMap_Next();
401         }
402 }
403
404 //  used by GameCommand_make_mapinfo()
405 void make_mapinfo_Think()
406 {
407         if(MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
408         {
409                 print("Done rebuiling mapinfos.\n");
410                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
411                 remove(self);
412         }
413         else
414         {
415                 self.think = make_mapinfo_Think;
416                 self.nextthink = time;
417         }
418 }
419
420 //  used by GameCommand_extendmatchtime() and GameCommand_reducematchtime()
421 void changematchtime(float delta, float mi, float ma)
422 {
423         float cur;
424         float new;
425         float lim;
426
427         if(delta == 0)
428                 return;
429         if(autocvar_timelimit < 0)
430                 return;
431
432         if(mi <= 10)
433                 mi = 10; // at least ten sec in the future
434         cur = time - game_starttime;
435         if(cur > 0)
436                 mi += cur; // from current time!
437
438         lim = autocvar_timelimit * 60;
439
440         if(delta > 0)
441         {
442                 if(lim == 0)
443                         return; // cannot increase any further
444                 else if(lim < ma)
445                         new = min(ma, lim + delta);
446                 else // already above maximum: FAIL
447                         return;
448         }
449         else
450         {
451                 if(lim == 0) // infinite: try reducing to max, if we are allowed to
452                         new = max(mi, ma);
453                 else if(lim > mi) // above minimum: decrease
454                         new = max(mi, lim + delta);
455                 else // already below minimum: FAIL
456                         return;
457         }
458
459         cvar_set("timelimit", ftos(new / 60));
460 }
461
462 //  used by GameCommand_modelbug() // TODO: is this even needed?
463 float g_clientmodel_genericsendentity (entity to, float sf);
464 void modelbug_make_svqc();
465 void modelbug_make_csqc()
466 {
467         Net_LinkEntity(self, TRUE, 0, g_clientmodel_genericsendentity);
468         self.think = modelbug_make_svqc;
469         self.nextthink = time + 1;
470         setorigin(self, self.origin - '0 0 8');
471 }
472 void modelbug_make_svqc()
473 {
474         self.SendEntity = func_null;
475         self.think = modelbug_make_csqc;
476         self.nextthink = time + 1;
477         setorigin(self, self.origin + '0 0 8');
478 }
479 void modelbug()
480 {
481         entity e;
482         e = spawn();
483         setorigin(e, nextent(world).origin);
484         precache_model("models_portal.md3");
485         setmodel(e, "models/portal.md3");
486         e.think = modelbug_make_svqc;
487         e.nextthink = time + 1;
488 }
489
490
491 // =======================
492 //  Command Sub-Functions
493 // =======================
494
495 void GameCommand_adminmsg(float request, float argc)
496 {
497         entity client;
498         float entno = stof(argv(1)); 
499         float n, i;
500         string s;
501         
502         switch(request)
503         {
504                 case GC_REQUEST_HELP:
505                         print("  ^2adminmsg^7: Send an admin message to a client directly\n");
506                         return;
507                         
508                 case GC_REQUEST_COMMAND:
509                         if(argc >= 3 && argc <= 4) {
510                                 if((entno < 0) | (entno > maxclients)) {
511                                         print("Player ", argv(1), " doesn't exist\n");
512                                         return;
513                                 }
514                                 n = 0;
515                                 for(i = (entno ? entno : 1); i <= (entno ? entno : maxclients); ++i)
516                                 {
517                                         client = edict_num(i);
518                                         if(client.flags & FL_CLIENT)
519                                         {
520                                                 if(argc == 4)
521                                                 {
522                                                         // make the string console safe
523                                                         s = argv(2);
524                                                         s = strreplace("\n", "", s);
525                                                         s = strreplace("\\", "\\\\", s);
526                                                         s = strreplace("$", "$$", s);
527                                                         s = strreplace("\"", "\\\"", s);
528                                                         stuffcmd(client, sprintf("\ninfobar %f \"%s\"\n", stof(argv(3)), s));
529                                                 }
530                                                 else
531                                                 {
532                                                         centerprint_atprio(client, CENTERPRIO_ADMIN, strcat("^3", admin_name(), ":\n\n^7", argv(2)));
533                                                         sprint(client, strcat("\{1}\{13}^3", admin_name(), "^7: ", argv(2), "\n"));
534                                                 }
535                                                 dprint("Message sent to ", client.netname, "\n");
536                                                 ++n;
537                                         }
538                                 }
539                                 if(!n) { print(strcat("Client (", argv(1) ,") not found.\n")); } 
540                                 return;
541                         } 
542                         
543                 default:
544                         print("Incorrect parameters for ^2adminmsg^7\n");
545                 case GC_REQUEST_USAGE:
546                         print("\nUsage:^3 sv_cmd adminmsg clientnumber \"message\" [infobartime]\n");
547                         print("  If infobartime is provided, the message will be sent to infobar.\n");
548                         print("  Otherwise, it will just be sent as a centerprint message.\n");
549                         print("Examples: adminmsg 4 \"this infomessage will last for ten seconds\" 10\n");
550                         print("          adminmsg 2 \"this message will be a centerprint\"\n");
551                         return;
552         }
553 }
554
555 void GameCommand_allready(float request)
556 {
557         switch(request)
558         {
559                 case GC_REQUEST_HELP:
560                         print("  ^2allready^7: Restart the server and reset the players\n");
561                         return;
562                         
563                 case GC_REQUEST_COMMAND:
564                         ReadyRestart();
565                         return;
566                         
567                 default:
568                 case GC_REQUEST_USAGE:
569                         print("\nUsage:^3 sv_cmd allready\n");
570                         print("  No arguments required.\n");
571                         return;
572         }
573 }
574
575 void GameCommand_allspec(float request, float argc)
576 {
577         entity client;
578         float i;
579         
580         switch(request)
581         {
582                 case GC_REQUEST_HELP:
583                         print("  ^2allspec^7: Force all players to spectate\n");
584                         return;
585                         
586                 case GC_REQUEST_COMMAND:
587                         FOR_EACH_PLAYER(client)
588                         {
589                                 self = client;
590                                 PutObserverInServer();
591                                 ++i;
592                         }
593                         if(i) { bprint(strcat("Successfully forced all (", ftos(i), ") players to spectate", (argv(1) ? strcat(" for reason: '", argv(1), "'") : ""), ".\n")); }
594                         else { print("No players found to spectate.\n"); }
595                         return;
596                         
597                 default:
598                 case GC_REQUEST_USAGE:
599                         print("\nUsage:^3 sv_cmd allspec [reason]\n");
600                         print("  Where 'reason' is an optional argument for explanation of allspec command.\n");
601                         print("See also: ^2moveplayer^7\n");
602                         return;
603         }
604 }
605
606 void GameCommand_anticheat(float request, float argc) // FIXME: player entity is never found
607 {
608         entity client;
609         float entno = stof(argv(1)); 
610         
611         switch(request)
612         {
613                 case GC_REQUEST_HELP:
614                         print("  ^2anticheat^7: Create an anticheat report for a client\n");
615                         return;
616                         
617                 case GC_REQUEST_COMMAND:
618                         if((entno < 1) | (entno > maxclients)) {
619                                 print("Player ", argv(1), " doesn't exist\n");
620                                 return;
621                         }
622                         client = edict_num(entno);
623                         if(clienttype(client) != CLIENTTYPE_REAL && clienttype(client) != CLIENTTYPE_BOT) {
624                                 print("Player ", client.netname, " is not active\n");
625                                 return;
626                         }
627                         self = client;
628                         anticheat_report();
629                         return;
630                         
631                 default:
632                         print("Incorrect parameters for ^2anticheat^7\n");
633                 case GC_REQUEST_USAGE:
634                         print("\nUsage:^3 sv_cmd anticheat clientnumber\n");
635                         print("  where 'clientnumber' is player entity number.\n");
636                         return;
637         }
638 }
639
640 void GameCommand_bbox(float request)
641 {
642         switch(request)
643         {
644                 case GC_REQUEST_HELP:
645                         print("  ^2bbox^7: Print detailed information about world size\n");
646                         return;
647                         
648                 case GC_REQUEST_COMMAND:
649                         print("Original size: ", ftos(world.absmin_x), " ", ftos(world.absmin_y), " ", ftos(world.absmin_z));
650                         print(" ", ftos(world.absmax_x), " ", ftos(world.absmax_y), " ", ftos(world.absmax_z), "\n");
651                         print("Currently set size: ", ftos(world.mins_x), " ", ftos(world.mins_y), " ", ftos(world.mins_z));
652                         print(" ", ftos(world.maxs_x), " ", ftos(world.maxs_y), " ", ftos(world.maxs_z), "\n");
653                         print("Solid bounding box size:");
654
655                         tracebox('1 0 0' * world.absmin_x,
656                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
657                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
658                                                         '1 0 0' * world.absmax_x,
659                                         MOVE_WORLDONLY,
660                                         world);
661                         if(trace_startsolid)
662                                 print(" ", ftos(world.absmin_x));
663                         else
664                                 print(" ", ftos(trace_endpos_x));
665
666                         tracebox('0 1 0' * world.absmin_y,
667                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
668                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
669                                                         '0 1 0' * world.absmax_y,
670                                         MOVE_WORLDONLY,
671                                         world);
672                         if(trace_startsolid)
673                                 print(" ", ftos(world.absmin_y));
674                         else
675                                 print(" ", ftos(trace_endpos_y));
676
677                         tracebox('0 0 1' * world.absmin_z,
678                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
679                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
680                                                         '0 0 1' * world.absmax_z,
681                                         MOVE_WORLDONLY,
682                                         world);
683                         if(trace_startsolid)
684                                 print(" ", ftos(world.absmin_z));
685                         else
686                                 print(" ", ftos(trace_endpos_z));
687
688                         tracebox('1 0 0' * world.absmax_x,
689                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
690                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
691                                                         '1 0 0' * world.absmin_x,
692                                         MOVE_WORLDONLY,
693                                         world);
694                         if(trace_startsolid)
695                                 print(" ", ftos(world.absmax_x));
696                         else
697                                 print(" ", ftos(trace_endpos_x));
698
699                         tracebox('0 1 0' * world.absmax_y,
700                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
701                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
702                                                         '0 1 0' * world.absmin_y,
703                                         MOVE_WORLDONLY,
704                                         world);
705                         if(trace_startsolid)
706                                 print(" ", ftos(world.absmax_y));
707                         else
708                                 print(" ", ftos(trace_endpos_y));
709
710                         tracebox('0 0 1' * world.absmax_z,
711                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
712                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
713                                                         '0 0 1' * world.absmin_z,
714                                         MOVE_WORLDONLY,
715                                         world);
716                         if(trace_startsolid)
717                                 print(" ", ftos(world.absmax_z));
718                         else
719                                 print(" ", ftos(trace_endpos_z));
720                                 
721                         print("\n");
722                         return;
723                         
724                 default:
725                 case GC_REQUEST_USAGE:
726                         print("\nUsage:^3 sv_cmd bbox\n");
727                         print("  No arguments required.\n");
728                         print("See also: ^2gettaginfo^7\n");
729                         return;
730         }
731 }
732
733 void GameCommand_bot_cmd(float request, float argc) // what a mess... old old code.
734 {
735         entity bot;
736         
737         switch(request)
738         {
739                 case GC_REQUEST_HELP:
740                         print("  ^2bot_cmd^7: Control and send commands to bots\n");
741                         return;
742                         
743                 case GC_REQUEST_COMMAND:
744                         if(argv(1) == "reset")
745                         {
746                                 bot_resetqueues();
747                                 return;
748                         }
749                         else if(argv(1) == "load" && argc == 3)
750                         {
751                                 float fh, i;
752                                 string s;
753                                 fh = fopen(argv(2), FILE_READ);
754                                 if(fh < 0)
755                                 {
756                                         print("cannot open the file\n");
757                                         return;
758                                 }
759
760                                 i = 0;
761                                 while((s = fgets(fh)))
762                                 {
763                                         argc = tokenize_console(s);
764
765                                         if(argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd")
766                                         {
767                                                 // let's start at token 2 so we can skip sv_cmd bot_cmd
768                                                 bot = find_bot_by_number(stof(argv(2)));
769                                                 if(bot == world)
770                                                         bot = find_bot_by_name(argv(2));
771                                                 if(bot)
772                                                         bot_queuecommand(bot, strcat(argv(3), " ", argv(4)));
773                                         }
774                                         else
775                                                 localcmd(strcat(s, "\n"));
776
777                                         ++i;
778                                 }
779                                 print(ftos(i), " commands read\n");
780                                 fclose(fh);
781                                 return;
782                         }
783                         else if(argv(1) == "help")
784                         {
785                                 if(argv(2))
786                                         bot_cmdhelp(argv(2));
787                                 else
788                                         bot_list_commands();
789                                 return;
790                         }
791                         else if(argc >= 3) // this comes last
792                         {
793                                 bot = find_bot_by_number(stof(argv(1)));
794                                 if(bot == world)
795                                         bot = find_bot_by_name(argv(1));
796                                 if(bot)
797                                 {
798                                         print(strcat("Command '", (argv(2), " ", argv(3)), "' sent to bot ", bot.netname, "\n"));
799                                         bot_queuecommand(bot, strcat(argv(2), " ", argv(3)));
800                                         return;
801                                 }
802                                 else
803                                         print(strcat("Error: Can't find bot with the name or id '", argv(1),"' - Did you mistype the command?\n")); // don't return so that usage is shown
804                         }
805                         
806                 default:
807                         print("Incorrect parameters for ^2bot_cmd^7\n");
808                 case GC_REQUEST_USAGE:
809                         print("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n");
810                         print("  'client' can be either the name or entity id of the bot\n");
811                         print("  For full list of commands, see bot_cmd help [command].\n");
812                         print("Examples: bot_cmd <id> cc \"say something\"\n");
813                         print("          bot_cmd <id> presskey jump\n");
814                         return;
815         }
816 }
817
818 void GameCommand_cointoss(float request, float argc)
819 {
820         entity client;
821         string result1 = (argv(2) ? strcat("^7", argv(1), "^3!\n") : "^1HEADS^3!\n");
822         string result2 = (argv(2) ? strcat("^7", argv(2), "^3!\n") : "^4TAILS^3!\n");
823         string choice = ((random() > 0.5) ? result1 : result2);
824         
825         switch(request)
826         {
827                 case GC_REQUEST_HELP:
828                         print("  ^2cointoss^7: Flip a virtual coin and give random result\n");
829                         return;
830                         
831                 case GC_REQUEST_COMMAND:
832                         FOR_EACH_CLIENT(client)
833                                 centerprint(client, strcat("^3Throwing coin... Result: ", choice));
834                         bprint(strcat("^3Throwing coin... Result: ", choice));
835                         return;
836                         
837                 default:
838                 case GC_REQUEST_USAGE:
839                         print("\nUsage:^3 sv_cmd cointoss [result1 result2]\n");
840                         print("  Where 'result1' and 'result2' are user created options.\n");
841                         return;
842         }
843 }
844
845 void GameCommand_cvar_changes(float request)
846 {
847         switch(request)
848         {
849                 case GC_REQUEST_HELP:
850                         print("  ^2cvar_changes^7: Prints a list of all changed server cvars\n");
851                         return;
852                         
853                 case GC_REQUEST_COMMAND:
854                         print(cvar_changes);
855                         return;
856                         
857                 default:
858                 case GC_REQUEST_USAGE:
859                         print("\nUsage:^3 sv_cmd cvar_changes\n");
860                         print("  No arguments required.\n");
861                         print("See also: ^2cvar_purechanges^7\n");
862                         return;
863         }
864 }
865
866 void GameCommand_cvar_purechanges(float request)
867 {
868         switch(request)
869         {
870                 case GC_REQUEST_HELP:
871                         print("  ^2cvar_purechanges^7: Prints a list of all changed gameplay cvars\n");
872                         return;
873                         
874                 case GC_REQUEST_COMMAND:
875                         print(cvar_purechanges);
876                         return;
877                         
878                 default:
879                 case GC_REQUEST_USAGE:
880                         print("\nUsage:^3 sv_cmd cvar_purechanges\n");
881                         print("  No arguments required.\n");
882                         print("See also: ^2cvar_changes^7\n");
883                         return;
884         }
885 }
886
887 void GameCommand_database(float request, float argc)
888 {
889         switch(request)
890         {
891                 case GC_REQUEST_HELP:
892                         print("  ^2database^7: Extra controls of the serverprogs database\n");
893                         return;
894                         
895                 case GC_REQUEST_COMMAND:
896                         if(argc == 3)
897                         {
898                                 if(argv(1) == "save")
899                                 {
900                                         db_save(ServerProgsDB, argv(2));
901                                         print(strcat("Copied serverprogs database to '", argv(2), "' in the data directory.\n"));
902                                         return;
903                                 }
904                                 else if(argv(1) == "dump")
905                                 {
906                                         db_dump(ServerProgsDB, argv(2));
907                                         print("DB dumped.\n"); // wtf does this do?
908                                         return;
909                                 }
910                                 else if(argv(1) == "load")
911                                 {
912                                         db_close(ServerProgsDB);
913                                         ServerProgsDB = db_load(argv(2));
914                                         print(strcat("Loaded '", argv(2), "' as new serverprogs database.\n"));
915                                         return;
916                                 }
917                         }
918                         
919                 default:
920                         print("Incorrect parameters for ^2database^7\n");
921                 case GC_REQUEST_USAGE:
922                         print("\nUsage:^3 sv_cmd database action filename\n");
923                         print("  Where 'action' is the command to complete,\n");
924                         print("  and 'filename' is what it acts upon.\n");
925                         print("  Full list of commands here: \"save, dump, load.\"\n");
926                         return;
927         }
928 }
929
930 void GameCommand_defer_clear(float request, float argc)
931 {
932         entity client;
933         float entno = stof(argv(1));
934         
935         switch(request)
936         {
937                 case GC_REQUEST_HELP:
938                         print("  ^2defer_clear^7: Clear all queued defer commands for client\n");
939                         return;
940                         
941                 case GC_REQUEST_COMMAND:
942                         if(argc == 2)
943                         {
944                                 // player_id is out of range
945                                 if((entno < 1) | (entno > maxclients)) {
946                                         print("Player ", argv(1), " doesn't exist\n");
947                                         return;
948                                 }
949                                 client = edict_num(entno);
950                                 if not(client.flags & FL_CLIENT) {
951                                         print("Player ", argv(1), " doesn't exist\n");
952                                         return;
953                                 }
954                                 if(clienttype(client) == CLIENTTYPE_BOT) {
955                                         print("Player ", argv(1), " (", client.netname, ") is a bot\n");
956                                         return;
957                                 }
958                                 stuffcmd(client, "defer clear\n");
959                                 print("defer clear stuffed to ", argv(1), " (", client.netname, ")\n");
960                                 return;
961                         }
962                 
963                 default:
964                         print("Incorrect parameters for ^2defer_clear^7\n");
965                 case GC_REQUEST_USAGE:
966                         print("\nUsage:^3 sv_cmd defer_clear clientnumber\n");
967                         print("  where 'clientnumber' is player entity number.\n");
968                         print("See also: ^2defer_clear_all^7\n");
969                         return;
970         }
971 }
972
973 void GameCommand_defer_clear_all(float request)
974 {
975         entity client;
976         float i;
977         float argc;
978         
979         switch(request)
980         {
981                 case GC_REQUEST_HELP:
982                         print("  ^2defer_clear_all^7: Clear all queued defer commands for all clients\n");
983                         return;
984                         
985                 case GC_REQUEST_COMMAND:
986                         FOR_EACH_CLIENT(client)
987                         {
988                                 argc = tokenize_console(strcat("defer_clear ", ftos(num_for_edict(client))));
989                                 GameCommand_defer_clear(GC_REQUEST_COMMAND, argc);      
990                                 ++i;
991                         }
992                         if(i) { bprint(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found? 
993                         return;
994                 
995                 default:
996                 case GC_REQUEST_USAGE:
997                         print("\nUsage:^3 sv_cmd defer_clear_all\n");
998                         print("  No arguments required.\n");
999                         print("See also: ^2defer_clear^7\n");
1000                         return;
1001         }
1002 }
1003
1004 void GameCommand_delrec(float request, float argc) // UNTESTED // perhaps merge later with records and printstats and such?
1005 {
1006         switch(request)
1007         {
1008                 case GC_REQUEST_HELP:
1009                         print("  ^2delrec^7: Delete race time record for a map\n");
1010                         return;
1011                         
1012                 case GC_REQUEST_COMMAND:
1013                         if(argv(1))
1014                         {
1015                                 if(argv(2))
1016                                         race_deleteTime(argv(2), stof(argv(1)));
1017                                 else
1018                                         race_deleteTime(GetMapname(), stof(argv(1)));
1019                                 return;
1020                         }
1021                         
1022                 default:
1023                         print("Incorrect parameters for ^2delrec^7\n");
1024                 case GC_REQUEST_USAGE:
1025                         print("\nUsage:^3 sv_cmd delrec ranking [map]\n");
1026                         print("  'ranking' is which ranking level to clear up to, \n");
1027                         print("  it will clear all records up to nth place.\n");
1028                         print("  if 'map' is not provided it will use current map.\n");
1029                         return;
1030         }
1031 }
1032
1033 void GameCommand_effectindexdump(float request)
1034 {
1035         float fh, d;
1036         string s;
1037         
1038         switch(request)
1039         {
1040                 case GC_REQUEST_HELP:
1041                         print("  ^2effectindexdump^7: Dump list of effects from code and effectinfo.txt\n");
1042                         return;
1043                         
1044                 case GC_REQUEST_COMMAND:
1045                         d = db_create();
1046                         print("begin of effects list\n");
1047                         db_put(d, "TE_GUNSHOT", "1"); print("effect TE_GUNSHOT is ", ftos(particleeffectnum("TE_GUNSHOT")), "\n");
1048                         db_put(d, "TE_GUNSHOTQUAD", "1"); print("effect TE_GUNSHOTQUAD is ", ftos(particleeffectnum("TE_GUNSHOTQUAD")), "\n");
1049                         db_put(d, "TE_SPIKE", "1"); print("effect TE_SPIKE is ", ftos(particleeffectnum("TE_SPIKE")), "\n");
1050                         db_put(d, "TE_SPIKEQUAD", "1"); print("effect TE_SPIKEQUAD is ", ftos(particleeffectnum("TE_SPIKEQUAD")), "\n");
1051                         db_put(d, "TE_SUPERSPIKE", "1"); print("effect TE_SUPERSPIKE is ", ftos(particleeffectnum("TE_SUPERSPIKE")), "\n");
1052                         db_put(d, "TE_SUPERSPIKEQUAD", "1"); print("effect TE_SUPERSPIKEQUAD is ", ftos(particleeffectnum("TE_SUPERSPIKEQUAD")), "\n");
1053                         db_put(d, "TE_WIZSPIKE", "1"); print("effect TE_WIZSPIKE is ", ftos(particleeffectnum("TE_WIZSPIKE")), "\n");
1054                         db_put(d, "TE_KNIGHTSPIKE", "1"); print("effect TE_KNIGHTSPIKE is ", ftos(particleeffectnum("TE_KNIGHTSPIKE")), "\n");
1055                         db_put(d, "TE_EXPLOSION", "1"); print("effect TE_EXPLOSION is ", ftos(particleeffectnum("TE_EXPLOSION")), "\n");
1056                         db_put(d, "TE_EXPLOSIONQUAD", "1"); print("effect TE_EXPLOSIONQUAD is ", ftos(particleeffectnum("TE_EXPLOSIONQUAD")), "\n");
1057                         db_put(d, "TE_TAREXPLOSION", "1"); print("effect TE_TAREXPLOSION is ", ftos(particleeffectnum("TE_TAREXPLOSION")), "\n");
1058                         db_put(d, "TE_TELEPORT", "1"); print("effect TE_TELEPORT is ", ftos(particleeffectnum("TE_TELEPORT")), "\n");
1059                         db_put(d, "TE_LAVASPLASH", "1"); print("effect TE_LAVASPLASH is ", ftos(particleeffectnum("TE_LAVASPLASH")), "\n");
1060                         db_put(d, "TE_SMALLFLASH", "1"); print("effect TE_SMALLFLASH is ", ftos(particleeffectnum("TE_SMALLFLASH")), "\n");
1061                         db_put(d, "TE_FLAMEJET", "1"); print("effect TE_FLAMEJET is ", ftos(particleeffectnum("TE_FLAMEJET")), "\n");
1062                         db_put(d, "EF_FLAME", "1"); print("effect EF_FLAME is ", ftos(particleeffectnum("EF_FLAME")), "\n");
1063                         db_put(d, "TE_BLOOD", "1"); print("effect TE_BLOOD is ", ftos(particleeffectnum("TE_BLOOD")), "\n");
1064                         db_put(d, "TE_SPARK", "1"); print("effect TE_SPARK is ", ftos(particleeffectnum("TE_SPARK")), "\n");
1065                         db_put(d, "TE_PLASMABURN", "1"); print("effect TE_PLASMABURN is ", ftos(particleeffectnum("TE_PLASMABURN")), "\n");
1066                         db_put(d, "TE_TEI_G3", "1"); print("effect TE_TEI_G3 is ", ftos(particleeffectnum("TE_TEI_G3")), "\n");
1067                         db_put(d, "TE_TEI_SMOKE", "1"); print("effect TE_TEI_SMOKE is ", ftos(particleeffectnum("TE_TEI_SMOKE")), "\n");
1068                         db_put(d, "TE_TEI_BIGEXPLOSION", "1"); print("effect TE_TEI_BIGEXPLOSION is ", ftos(particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n");
1069                         db_put(d, "TE_TEI_PLASMAHIT", "1"); print("effect TE_TEI_PLASMAHIT is ", ftos(particleeffectnum("TE_TEI_PLASMAHIT")), "\n");
1070                         db_put(d, "EF_STARDUST", "1"); print("effect EF_STARDUST is ", ftos(particleeffectnum("EF_STARDUST")), "\n");
1071                         db_put(d, "TR_ROCKET", "1"); print("effect TR_ROCKET is ", ftos(particleeffectnum("TR_ROCKET")), "\n");
1072                         db_put(d, "TR_GRENADE", "1"); print("effect TR_GRENADE is ", ftos(particleeffectnum("TR_GRENADE")), "\n");
1073                         db_put(d, "TR_BLOOD", "1"); print("effect TR_BLOOD is ", ftos(particleeffectnum("TR_BLOOD")), "\n");
1074                         db_put(d, "TR_WIZSPIKE", "1"); print("effect TR_WIZSPIKE is ", ftos(particleeffectnum("TR_WIZSPIKE")), "\n");
1075                         db_put(d, "TR_SLIGHTBLOOD", "1"); print("effect TR_SLIGHTBLOOD is ", ftos(particleeffectnum("TR_SLIGHTBLOOD")), "\n");
1076                         db_put(d, "TR_KNIGHTSPIKE", "1"); print("effect TR_KNIGHTSPIKE is ", ftos(particleeffectnum("TR_KNIGHTSPIKE")), "\n");
1077                         db_put(d, "TR_VORESPIKE", "1"); print("effect TR_VORESPIKE is ", ftos(particleeffectnum("TR_VORESPIKE")), "\n");
1078                         db_put(d, "TR_NEHAHRASMOKE", "1"); print("effect TR_NEHAHRASMOKE is ", ftos(particleeffectnum("TR_NEHAHRASMOKE")), "\n");
1079                         db_put(d, "TR_NEXUIZPLASMA", "1"); print("effect TR_NEXUIZPLASMA is ", ftos(particleeffectnum("TR_NEXUIZPLASMA")), "\n");
1080                         db_put(d, "TR_GLOWTRAIL", "1"); print("effect TR_GLOWTRAIL is ", ftos(particleeffectnum("TR_GLOWTRAIL")), "\n");
1081                         db_put(d, "TR_SEEKER", "1"); print("effect TR_SEEKER is ", ftos(particleeffectnum("TR_SEEKER")), "\n");
1082                         db_put(d, "SVC_PARTICLE", "1"); print("effect SVC_PARTICLE is ", ftos(particleeffectnum("SVC_PARTICLE")), "\n");
1083
1084                         fh = fopen("effectinfo.txt", FILE_READ);
1085                         while((s = fgets(fh)))
1086                         {
1087                                 tokenize(s); // tokenize_console would hit the loop counter :(
1088                                 if(argv(0) == "effect")
1089                                 {
1090                                         if(db_get(d, argv(1)) != "1")
1091                                         {
1092                                                 if(particleeffectnum(argv(1)) >= 0)
1093                                                         print("effect ", argv(1), " is ", ftos(particleeffectnum(argv(1))), "\n");
1094                                                 db_put(d, argv(1), "1");
1095                                         }
1096                                 }
1097                         }
1098                         print("end of effects list\n");
1099
1100                         db_close(d);
1101                         return;
1102                         
1103                 default:
1104                 case GC_REQUEST_USAGE:
1105                         print("\nUsage:^3 sv_cmd effectindexdump\n");
1106                         print("  No arguments required.\n");
1107                         return;
1108         }
1109 }
1110
1111 void GameCommand_extendmatchtime(float request)
1112 {
1113         switch(request)
1114         {
1115                 case GC_REQUEST_HELP:
1116                         print("  ^2extendmatchtime^7: Increase the timelimit value incrementally\n");
1117                         return;
1118                         
1119                 case GC_REQUEST_COMMAND:
1120                         changematchtime(autocvar_timelimit_increment* 60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
1121                         return;
1122                         
1123                 default:
1124                 case GC_REQUEST_USAGE:
1125                         print("\nUsage:^3 sv_cmd extendmatchtime\n");
1126                         print("  No arguments required.\n");
1127                         print("See also: ^2reducematchtime^7\n");
1128                         return;
1129         }
1130 }
1131
1132 void GameCommand_find(float request, float argc)
1133 {
1134         entity client;
1135         
1136         switch(request)
1137         {
1138                 case GC_REQUEST_HELP:
1139                         print("  ^2find^7: Search through entities for matching classname\n");
1140                         return;
1141                         
1142                 case GC_REQUEST_COMMAND:
1143                         for(client = world; (client = find(client, classname, argv(1))); )
1144                                 print(etos(client), "\n");
1145                         return;
1146                         
1147                 default:
1148                         print("Incorrect parameters for ^2find^7\n");
1149                 case GC_REQUEST_USAGE:
1150                         print("\nUsage:^3 sv_cmd find classname\n");
1151                         print("  Where 'classname' is the classname to search for.\n");
1152                         return;
1153         }
1154 }
1155
1156 void GameCommand_gametype(float request, float argc)
1157 {
1158         string s = argv(1);
1159         float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
1160         
1161         switch(request)
1162         {
1163                 case GC_REQUEST_HELP:
1164                         print("  ^2gametype^7: Simple command to change the active gametype\n");
1165                         return;
1166                         
1167                 case GC_REQUEST_COMMAND:
1168                         if(t)
1169                         {
1170                                 MapInfo_SwitchGameType(t);
1171                                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
1172                                 if(MapInfo_count > 0)
1173                                         bprint("Game type successfully switched to ", s, "\n");
1174                                 else
1175                                 {
1176                                         bprint("Cannot use this game type: no map for it found\n");
1177                                         MapInfo_SwitchGameType(tsave);
1178                                         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
1179                                 }
1180                         }
1181                         else
1182                                 bprint("Game type switch to ", s, " failed: this type does not exist!\n");
1183                         return;
1184                         
1185                 default:
1186                         print("Incorrect parameters for ^2gametype^7\n");
1187                 case GC_REQUEST_USAGE:
1188                         print("\nUsage:^3 sv_cmd gametype mode\n");
1189                         print("  Where 'mode' is the gametype mode to switch to.\n");
1190                         print("See also: ^2gotomap^7\n");
1191                         return;
1192         }
1193 }
1194
1195 void GameCommand_gettaginfo(float request, float argc) // UNTESTED // todo: finish usage description for it (but, must first learn this shit)
1196 {
1197         entity tmp_entity;
1198         float i;
1199         vector v;
1200         
1201         switch(request)
1202         {
1203                 case GC_REQUEST_HELP:
1204                         print("  ^2gettaginfo^7: Get specific information about a weapon model\n");
1205                         return;
1206                         
1207                 case GC_REQUEST_COMMAND:
1208                         if(argc >= 4)
1209                         {
1210                                 tmp_entity = spawn();
1211                                 if(argv(1) == "w")
1212                                         setmodel(tmp_entity, (nextent(world)).weaponentity.model);
1213                                 else
1214                                 {
1215                                         precache_model(argv(1));
1216                                         setmodel(tmp_entity, argv(1));
1217                                 }
1218                                 tmp_entity.frame = stof(argv(2));
1219                                 if(substring(argv(3), 0, 1) == "#")
1220                                         i = stof(substring(argv(3), 1, -1));
1221                                 else
1222                                         i = gettagindex(tmp_entity, argv(3));
1223                                 if(i)
1224                                 {
1225                                         v = gettaginfo(tmp_entity, i);
1226                                         print("model ", tmp_entity.model, " frame ", ftos(tmp_entity.frame), " tag ", gettaginfo_name);
1227                                         print(" index ", ftos(i), " parent ", ftos(gettaginfo_parent), "\n");
1228                                         print(" vector = ", ftos(v_x), " ", ftos(v_y), " ", ftos(v_z), "\n");
1229                                         print(" offset = ", ftos(gettaginfo_offset_x), " ", ftos(gettaginfo_offset_y), " ", ftos(gettaginfo_offset_z), "\n");
1230                                         print(" forward = ", ftos(gettaginfo_forward_x), " ", ftos(gettaginfo_forward_y), " ", ftos(gettaginfo_forward_z), "\n");
1231                                         print(" right = ", ftos(gettaginfo_right_x), " ", ftos(gettaginfo_right_y), " ", ftos(gettaginfo_right_z), "\n");
1232                                         print(" up = ", ftos(gettaginfo_up_x), " ", ftos(gettaginfo_up_y), " ", ftos(gettaginfo_up_z), "\n");
1233                                         if(argc >= 6)
1234                                         {
1235                                                 v_y = -v_y;
1236                                                 localcmd(strcat(argv(4), vtos(v), argv(5), "\n"));
1237                                         }
1238                                 }
1239                                 else
1240                                         print("bone not found\n");
1241                                         
1242                                 remove(tmp_entity);
1243                                 return;
1244                         }
1245                         
1246                 default:
1247                         print("Incorrect parameters for ^2gettaginfo^7\n");
1248                 case GC_REQUEST_USAGE:
1249                         print("\nUsage:^3 sv_cmd gettaginfo\n");
1250                         print("  FIXME: Arguments currently unknown\n");
1251                         print("See also: ^2bbox^7\n");
1252                         return;
1253         }
1254 }
1255
1256 void GameCommand_gotomap(float request, float argc)
1257 {
1258         switch(request)
1259         {
1260                 case GC_REQUEST_HELP:
1261                         print("  ^2gotomap^7: Simple command to switch to another map\n");
1262                         return;
1263                         
1264                 case GC_REQUEST_COMMAND:
1265                         if(argc == 2)
1266                         {
1267                                 print(GotoMap(argv(1)), "\n");
1268                                 return;
1269                         }
1270                         
1271                 default:
1272                         print("Incorrect parameters for ^2gotomap^7\n");
1273                 case GC_REQUEST_USAGE:
1274                         print("\nUsage:^3 sv_cmd gotomap map\n");
1275                         print("  Where 'map' is the *.bsp file to change to.\n");
1276                         print("See also: ^2gametype^7\n");
1277                         return;
1278         }
1279 }
1280
1281 void GameCommand_ladder(float request)
1282 {
1283         switch(request)
1284         {
1285                 case GC_REQUEST_HELP:
1286                         print("  ^2ladder^7: Get information about top players if supported\n");
1287                         return;
1288                         
1289                 case GC_REQUEST_COMMAND:
1290                         print(ladder_reply);
1291                         return;
1292                         
1293                 default:
1294                 case GC_REQUEST_USAGE:
1295                         print("\nUsage:^3 sv_cmd ladder\n");
1296                         print("  No arguments required.\n");
1297                         return;
1298         }
1299 }
1300
1301 void GameCommand_lockteams(float request)
1302 {
1303         switch(request)
1304         {
1305                 case GC_REQUEST_HELP:
1306                         print("  ^2lockteams^7: Disable the ability for players to switch or enter teams\n");
1307                         return;
1308                         
1309                 case GC_REQUEST_COMMAND:
1310                         if(teamplay)
1311                         {
1312                                 lockteams = 1;
1313                                 bprint("^1The teams are now locked.\n");
1314                         }
1315                         else
1316                                 bprint("That command can only be used in a team-based gamemode.\n");
1317                         return;
1318                         
1319                 default:
1320                 case GC_REQUEST_USAGE:
1321                         print("\nUsage:^3 sv_cmd lockteams\n");
1322                         print("  No arguments required.\n");
1323                         print("See also: ^2unlockteams^7\n");
1324                         return;
1325         }
1326 }
1327
1328 void GameCommand_make_mapinfo(float request) // UNTESTED
1329 {
1330         entity tmp_entity;
1331         
1332         switch(request)
1333         {
1334                 case GC_REQUEST_HELP:
1335                         print("  ^2make_mapinfo^7: Automatically rebuild mapinfo files\n");
1336                         return;
1337                         
1338                 case GC_REQUEST_COMMAND: 
1339                         tmp_entity = spawn();
1340                         tmp_entity.classname = "make_mapinfo";
1341                         tmp_entity.think = make_mapinfo_Think;
1342                         tmp_entity.nextthink = time; // this sucks... todo: re-write this -- Use initializeentity later
1343                         MapInfo_Enumerate();
1344                         return;
1345                         
1346                 default:
1347                 case GC_REQUEST_USAGE:
1348                         print("\nUsage:^3 sv_cmd make_mapinfo\n");
1349                         print("  No arguments required.\n");
1350                         return;
1351         }
1352 }
1353
1354 void GameCommand_modelbug(float request) // UNTESTED // is this even needed anymore? 
1355 {
1356         switch(request)
1357         {
1358                 case GC_REQUEST_HELP:
1359                         print("  ^2modelbug^7: foobar\n");
1360                         return;
1361                         
1362                 case GC_REQUEST_COMMAND:
1363                         modelbug();
1364                         return;
1365                         
1366                 default:
1367                 case GC_REQUEST_USAGE:
1368                         print("\nUsage:^3 sv_cmd modelbug\n");
1369                         print("  No arguments required.\n");
1370                         return;
1371         }
1372 }
1373
1374 void GameCommand_moveplayer(float request, float argc)
1375 {
1376         entity client;
1377         string targets = argv(1);
1378         string destination = argv(2);
1379         string notify = argv(3);
1380         float i;
1381         argc = tokenizebyseparator(targets, ","); // re-use argc for the targets
1382         
1383         switch(request)
1384         {
1385                 case GC_REQUEST_HELP:
1386                         print("  ^2moveplayer^7: Change the team/status of a player\n");
1387                         return;
1388                         
1389                 case GC_REQUEST_COMMAND:
1390                         // lets see if the target(s) even actually exist.
1391                         if((targets) && (destination))
1392                         { 
1393                                 for(i = 0; i < argc; ++i)
1394                                 {
1395                                         // Check to see if the player is a valid target
1396                                         if((stof(argv(i)) < 1) | (stof(argv(i)) > maxclients)) // player_id is out of range
1397                                         {
1398                                                 print(strcat("Player ", argv(i), " doesn't exist", (((i + 1) < argc) ? ", skipping to next player.\n" : ".\n")));
1399                                                 continue; 
1400                                         }
1401                                         client = edict_num(stof(argv(i)));
1402                                         if not(client.flags & FL_CLIENT) // player entity is not a client
1403                                         {
1404                                                 print(strcat("Player ", argv(i), " doesn't exist", (((i + 1) < argc) ? ", skipping to next player.\n" : ".\n")));
1405                                                 continue;
1406                                         }
1407                                         
1408                                         // Where are we putting this player?
1409                                         if(destination == "spec" || destination == "spectator") 
1410                                         {
1411                                                 if(client.classname != "spectator" && client.classname != "observer")
1412                                                 {
1413                                                         self = client;
1414                                                         PutObserverInServer();
1415                                                 }
1416                                                 else
1417                                                 {
1418                                                         print("Player ", argv(i), " (", client.netname, ") is already spectating.\n");
1419                                                 }
1420                                                 return;
1421                                         }
1422                                         else
1423                                         {
1424                                                 if(client.classname != "spectator" && client.classname != "observer")
1425                                                 {
1426                                                         if(teamplay)
1427                                                         {
1428                                                                 // set up
1429                                                                 float team_color;
1430                                                                 float save = client.team_forced;
1431                                                                 client.team_forced = 0;
1432
1433                                                                 // find the team to move the player to
1434                                                                 team_color = ColourToNumber(destination);
1435                                                                 if(team_color == client.team) // already on the destination team
1436                                                                 {
1437                                                                         // keep the forcing undone
1438                                                                         print("Player ", argv(i), " (", client.netname, ") is already on the ", ColoredTeamName(client.team), ".\n");
1439                                                                         return;
1440                                                                 } 
1441                                                                 else if(team_color == 0)  // auto team
1442                                                                 {
1443                                                                         team_color = NumberToTeamNumber(FindSmallestTeam(client, FALSE));
1444                                                                 }
1445                                                                 else
1446                                                                 {
1447                                                                         CheckAllowedTeams(client);
1448                                                                 }
1449                                                                 client.team_forced = save;
1450                                                                 
1451                                                                 // Check to see if the destination team is even available
1452                                                                 switch(team_color) 
1453                                                                 {
1454                                                                         case COLOR_TEAM1:
1455                                                                                 if(c1 == -1) {
1456                                                                                         print("Sorry, can't move player to red team if it doesn't exist.\n");
1457                                                                                         return;
1458                                                                                 }
1459                                                                                 break;
1460
1461                                                                         case COLOR_TEAM2:
1462                                                                                 if(c2 == -1) {
1463                                                                                         print("Sorry, can't move player to blue team if it doesn't exist.\n");
1464                                                                                         return;
1465                                                                                 }
1466                                                                                 break;
1467
1468                                                                         case COLOR_TEAM3:
1469                                                                                 if(c3 == -1) {
1470                                                                                         print("Sorry, can't move player to yellow team if it doesn't exist.\n");
1471                                                                                         return;
1472                                                                                 }
1473                                                                                 break;
1474
1475                                                                         case COLOR_TEAM4:
1476                                                                                 if(c4 == -1) {
1477                                                                                         print("Sorry, can't move player to pink team if it doesn't exist.\n");
1478                                                                                         return;
1479                                                                                 }
1480                                                                                 break;
1481
1482                                                                         default:
1483                                                                                 print("Sorry, can't move player here if team ", destination, " doesn't exist.\n");
1484                                                                                 return;
1485                                                                 }
1486                                                                 
1487                                                                 // If so, lets continue and finally move the player
1488                                                                 client.team_forced = 0;
1489                                                                 MoveToTeam(client, team_color, 6, stof(notify));
1490                                                                 print("Player ", argv(i), " (", client.netname, ") has been moved to the ", ColoredTeamName(team_color), ".\n");
1491                                                                 return;
1492                                                         }
1493                                                         else
1494                                                         {
1495                                                                 print("Can't change teams when currently not playing a team game.\n");
1496                                                                 return;
1497                                                         }
1498                                                 }
1499                                                 else
1500                                                 {
1501                                                         print("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P 
1502                                                         return;
1503                                                 }
1504                                         }
1505                                 }
1506                                 print("No acceptable players given, aborting.\n");
1507                                 return; // still correct parameters so return to avoid usage print
1508                         }
1509                         
1510                 default:
1511                         print("Incorrect parameters for ^2moveplayer^7\n");
1512                 case GC_REQUEST_USAGE:
1513                         print("\nUsage:^3 sv_cmd moveplayer clientnumbers destination [notify]\n");
1514                         print("  'clientnumbers' is a list (separated by commas) of player entity ID's\n");
1515                         print("  'destination' is what to send the player to, be it team or spectating\n");
1516                         print("  Full list of destinations here: \"spec, spectator, red, blue, yellow, pink, auto.\"\n");
1517                         print("  'notify' is whether or not to send messages notifying of the move. Detail below.\n");
1518                         print("    0 (00) automove centerprint, admin message; 1 (01) automove centerprint, no admin message\n");
1519                         print("    2 (10) no centerprint, admin message; 3 (11) no centerprint, no admin message\n");
1520                         print("Examples: moveplayer 1,3,5 red 3\n");
1521                         print("          moveplayer 2 spec \n");
1522                         print("See also: ^2allspec^7\n");
1523                         return;
1524         }
1525 }
1526
1527 void GameCommand_nospectators(float request)
1528 {
1529         switch(request)
1530         {
1531                 case GC_REQUEST_HELP:
1532                         print("  ^2nospectators^7: Automatically remove spectators from a match\n");
1533                         return;
1534                         
1535                 case GC_REQUEST_COMMAND:
1536                         blockSpectators = 1;
1537                         entity plr;
1538                         FOR_EACH_CLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
1539                         {
1540                                 if(plr.classname == "spectator" || plr.classname == "observer")
1541                                 {
1542                                         plr.spectatortime = time;
1543                                         sprint(plr, strcat("^7You have to become a player within the next ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds, otherwise you will be kicked, because spectators aren't allowed at this time!\n"));
1544                                 }
1545                         }
1546                         bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n"));
1547                         return;
1548                         
1549                 default:
1550                 case GC_REQUEST_USAGE:
1551                         print("\nUsage:^3 sv_cmd nospectators\n");
1552                         print("  No arguments required.\n");
1553                         return;
1554         }
1555 }
1556
1557 void GameCommand_onslaught_updatelinks(float request) // UNTESTED // should this be here? Perhaps some mutatorhook call instead....
1558 {
1559         switch(request)
1560         {
1561                 case GC_REQUEST_HELP:
1562                         print("  ^2onslaught_updatelinks^7: Refresh link status for onslaught\n");
1563                         return;
1564                         
1565                 case GC_REQUEST_COMMAND:
1566                         onslaught_updatelinks();
1567                         print("ONS links updated\n");
1568                         return;
1569                         
1570                 default:
1571                 case GC_REQUEST_USAGE:
1572                         print("\nUsage:^3 sv_cmd onslaught_updatelinks\n");
1573                         print("  No arguments required.\n");
1574                         return;
1575         }
1576 }
1577
1578 void GameCommand_playerdemo(float request, float argc) // UNTESTED // TODO: change the if statements for sub arguments to switches
1579 {
1580         entity client;
1581         float i, n, entno;
1582         string s;
1583         
1584         switch(request)
1585         {
1586                 case GC_REQUEST_HELP:
1587                         print("  ^2playerdemo^7: Control the ability to save demos of players\n");
1588                         return;
1589                         
1590                 case GC_REQUEST_COMMAND:
1591                         if(argv(1) == "read")
1592                         {
1593                                 // TODO: Create a general command for looking this up, save a lot of space everywhere in this file
1594                                 entno = stof(argv(2));
1595                                 if((entno < 1) | (entno > maxclients)) {
1596                                         print("Player ", argv(2), " doesn't exist\n");
1597                                         return;
1598                                 }
1599                                 client = edict_num(entno);
1600                                 if(clienttype(client) != CLIENTTYPE_BOT) {
1601                                         print("Player ", client.netname, " is not a bot\n");
1602                                         return;
1603                                 }
1604                                 self = client;
1605                                 playerdemo_open_read(argv(3));
1606                                 return;
1607                         }
1608                         else if(argv(1) == "write")
1609                         {
1610                                 entno = stof(argv(2));
1611                                 if((entno < 1) | (entno > maxclients)) {
1612                                         print("Player ", argv(2), " doesn't exist\n");
1613                                         return;
1614                                 }
1615                                 client = edict_num(entno);
1616                                 self = client;
1617                                 playerdemo_open_write(argv(3));
1618                                 return;
1619                         }
1620                         else if(argv(1) == "auto_read_and_write")
1621                         {
1622                                 s = argv(2);
1623                                 n = stof(argv(3));
1624                                 cvar_set("bot_number", ftos(n));
1625                                 localcmd("wait; wait; wait\n");
1626                                 for(i = 0; i < n; ++i)
1627                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1628                                 localcmd("sv_cmd playerdemo write 1 ", ftos(n+1), "\n");
1629                                 return;
1630                         }
1631                         else if(argv(1) == "auto_read")
1632                         {
1633                                 s = argv(2);
1634                                 n = stof(argv(3));
1635                                 cvar_set("bot_number", ftos(n));
1636                                 localcmd("wait; wait; wait\n");
1637                                 for(i = 0; i < n; ++i)
1638                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1639                                 return;
1640                         }
1641                         return;
1642                         
1643                 default:
1644                         print("Incorrect parameters for ^2radarmap^7\n");
1645                 case GC_REQUEST_USAGE:
1646                         print("\nUsage:^3 sv_cmd \n");
1647                         print("  FIXME: Arguments currently unknown\n");
1648                         return;
1649         }
1650 }
1651
1652 void GameCommand_printstats(float request)
1653 {
1654         switch(request)
1655         {
1656                 case GC_REQUEST_HELP:
1657                         print("  ^2printstats^7: foobar\n");
1658                         return;
1659                         
1660                 case GC_REQUEST_COMMAND:
1661                         DumpStats(FALSE);
1662                         print("stats dumped.\n");
1663                         return;
1664                         
1665                 default:
1666                 case GC_REQUEST_USAGE:
1667                         print("\nUsage:^3 sv_cmd printstats\n");
1668                         print("  No arguments required.\n");
1669                         return;
1670         }
1671 }
1672
1673 void GameCommand_radarmap(float request, float argc)
1674 {
1675         float i;
1676         
1677         switch(request)
1678         {
1679                 case GC_REQUEST_HELP:
1680                         print("  ^2radarmap^7: Generate a radar image of the map\n");
1681                         return;
1682                         
1683                 case GC_REQUEST_COMMAND:
1684                         if(!radarmapper)
1685                         {
1686                                 radarmapper = spawn();
1687                                 radarmapper.classname = "radarmapper";
1688                                 radarmapper.think = RadarMap_Think;
1689                                 radarmapper.nextthink = time;
1690                                 radarmapper.count = 8; // default to the --trace method, as it is faster now
1691                                 radarmapper.ltime = 1;
1692                                 radarmapper.size = '512 512 1';
1693
1694                                 for(i = 1; i < argc; ++i)
1695                                 {
1696                                         switch(argv(i))
1697                                         {
1698                                                 case "--force": { radarmapper.count |= 1; break; }
1699                                                 case "--loop": { radarmapper.count |= 2; break; }
1700                                                 case "--quit": { radarmapper.count |= 4; break; }
1701                                                 case "--block": { radarmapper.count &~= 24; break; }
1702                                                 case "--trace": { radarmapper.count &~= 24; radarmapper.count |= 8; break; }
1703                                                 case "--sample": { radarmapper.count &~= 24; radarmapper.count |= 16; break; }
1704                                                 case "--lineblock": { radarmapper.count |= 24; break; }
1705                                                 case "--flags": { ++i; radarmapper.count = stof(argv(i)); break; } // for the recursive call
1706                                                 case "--sharpen": { ++i; radarmapper.ltime = stof(argv(i)); break; } // for the recursive call
1707                                                 case "--res": // minor alias
1708                                                 case "--resolution": { ++i; radarmapper.size_x = stof(argv(i)); ++i; radarmapper.size_y = stof(argv(i)); break; }
1709                                                 case "--qual": // minor alias
1710                                                 case "--quality": { ++i; radarmapper.size_z = stof(argv(i)); break; }
1711                                                 
1712                                                 default: 
1713                                                         i = argc; 
1714                                                         remove(radarmapper);
1715                                                         radarmapper = world;
1716                                                         break;
1717                                         }
1718                                 }
1719                                 
1720                                 if(radarmapper) // after doing the arguments, see if we successfully went forward. 
1721                                 {
1722                                         print("Radarmap entity spawned.\n");
1723                                         return; // if so, don't print usage.
1724                                 }
1725                         }
1726                         
1727                 default:
1728                         print("Incorrect parameters for ^2radarmap^7\n");
1729                 case GC_REQUEST_USAGE:
1730                         print("\nUsage:^3 sv_cmd radarmap [--force] [--loop] [--quit] [--block | --trace | --sample | --lineblock] [--sharpen N] [--res W H] [--qual Q]\n");
1731                         print("  The quality factor Q is roughly proportional to the time taken.\n");
1732                         print("  trace supports no quality factor; its result should look like --block with infinite quality factor.\n");
1733                         return;
1734         }
1735 }
1736
1737 void GameCommand_rankings(float request) // this is OLD.... jeez.
1738 {
1739         switch(request)
1740         {
1741                 case GC_REQUEST_HELP:
1742                         print("  ^2rankings^7: Print information about rankings\n");
1743                         return;
1744                         
1745                 case GC_REQUEST_COMMAND:
1746                         strunzone(rankings_reply);
1747                         rankings_reply = strzone(getrankings());
1748                         print(rankings_reply);
1749                         return;
1750                         
1751                 default:
1752                 case GC_REQUEST_USAGE:
1753                         print("\nUsage:^3 sv_cmd rankings\n");
1754                         print("  No arguments required.\n");
1755                         return;
1756         }
1757 }
1758
1759 void GameCommand_records(float request)
1760 {
1761         float i;
1762         
1763         switch(request)
1764         {
1765                 case GC_REQUEST_HELP:
1766                         print("  ^2records^7: List top 10 records for the current map\n");
1767                         return;
1768                         
1769                 case GC_REQUEST_COMMAND:
1770                         for (i = 0; i < 10; ++i)
1771                                 print(records_reply[i]);
1772                         return;
1773                         
1774                 default:
1775                 case GC_REQUEST_USAGE:
1776                         print("\nUsage:^3 sv_cmd records\n");
1777                         print("  No arguments required.\n");
1778                         return;
1779         }
1780 }
1781
1782 void GameCommand_reducematchtime(float request)
1783 {
1784         switch(request)
1785         {
1786                 case GC_REQUEST_HELP:
1787                         print("  ^2reducematchtime^7: Decrease the timelimit value incrementally\n");
1788                         return;
1789                         
1790                 case GC_REQUEST_COMMAND:
1791                         changematchtime(autocvar_timelimit_decrement*-60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
1792                         return;
1793                         
1794                 default:
1795                 case GC_REQUEST_USAGE:
1796                         print("\nUsage:^3 sv_cmd reducematchtime\n");
1797                         print("  No arguments required.\n");
1798                         print("See also: ^2extendmatchtime^7\n");
1799                         return;
1800         }
1801 }
1802
1803 void GameCommand_stuffto(float request, float argc)
1804 {
1805         // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
1806         // Because of this, it is disabled by default and must be enabled by the server owner when doing compilation. That way,
1807         // we can be certain they understand the risks of it... So to enable, compile server with -DSTUFFTO_ENABLED argument.
1808         
1809         entity client;
1810         float entno;
1811         
1812         #ifdef STUFFTO_ENABLED
1813         switch(request)
1814         {
1815                 case GC_REQUEST_HELP:
1816                         print("  ^2stuffto^7: Send a command to be executed on a client\n");
1817                         return;
1818                         
1819                 case GC_REQUEST_COMMAND:
1820                         if(argc == 3)
1821                         {
1822                                 entno = stof(argv(1));
1823                                 client = world;
1824                                 if(entno <= maxclients)
1825                                         client = edict_num(entno);
1826                                 if(client.flags & FL_CLIENT)
1827                                 {
1828                                         stuffcmd(client, strcat("\n", argv(2), "\n"));
1829                                         print(strcat("Command: \"", argv(2), "\" sent to ", client.netname, " (", argv(1) ,").\n"));
1830                                 }
1831                                 else
1832                                         print(strcat("Client (", argv(1) ,") not found.\n"));
1833                                 
1834                                 return;
1835                         }
1836                         
1837                 default:
1838                         print("Incorrect parameters for ^2stuffto^7\n");
1839                 case GC_REQUEST_USAGE:
1840                         print("\nUsage:^3 sv_cmd stuffto clientnumber command\n");
1841                         print("  FIXME: Arguments currently unknown\n");
1842                         return;
1843         }
1844         #else // give the response for missing command to fake them out ;3
1845         if(request == GC_REQUEST_COMMAND)
1846         {
1847                 print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
1848                 return;
1849         }
1850         #endif
1851 }
1852
1853 void GameCommand_teamstatus(float request)
1854 {
1855         switch(request)
1856         {
1857                 case GC_REQUEST_HELP:
1858                         print("  ^2teamstatus^7: Show information about player and team scores\n");
1859                         return;
1860                         
1861                 case GC_REQUEST_COMMAND:
1862                         Score_NicePrint(world);
1863                         return;
1864                         
1865                 default:
1866                 case GC_REQUEST_USAGE:
1867                         print("\nUsage:^3 sv_cmd teamstatus\n");
1868                         print("  No arguments required.\n");
1869                         return;
1870         }
1871 }
1872
1873 void GameCommand_time(float request)
1874 {
1875         switch(request)
1876         {
1877                 case GC_REQUEST_HELP:
1878                         print("  ^2time^7: Print different formats/readouts of time\n");
1879                         return;
1880                         
1881                 case GC_REQUEST_COMMAND:
1882                         print("time = ", ftos(time), "\n");
1883                         print("frame start = ", ftos(gettime(GETTIME_FRAMESTART)), "\n");
1884                         print("realtime = ", ftos(gettime(GETTIME_REALTIME)), "\n");
1885                         print("hires = ", ftos(gettime(GETTIME_HIRES)), "\n");
1886                         print("uptime = ", ftos(gettime(GETTIME_UPTIME)), "\n");
1887                         print("localtime = ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"); // FIXME: Why is strftime broken? is engine problem, I think.
1888                         print("gmtime = ", strftime(FALSE, "%a %b %e %H:%M:%S %Z %Y"), "\n");
1889                         return;
1890                         
1891                 default:
1892                 case GC_REQUEST_USAGE:
1893                         print("\nUsage:^3 sv_cmd time\n");
1894                         print("  No arguments required.\n");
1895                         return;
1896         }
1897 }
1898
1899 void GameCommand_trace(float request, float argc)
1900 {
1901         entity e;
1902         vector org, delta, start, end, p, q, q0, pos, vv, dv;
1903         float i, f, safe, unsafe, dq, dqf;
1904         
1905         // TODO: Clean up all of these variables and merge the code below to use only a few
1906                                                 
1907         switch(request)
1908         {
1909                 case GC_REQUEST_HELP:
1910                         print("  ^2trace^7: Various debugging tools with tracing\n");
1911                         return;
1912                         
1913                 case GC_REQUEST_COMMAND:
1914                         switch(argv(1))
1915                         {
1916                                 case "debug":
1917                                         print("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.\n");
1918                                         for(;;)
1919                                         {
1920                                                 org = world.mins;
1921                                                 delta = world.maxs - world.mins;
1922
1923                                                 start_x = org_x + random() * delta_x;
1924                                                 start_y = org_y + random() * delta_y;
1925                                                 start_z = org_z + random() * delta_z;
1926
1927                                                 end_x = org_x + random() * delta_x;
1928                                                 end_y = org_y + random() * delta_y;
1929                                                 end_z = org_z + random() * delta_z;
1930
1931                                                 start = stov(vtos(start));
1932                                                 end = stov(vtos(end));
1933
1934                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1935                                                 if(!trace_startsolid)
1936                                                 {
1937                                                         p = trace_endpos;
1938                                                         tracebox(p, PL_MIN, PL_MAX, p, MOVE_NOMONSTERS, world);
1939                                                         if(trace_startsolid || trace_fraction == 1)
1940                                                         {
1941                                                                 rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
1942                                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1943                                                                 tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1944
1945                                                                 if(trace_startsolid)
1946                                                                 {
1947                                                                         // how much do we need to back off?
1948                                                                         safe = 1;
1949                                                                         unsafe = 0;
1950                                                                         for(;;)
1951                                                                         {
1952                                                                                 pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
1953                                                                                 tracebox(pos, PL_MIN, PL_MAX, pos, MOVE_NOMONSTERS, world);
1954                                                                                 if(trace_startsolid)
1955                                                                                 {
1956                                                                                         if((safe + unsafe) * 0.5 == unsafe)
1957                                                                                                 break;
1958                                                                                         unsafe = (safe + unsafe) * 0.5;
1959                                                                                 }
1960                                                                                 else
1961                                                                                 {
1962                                                                                         if((safe + unsafe) * 0.5 == safe)
1963                                                                                                 break;
1964                                                                                         safe = (safe + unsafe) * 0.5;
1965                                                                                 }
1966                                                                         }
1967
1968                                                                         print("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
1969                                                                         print("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
1970
1971                                                                         tracebox(p, PL_MIN + '0.1 0.1 0.1', PL_MAX - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, world);
1972                                                                         if(trace_startsolid)
1973                                                                                 print("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1974                                                                         else
1975                                                                                 print("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1976                                                                         break;
1977                                                                 }
1978
1979                                                                 q0 = p;
1980                                                                 dq = 0;
1981                                                                 dqf = 1;
1982                                                                 for(;;)
1983                                                                 {
1984                                                                         q = p + normalize(end - p) * (dq + dqf);
1985                                                                         if(q == q0)
1986                                                                                 break;
1987                                                                         tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1988                                                                         if(trace_startsolid)
1989                                                                                 error("THIS ONE cannot happen");
1990                                                                         if(trace_fraction > 0)
1991                                                                                 dq += dqf * trace_fraction;
1992                                                                         dqf *= 0.5;
1993                                                                         q0 = q;
1994                                                                 }
1995                                                                 if(dq > 0)
1996                                                                 {
1997                                                                         print("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1998                                                                         print("could go ", ftos(dq), " units further to ", vtos(q), "\n");
1999                                                                         break;
2000                                                                 }
2001                                                         }
2002                                                 }
2003                                         }
2004                                         return;
2005                                         
2006                                 case "debug2":
2007                                         e = nextent(world);
2008                                         tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e);
2009                                         vv = trace_endpos;
2010                                         if(trace_fraction == 1)
2011                                         {
2012                                                 print("not above ground, aborting\n");
2013                                                 return;
2014                                         }
2015                                         f = 0;
2016                                         for(i = 0; i < 100000; ++i)
2017                                         {
2018                                                 dv = randomvec();
2019                                                 if(dv_z > 0)
2020                                                         dv = -1 * dv;
2021                                                 tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e);
2022                                                 if(trace_startsolid)
2023                                                         print("bug 1\n");
2024                                                 if(trace_fraction == 1)
2025                                                 if(dv_z < f)
2026                                                 {
2027                                                         print("bug 2: ", ftos(dv_x), " ", ftos(dv_y), " ", ftos(dv_z));
2028                                                         print(" (", ftos(asin(dv_z / vlen(dv)) * 180 / M_PI), " degrees)\n");
2029                                                         f = dv_z;
2030                                                 }
2031                                         }
2032                                         print("highest possible dist: ", ftos(f), "\n");
2033                                         return;
2034                                 
2035                                 case "walk":
2036                                         if(argc == 3)
2037                                         {
2038                                                 e = nextent(world);
2039                                                 if(tracewalk(e, stov(argv(1)), e.mins, e.maxs, stov(argv(2)), MOVE_NORMAL))
2040                                                         print("can walk\n");
2041                                                 else
2042                                                         print("cannot walk\n");
2043                                                 return;
2044                                         }
2045                                                 
2046                                 case "showline":
2047                                         if(argc == 3)
2048                                         {
2049                                                 vv = stov(argv(1));
2050                                                 dv = stov(argv(2));
2051                                                 traceline(vv, dv, MOVE_NORMAL, world);
2052                                                 trailparticles(world, particleeffectnum("TR_NEXUIZPLASMA"), vv, trace_endpos);
2053                                                 trailparticles(world, particleeffectnum("TR_CRYLINKPLASMA"), trace_endpos, dv);
2054                                                 return;
2055                                         }
2056                                         
2057                                 // no default case, just go straight to "invalid arguments"
2058                         }
2059                         
2060                 default:
2061                 case GC_REQUEST_USAGE:
2062                         print("\nUsage:^3 sv_cmd trace command [arguments]\n");
2063                         print("  FIXME: Arguments currently unknown\n");
2064                         return;
2065         }
2066 }
2067
2068 void GameCommand_unlockteams(float request)
2069 {
2070         switch(request)
2071         {
2072                 case GC_REQUEST_HELP:
2073                         print("  ^2unlockteams^7: Enable the ability for players to switch or enter teams\n");
2074                         return;
2075                         
2076                 case GC_REQUEST_COMMAND:
2077                         if(teamplay)
2078                         {
2079                                 lockteams = 0;
2080                                 bprint("^1The teams are now unlocked.\n");
2081                         }
2082                         else
2083                                 bprint("That command can only be used in a team-based gamemode.\n");
2084                         return;
2085                         
2086                 default:
2087                 case GC_REQUEST_USAGE:
2088                         print("\nUsage:^3 sv_cmd unlockteams\n");
2089                         print("  No arguments required.\n");
2090                         print("See also: ^2lockteams^7\n");
2091                         return;
2092         }
2093 }
2094
2095
2096 // =========================================
2097 //  Main Function Called By Engine (sv_cmd)
2098 // =========================================
2099 // If this function exists, game code handles gamecommand instead of the engine code.
2100
2101 void GameCommand(string command)
2102 {
2103         // ===== TODO list =====
2104         // find all FIXME's and TODO'S and UNTESTED'S and finish them :P
2105
2106         float search_request_type;
2107         float argc = tokenize_console(command);
2108
2109         if(strtolower(argv(0)) == "help") 
2110         {
2111                 if(argc == 1) 
2112                 {
2113                         print("\nUsage:^3 sv_cmd COMMAND...^7, where possible commands are:\n");
2114                         GameCommand_adminmsg(GC_REQUEST_HELP, 0);
2115                         GameCommand_allready(GC_REQUEST_HELP);
2116                         GameCommand_allspec(GC_REQUEST_HELP, 0);
2117                         GameCommand_anticheat(GC_REQUEST_HELP, 0);
2118                         GameCommand_bbox(GC_REQUEST_HELP);
2119                         GameCommand_bot_cmd(GC_REQUEST_HELP, 0);
2120                         GameCommand_cointoss(GC_REQUEST_HELP, 0);
2121                         GameCommand_cvar_changes(GC_REQUEST_HELP);
2122                         GameCommand_cvar_purechanges(GC_REQUEST_HELP);
2123                         GameCommand_database(GC_REQUEST_HELP, 0);
2124                         GameCommand_defer_clear(GC_REQUEST_HELP, 0);
2125                         GameCommand_defer_clear_all(GC_REQUEST_HELP);
2126                         GameCommand_delrec(GC_REQUEST_HELP, 0);
2127                         GameCommand_effectindexdump(GC_REQUEST_HELP);
2128                         GameCommand_extendmatchtime(GC_REQUEST_HELP);
2129                         GameCommand_find(GC_REQUEST_HELP, 0);
2130                         GameCommand_gametype(GC_REQUEST_HELP, 0);
2131                         GameCommand_gettaginfo(GC_REQUEST_HELP, 0);
2132                         GameCommand_gotomap(GC_REQUEST_HELP, 0);
2133                         GameCommand_ladder(GC_REQUEST_HELP);
2134                         GameCommand_lockteams(GC_REQUEST_HELP);
2135                         GameCommand_make_mapinfo(GC_REQUEST_HELP);
2136                         GameCommand_modelbug(GC_REQUEST_HELP);
2137                         GameCommand_moveplayer(GC_REQUEST_HELP, 0);
2138                         GameCommand_nospectators(GC_REQUEST_HELP);
2139                         GameCommand_onslaught_updatelinks(GC_REQUEST_HELP);
2140                         GameCommand_playerdemo(GC_REQUEST_HELP, 0);
2141                         GameCommand_printstats(GC_REQUEST_HELP);
2142                         GameCommand_radarmap(GC_REQUEST_HELP, 0);
2143                         GameCommand_rankings(GC_REQUEST_HELP);
2144                         GameCommand_records(GC_REQUEST_HELP);
2145                         GameCommand_reducematchtime(GC_REQUEST_HELP);
2146                         GameCommand_stuffto(GC_REQUEST_HELP, 0);
2147                         GameCommand_teamstatus(GC_REQUEST_HELP);
2148                         GameCommand_time(GC_REQUEST_HELP);
2149                         GameCommand_trace(GC_REQUEST_HELP, 0);
2150                         GameCommand_unlockteams(GC_REQUEST_HELP);
2151                         GameCommand_Vote("help", world);
2152                         GameCommand_Ban("help");
2153                         GameCommand_Generic("help");
2154                         print("For help about specific commands, type sv_cmd help COMMAND\n");
2155                         return;
2156                 } 
2157                 else
2158                         search_request_type = GC_REQUEST_USAGE; // Instead of trying to call a command, we're going to see detailed information about it
2159         } 
2160         else if(GameCommand_Vote(command, world)) 
2161         {
2162                 return; // handled by server/vote.qc 
2163         }
2164         else if(GameCommand_Ban(command)) 
2165         {
2166                 return; // handled by server/ipban.qc
2167         }
2168         else if(GameCommand_Generic(command)) 
2169         {
2170                 return; // handled by common/gamecommand.qc
2171         }
2172         else
2173                 search_request_type = GC_REQUEST_COMMAND; // continue as usual and scan for normal commands
2174                 
2175         switch(strtolower((search_request_type == GC_REQUEST_USAGE) ? argv(1) : argv(0))) // if first argument is help, then search for the second argument. Else, search for first. 
2176         {
2177                 // Do not hard code aliases for these, instead create them in defaultXonotic.cfg
2178                 // also: keep in alphabetical order, please ;)
2179                 
2180                 case "adminmsg": GameCommand_adminmsg(search_request_type, argc); break;
2181                 case "allready": GameCommand_allready(search_request_type); break;
2182                 case "allspec": GameCommand_allspec(search_request_type, argc); break;
2183                 case "anticheat": GameCommand_anticheat(search_request_type, argc); break;
2184                 case "bbox": GameCommand_bbox(search_request_type); break;
2185                 case "bot_cmd": GameCommand_bot_cmd(search_request_type, argc); break;
2186                 case "cointoss": GameCommand_cointoss(search_request_type, argc); break; 
2187                 case "cvar_changes": GameCommand_cvar_changes(search_request_type); break; 
2188                 case "cvar_purechanges": GameCommand_cvar_purechanges(search_request_type); break; 
2189                 case "database": GameCommand_database(search_request_type, argc); break;
2190                 case "defer_clear": GameCommand_defer_clear(search_request_type, argc); break;
2191                 case "defer_clear_all": GameCommand_defer_clear_all(search_request_type); break;
2192                 case "delrec": GameCommand_delrec(search_request_type, argc); break;
2193                 case "effectindexdump": GameCommand_effectindexdump(search_request_type); break;
2194                 case "extendmatchtime": GameCommand_extendmatchtime(search_request_type); break;
2195                 case "find": GameCommand_find(search_request_type, argc); break; 
2196                 case "gametype": GameCommand_gametype(search_request_type, argc); break;
2197                 case "gettaginfo": GameCommand_gettaginfo(search_request_type, argc); break;
2198                 case "gotomap": GameCommand_gotomap(search_request_type, argc); break;
2199                 case "ladder": GameCommand_ladder(search_request_type); break;
2200                 case "lockteams": GameCommand_lockteams(search_request_type); break;
2201                 case "make_mapinfo": GameCommand_make_mapinfo(search_request_type); break;
2202                 case "modelbug": GameCommand_modelbug(search_request_type); break;
2203                 case "moveplayer": GameCommand_moveplayer(search_request_type, argc); break;
2204                 case "nospectators": GameCommand_nospectators(search_request_type); break;
2205                 case "onslaught_updatelinks": GameCommand_onslaught_updatelinks(search_request_type); break;
2206                 case "playerdemo": GameCommand_playerdemo(search_request_type, argc); break;
2207                 case "printstats": GameCommand_printstats(search_request_type); break;
2208                 case "radarmap": GameCommand_radarmap(search_request_type, argc); break;
2209                 case "rankings": GameCommand_rankings(search_request_type); break;
2210                 case "records": GameCommand_records(search_request_type); break;
2211                 case "reducematchtime": GameCommand_reducematchtime(search_request_type); break;
2212                 case "stuffto": GameCommand_stuffto(search_request_type, argc); break;
2213                 case "teamstatus": GameCommand_teamstatus(search_request_type); break;
2214                 case "time": GameCommand_time(search_request_type); break;
2215                 case "trace": GameCommand_trace(search_request_type, argc); break;
2216                 case "unlockteams": GameCommand_unlockteams(search_request_type); break;
2217                 
2218                 default:
2219                         print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
2220         }
2221 }