]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/gamecommand.qc
Add teamstatus and trace commands (half-assed, needs more work) and lots of comment...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / gamecommand.qc
1 // =====================================================
2 //  Server side game commands code, reworked by Samual
3 //  Last updated: July 13th, 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 message 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) // todo: Add ability to provide a reason string?
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 players to spectate (", ftos(i), ")\n")); } // should a message be added if no players were found? 
594                         return;
595                         
596                 default:
597                 case GC_REQUEST_USAGE:
598                         print("\nUsage:^3 sv_cmd allspec\n");
599                         print("  No arguments required.\n");
600                         print("See also: ^2moveplayer^7\n");
601                         return;
602         }
603 }
604
605 void GameCommand_anticheat(float request, float argc) // FIXME: player entity is never found
606 {
607         entity client;
608         float entno = stof(argv(1)); 
609         
610         switch(request)
611         {
612                 case GC_REQUEST_HELP:
613                         print("  ^2anticheat^7: Create an anticheat report for a client\n");
614                         return;
615                         
616                 case GC_REQUEST_COMMAND:
617                         if((entno < 1) | (entno > maxclients)) {
618                                 print("Player ", argv(1), " doesn't exist\n");
619                                 return;
620                         }
621                         client = edict_num(entno);
622                         if(clienttype(client) != CLIENTTYPE_REAL && clienttype(client) != CLIENTTYPE_BOT) {
623                                 print("Player ", client.netname, " is not active\n");
624                                 return;
625                         }
626                         self = client;
627                         anticheat_report();
628                         return;
629                         
630                 default:
631                         print("Incorrect parameters for ^2anticheat^7\n");
632                 case GC_REQUEST_USAGE:
633                         print("\nUsage:^3 sv_cmd anticheat clientnumber\n");
634                         print("  where clientnumber is player entity number.\n");
635                         return;
636         }
637 }
638
639 void GameCommand_bbox(float request)
640 {
641         switch(request)
642         {
643                 case GC_REQUEST_HELP:
644                         print("  ^2bbox^7: Print detailed information about world size\n");
645                         return;
646                         
647                 case GC_REQUEST_COMMAND:
648                         print("Original size: ", ftos(world.absmin_x), " ", ftos(world.absmin_y), " ", ftos(world.absmin_z));
649                         print(" ", ftos(world.absmax_x), " ", ftos(world.absmax_y), " ", ftos(world.absmax_z), "\n");
650                         print("Currently set size: ", ftos(world.mins_x), " ", ftos(world.mins_y), " ", ftos(world.mins_z));
651                         print(" ", ftos(world.maxs_x), " ", ftos(world.maxs_y), " ", ftos(world.maxs_z), "\n");
652                         print("Solid bounding box size:");
653
654                         tracebox('1 0 0' * world.absmin_x,
655                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
656                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
657                                                         '1 0 0' * world.absmax_x,
658                                         MOVE_WORLDONLY,
659                                         world);
660                         if(trace_startsolid)
661                                 print(" ", ftos(world.absmin_x));
662                         else
663                                 print(" ", ftos(trace_endpos_x));
664
665                         tracebox('0 1 0' * world.absmin_y,
666                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
667                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
668                                                         '0 1 0' * world.absmax_y,
669                                         MOVE_WORLDONLY,
670                                         world);
671                         if(trace_startsolid)
672                                 print(" ", ftos(world.absmin_y));
673                         else
674                                 print(" ", ftos(trace_endpos_y));
675
676                         tracebox('0 0 1' * world.absmin_z,
677                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
678                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
679                                                         '0 0 1' * world.absmax_z,
680                                         MOVE_WORLDONLY,
681                                         world);
682                         if(trace_startsolid)
683                                 print(" ", ftos(world.absmin_z));
684                         else
685                                 print(" ", ftos(trace_endpos_z));
686
687                         tracebox('1 0 0' * world.absmax_x,
688                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
689                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
690                                                         '1 0 0' * world.absmin_x,
691                                         MOVE_WORLDONLY,
692                                         world);
693                         if(trace_startsolid)
694                                 print(" ", ftos(world.absmax_x));
695                         else
696                                 print(" ", ftos(trace_endpos_x));
697
698                         tracebox('0 1 0' * world.absmax_y,
699                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
700                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
701                                                         '0 1 0' * world.absmin_y,
702                                         MOVE_WORLDONLY,
703                                         world);
704                         if(trace_startsolid)
705                                 print(" ", ftos(world.absmax_y));
706                         else
707                                 print(" ", ftos(trace_endpos_y));
708
709                         tracebox('0 0 1' * world.absmax_z,
710                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
711                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
712                                                         '0 0 1' * world.absmin_z,
713                                         MOVE_WORLDONLY,
714                                         world);
715                         if(trace_startsolid)
716                                 print(" ", ftos(world.absmax_z));
717                         else
718                                 print(" ", ftos(trace_endpos_z));
719                                 
720                         print("\n");
721                         return;
722                         
723                 default:
724                 case GC_REQUEST_USAGE:
725                         print("\nUsage:^3 sv_cmd bbox\n");
726                         print("  No arguments required.\n");
727                         print("See also: ^2gettaginfo^7\n");
728                         return;
729         }
730 }
731
732 void GameCommand_bot_cmd(float request, float argc) // what a mess... old old code.
733 {
734         entity bot;
735         
736         switch(request)
737         {
738                 case GC_REQUEST_HELP:
739                         print("  ^2bot_cmd^7: Control and send commands to bots\n");
740                         return;
741                         
742                 case GC_REQUEST_COMMAND:
743                         if(argv(1) == "reset")
744                         {
745                                 bot_resetqueues();
746                                 return;
747                         }
748                         else if(argv(1) == "load" && argc == 3)
749                         {
750                                 float fh, i;
751                                 string s;
752                                 fh = fopen(argv(2), FILE_READ);
753                                 if(fh < 0)
754                                 {
755                                         print("cannot open the file\n");
756                                         return;
757                                 }
758
759                                 i = 0;
760                                 while((s = fgets(fh)))
761                                 {
762                                         argc = tokenize_console(s);
763
764                                         if(argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd")
765                                         {
766                                                 // let's start at token 2 so we can skip sv_cmd bot_cmd
767                                                 bot = find_bot_by_number(stof(argv(2)));
768                                                 if(bot == world)
769                                                         bot = find_bot_by_name(argv(2));
770                                                 if(bot)
771                                                         bot_queuecommand(bot, strcat(argv(3), " ", argv(4)));
772                                         }
773                                         else
774                                                 localcmd(strcat(s, "\n"));
775
776                                         ++i;
777                                 }
778                                 print(ftos(i), " commands read\n");
779                                 fclose(fh);
780                                 return;
781                         }
782                         else if(argv(1) == "help")
783                         {
784                                 if(argv(2))
785                                         bot_cmdhelp(argv(2));
786                                 else
787                                         bot_list_commands();
788                                 return;
789                         }
790                         else if(argc >= 3) // this comes last
791                         {
792                                 bot = find_bot_by_number(stof(argv(1)));
793                                 if(bot == world)
794                                         bot = find_bot_by_name(argv(1));
795                                 if(bot)
796                                 {
797                                         print(strcat("Command '", (argv(2), " ", argv(3)), "' sent to bot ", bot.netname, "\n"));
798                                         bot_queuecommand(bot, strcat(argv(2), " ", argv(3)));
799                                         return;
800                                 }
801                                 else
802                                         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
803                         }
804                         
805                 default:
806                         print("Incorrect parameters for ^2bot_cmd^7\n");
807                 case GC_REQUEST_USAGE:
808                         print("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n");
809                         print("  'client' can be either the name or entity id of the bot\n");
810                         print("  For full list of commands, see bot_cmd help [command].\n");
811                         print("Examples: bot_cmd <id> cc \"say something\"\n");
812                         print("          bot_cmd <id> presskey jump\n");
813                         return;
814         }
815 }
816
817 void GameCommand_cointoss(float request) // todo: Perhaps add the ability to give your own arguments to pick between? (Like player names)
818 {
819         entity client;
820         float choice = (random() > 0.5);
821         
822         switch(request)
823         {
824                 case GC_REQUEST_HELP:
825                         print("  ^2cointoss^7: Flip a virtual coin and give random result\n");
826                         return;
827                         
828                 case GC_REQUEST_COMMAND:
829                         FOR_EACH_CLIENT(client)
830                                 centerprint(client, strcat("^3Throwing coin... Result: ", (choice ? "^1HEADS^3!\n" : "^4TAILS^3!\n")));
831                         bprint(strcat("^3Throwing coin... Result: ", (choice ? "^1HEADS^3!\n" : "^4TAILS^3!\n")));
832                         return;
833                         
834                 default:
835                 case GC_REQUEST_USAGE:
836                         print("\nUsage:^3 sv_cmd cointoss\n");
837                         print("  No arguments required.\n");
838                         return;
839         }
840 }
841
842 void GameCommand_cvar_changes(float request)
843 {
844         switch(request)
845         {
846                 case GC_REQUEST_HELP:
847                         print("  ^2cvar_changes^7: Prints a list of all changed server cvars\n");
848                         return;
849                         
850                 case GC_REQUEST_COMMAND:
851                         print(cvar_changes);
852                         return;
853                         
854                 default:
855                 case GC_REQUEST_USAGE:
856                         print("\nUsage:^3 sv_cmd \n");
857                         print("  No arguments required.\n");
858                         print("See also: ^2cvar_purechanges^7\n");
859                         return;
860         }
861 }
862
863 void GameCommand_cvar_purechanges(float request)
864 {
865         switch(request)
866         {
867                 case GC_REQUEST_HELP:
868                         print("  ^2cvar_purechanges^7: Prints a list of all changed gameplay cvars\n");
869                         return;
870                         
871                 case GC_REQUEST_COMMAND:
872                         print(cvar_purechanges);
873                         return;
874                         
875                 default:
876                 case GC_REQUEST_USAGE:
877                         print("\nUsage:^3 sv_cmd cvar_purechanges\n");
878                         print("  No arguments required.\n");
879                         print("See also: ^2cvar_changes^7\n");
880                         return;
881         }
882 }
883
884 void GameCommand_database(float request, float argc)
885 {
886         switch(request)
887         {
888                 case GC_REQUEST_HELP:
889                         print("  ^2database^7: Extra controls of the serverprogs database\n");
890                         return;
891                         
892                 case GC_REQUEST_COMMAND:
893                         if(argc == 3)
894                         {
895                                 if(argv(1) == "save")
896                                 {
897                                         db_save(ServerProgsDB, argv(2));
898                                         print(strcat("Copied serverprogs database to '", argv(2), "' in the data directory.\n"));
899                                         return;
900                                 }
901                                 else if(argv(1) == "dump")
902                                 {
903                                         db_dump(ServerProgsDB, argv(2));
904                                         print("DB dumped.\n"); // wtf does this do?
905                                         return;
906                                 }
907                                 else if(argv(1) == "load")
908                                 {
909                                         db_close(ServerProgsDB);
910                                         ServerProgsDB = db_load(argv(2));
911                                         print(strcat("Loaded '", argv(2), "' as new serverprogs database.\n"));
912                                         return;
913                                 }
914                         }
915                         
916                 default:
917                         print("Incorrect parameters for ^2database^7\n");
918                 case GC_REQUEST_USAGE:
919                         print("\nUsage:^3 sv_cmd database action filename\n");
920                         print("  Where 'action' is the command to complete,\n");
921                         print("  and 'filename' is what it acts upon.\n");
922                         print("  Full list of commands here: \"save, dump, load.\"\n");
923                         return;
924         }
925 }
926
927 void GameCommand_defer_clear(float request, float argc)
928 {
929         entity client;
930         float entno = stof(argv(1));
931         
932         switch(request)
933         {
934                 case GC_REQUEST_HELP:
935                         print("  ^2defer_clear^7: Clear all queued defer commands for client\n");
936                         return;
937                         
938                 case GC_REQUEST_COMMAND:
939                         if(argc == 2)
940                         {
941                                 // player_id is out of range
942                                 if((entno < 1) | (entno > maxclients)) {
943                                         print("Player ", argv(1), " doesn't exist\n");
944                                         return;
945                                 }
946                                 client = edict_num(entno);
947                                 if not(client.flags & FL_CLIENT) {
948                                         print("Player ", argv(1), " doesn't exist\n");
949                                         return;
950                                 }
951                                 if(clienttype(client) == CLIENTTYPE_BOT) {
952                                         print("Player ", argv(1), " (", client.netname, ") is a bot\n");
953                                         return;
954                                 }
955                                 stuffcmd(client, "defer clear\n");
956                                 print("defer clear stuffed to ", argv(1), " (", client.netname, ")\n");
957                                 return;
958                         }
959                 
960                 default:
961                         print("Incorrect parameters for ^2defer_clear^7\n");
962                 case GC_REQUEST_USAGE:
963                         print("\nUsage:^3 sv_cmd defer_clear clientnumber\n");
964                         print("  where 'clientnumber' is player entity number.\n");
965                         print("See also: ^2defer_clear_all^7\n");
966                         return;
967         }
968 }
969
970 void GameCommand_defer_clear_all(float request)
971 {
972         entity client;
973         float i;
974         float argc;
975         
976         switch(request)
977         {
978                 case GC_REQUEST_HELP:
979                         print("  ^2defer_clear_all^7: Clear all queued defer commands for all clients\n");
980                         return;
981                         
982                 case GC_REQUEST_COMMAND:
983                         FOR_EACH_CLIENT(client)
984                         {
985                                 argc = tokenize_console(strcat("defer_clear ", ftos(num_for_edict(client))));
986                                 GameCommand_defer_clear(GC_REQUEST_COMMAND, argc);      
987                                 ++i;
988                         }
989                         if(i) { bprint(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found? 
990                         return;
991                 
992                 default:
993                 case GC_REQUEST_USAGE:
994                         print("\nUsage:^3 sv_cmd defer_clear_all\n");
995                         print("  No arguments required.\n");
996                         print("See also: ^2defer_clear^7\n");
997                         return;
998         }
999 }
1000
1001 void GameCommand_delrec(float request, float argc) // UNTESTED // perhaps merge later with records and printstats and such?
1002 {
1003         switch(request)
1004         {
1005                 case GC_REQUEST_HELP:
1006                         print("  ^2delrec^7: Delete race time record for a map\n");
1007                         return;
1008                         
1009                 case GC_REQUEST_COMMAND:
1010                         if(argv(1))
1011                         {
1012                                 if(argv(2))
1013                                         race_deleteTime(argv(2), stof(argv(1)));
1014                                 else
1015                                         race_deleteTime(GetMapname(), stof(argv(1)));
1016                                 return;
1017                         }
1018                         
1019                 default:
1020                         print("Incorrect parameters for ^2delrec^7\n");
1021                 case GC_REQUEST_USAGE:
1022                         print("\nUsage:^3 sv_cmd delrec ranking [map]\n");
1023                         print("  'ranking' is which ranking level to clear up to, \n");
1024                         print("  it will clear all records up to nth place.\n");
1025                         print("  if 'map' is not provided it will use current map.\n");
1026                         return;
1027         }
1028 }
1029
1030 void GameCommand_effectindexdump(float request)
1031 {
1032         float fh, d;
1033         string s;
1034         
1035         switch(request)
1036         {
1037                 case GC_REQUEST_HELP:
1038                         print("  ^2effectindexdump^7: Dump list of effects from code and effectinfo.txt\n");
1039                         return;
1040                         
1041                 case GC_REQUEST_COMMAND:
1042                         d = db_create();
1043                         print("begin of effects list\n");
1044                         db_put(d, "TE_GUNSHOT", "1"); print("effect TE_GUNSHOT is ", ftos(particleeffectnum("TE_GUNSHOT")), "\n");
1045                         db_put(d, "TE_GUNSHOTQUAD", "1"); print("effect TE_GUNSHOTQUAD is ", ftos(particleeffectnum("TE_GUNSHOTQUAD")), "\n");
1046                         db_put(d, "TE_SPIKE", "1"); print("effect TE_SPIKE is ", ftos(particleeffectnum("TE_SPIKE")), "\n");
1047                         db_put(d, "TE_SPIKEQUAD", "1"); print("effect TE_SPIKEQUAD is ", ftos(particleeffectnum("TE_SPIKEQUAD")), "\n");
1048                         db_put(d, "TE_SUPERSPIKE", "1"); print("effect TE_SUPERSPIKE is ", ftos(particleeffectnum("TE_SUPERSPIKE")), "\n");
1049                         db_put(d, "TE_SUPERSPIKEQUAD", "1"); print("effect TE_SUPERSPIKEQUAD is ", ftos(particleeffectnum("TE_SUPERSPIKEQUAD")), "\n");
1050                         db_put(d, "TE_WIZSPIKE", "1"); print("effect TE_WIZSPIKE is ", ftos(particleeffectnum("TE_WIZSPIKE")), "\n");
1051                         db_put(d, "TE_KNIGHTSPIKE", "1"); print("effect TE_KNIGHTSPIKE is ", ftos(particleeffectnum("TE_KNIGHTSPIKE")), "\n");
1052                         db_put(d, "TE_EXPLOSION", "1"); print("effect TE_EXPLOSION is ", ftos(particleeffectnum("TE_EXPLOSION")), "\n");
1053                         db_put(d, "TE_EXPLOSIONQUAD", "1"); print("effect TE_EXPLOSIONQUAD is ", ftos(particleeffectnum("TE_EXPLOSIONQUAD")), "\n");
1054                         db_put(d, "TE_TAREXPLOSION", "1"); print("effect TE_TAREXPLOSION is ", ftos(particleeffectnum("TE_TAREXPLOSION")), "\n");
1055                         db_put(d, "TE_TELEPORT", "1"); print("effect TE_TELEPORT is ", ftos(particleeffectnum("TE_TELEPORT")), "\n");
1056                         db_put(d, "TE_LAVASPLASH", "1"); print("effect TE_LAVASPLASH is ", ftos(particleeffectnum("TE_LAVASPLASH")), "\n");
1057                         db_put(d, "TE_SMALLFLASH", "1"); print("effect TE_SMALLFLASH is ", ftos(particleeffectnum("TE_SMALLFLASH")), "\n");
1058                         db_put(d, "TE_FLAMEJET", "1"); print("effect TE_FLAMEJET is ", ftos(particleeffectnum("TE_FLAMEJET")), "\n");
1059                         db_put(d, "EF_FLAME", "1"); print("effect EF_FLAME is ", ftos(particleeffectnum("EF_FLAME")), "\n");
1060                         db_put(d, "TE_BLOOD", "1"); print("effect TE_BLOOD is ", ftos(particleeffectnum("TE_BLOOD")), "\n");
1061                         db_put(d, "TE_SPARK", "1"); print("effect TE_SPARK is ", ftos(particleeffectnum("TE_SPARK")), "\n");
1062                         db_put(d, "TE_PLASMABURN", "1"); print("effect TE_PLASMABURN is ", ftos(particleeffectnum("TE_PLASMABURN")), "\n");
1063                         db_put(d, "TE_TEI_G3", "1"); print("effect TE_TEI_G3 is ", ftos(particleeffectnum("TE_TEI_G3")), "\n");
1064                         db_put(d, "TE_TEI_SMOKE", "1"); print("effect TE_TEI_SMOKE is ", ftos(particleeffectnum("TE_TEI_SMOKE")), "\n");
1065                         db_put(d, "TE_TEI_BIGEXPLOSION", "1"); print("effect TE_TEI_BIGEXPLOSION is ", ftos(particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n");
1066                         db_put(d, "TE_TEI_PLASMAHIT", "1"); print("effect TE_TEI_PLASMAHIT is ", ftos(particleeffectnum("TE_TEI_PLASMAHIT")), "\n");
1067                         db_put(d, "EF_STARDUST", "1"); print("effect EF_STARDUST is ", ftos(particleeffectnum("EF_STARDUST")), "\n");
1068                         db_put(d, "TR_ROCKET", "1"); print("effect TR_ROCKET is ", ftos(particleeffectnum("TR_ROCKET")), "\n");
1069                         db_put(d, "TR_GRENADE", "1"); print("effect TR_GRENADE is ", ftos(particleeffectnum("TR_GRENADE")), "\n");
1070                         db_put(d, "TR_BLOOD", "1"); print("effect TR_BLOOD is ", ftos(particleeffectnum("TR_BLOOD")), "\n");
1071                         db_put(d, "TR_WIZSPIKE", "1"); print("effect TR_WIZSPIKE is ", ftos(particleeffectnum("TR_WIZSPIKE")), "\n");
1072                         db_put(d, "TR_SLIGHTBLOOD", "1"); print("effect TR_SLIGHTBLOOD is ", ftos(particleeffectnum("TR_SLIGHTBLOOD")), "\n");
1073                         db_put(d, "TR_KNIGHTSPIKE", "1"); print("effect TR_KNIGHTSPIKE is ", ftos(particleeffectnum("TR_KNIGHTSPIKE")), "\n");
1074                         db_put(d, "TR_VORESPIKE", "1"); print("effect TR_VORESPIKE is ", ftos(particleeffectnum("TR_VORESPIKE")), "\n");
1075                         db_put(d, "TR_NEHAHRASMOKE", "1"); print("effect TR_NEHAHRASMOKE is ", ftos(particleeffectnum("TR_NEHAHRASMOKE")), "\n");
1076                         db_put(d, "TR_NEXUIZPLASMA", "1"); print("effect TR_NEXUIZPLASMA is ", ftos(particleeffectnum("TR_NEXUIZPLASMA")), "\n");
1077                         db_put(d, "TR_GLOWTRAIL", "1"); print("effect TR_GLOWTRAIL is ", ftos(particleeffectnum("TR_GLOWTRAIL")), "\n");
1078                         db_put(d, "TR_SEEKER", "1"); print("effect TR_SEEKER is ", ftos(particleeffectnum("TR_SEEKER")), "\n");
1079                         db_put(d, "SVC_PARTICLE", "1"); print("effect SVC_PARTICLE is ", ftos(particleeffectnum("SVC_PARTICLE")), "\n");
1080
1081                         fh = fopen("effectinfo.txt", FILE_READ);
1082                         while((s = fgets(fh)))
1083                         {
1084                                 tokenize(s); // tokenize_console would hit the loop counter :(
1085                                 if(argv(0) == "effect")
1086                                 {
1087                                         if(db_get(d, argv(1)) != "1")
1088                                         {
1089                                                 if(particleeffectnum(argv(1)) >= 0)
1090                                                         print("effect ", argv(1), " is ", ftos(particleeffectnum(argv(1))), "\n");
1091                                                 db_put(d, argv(1), "1");
1092                                         }
1093                                 }
1094                         }
1095                         print("end of effects list\n");
1096
1097                         db_close(d);
1098                         return;
1099                         
1100                 default:
1101                 case GC_REQUEST_USAGE:
1102                         print("\nUsage:^3 sv_cmd effectindexdump\n");
1103                         print("  No arguments required.\n");
1104                         return;
1105         }
1106 }
1107
1108 void GameCommand_extendmatchtime(float request) // todo: Perhaps allows the user to send a specific time to extend it.
1109 {
1110         switch(request)
1111         {
1112                 case GC_REQUEST_HELP:
1113                         print("  ^2extendmatchtime^7: Increase the timelimit value incrementally\n");
1114                         return;
1115                         
1116                 case GC_REQUEST_COMMAND:
1117                         changematchtime(autocvar_timelimit_increment* 60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
1118                         return;
1119                         
1120                 default:
1121                 case GC_REQUEST_USAGE:
1122                         print("\nUsage:^3 sv_cmd extendmatchtime\n");
1123                         print("  No arguments required.\n");
1124                         print("See also: ^2reducematchtime^7\n");
1125                         return;
1126         }
1127 }
1128
1129 void GameCommand_find(float request, float argc)
1130 {
1131         entity client;
1132         
1133         switch(request)
1134         {
1135                 case GC_REQUEST_HELP:
1136                         print("  ^2find^7: Search through entities for matching classname\n");
1137                         return;
1138                         
1139                 case GC_REQUEST_COMMAND:
1140                         for(client = world; (client = find(client, classname, argv(1))); )
1141                                 print(etos(client), "\n");
1142                         return;
1143                         
1144                 default:
1145                         print("Incorrect parameters for ^2find^7\n");
1146                 case GC_REQUEST_USAGE:
1147                         print("\nUsage:^3 sv_cmd find classname\n");
1148                         print("  Where 'classname' is the classname to search for.\n");
1149                         return;
1150         }
1151 }
1152
1153 void GameCommand_gametype(float request, float argc)
1154 {
1155         string s = argv(1);
1156         float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
1157         
1158         switch(request)
1159         {
1160                 case GC_REQUEST_HELP:
1161                         print("  ^2gametype^7: Simple command to change the active gametype\n");
1162                         return;
1163                         
1164                 case GC_REQUEST_COMMAND:
1165                         if(t)
1166                         {
1167                                 MapInfo_SwitchGameType(t);
1168                                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
1169                                 if(MapInfo_count > 0)
1170                                         bprint("Game type successfully switched to ", s, "\n");
1171                                 else
1172                                 {
1173                                         bprint("Cannot use this game type: no map for it found\n");
1174                                         MapInfo_SwitchGameType(tsave);
1175                                         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
1176                                 }
1177                         }
1178                         else
1179                                 bprint("Game type switch to ", s, " failed: this type does not exist!\n");
1180                         return;
1181                         
1182                 default:
1183                         print("Incorrect parameters for ^2gametype^7\n");
1184                 case GC_REQUEST_USAGE:
1185                         print("\nUsage:^3 sv_cmd gametype mode\n");
1186                         print("  Where 'mode' is the gametype mode to switch to.\n");
1187                         print("See also: ^2gotomap^7\n");
1188                         return;
1189         }
1190 }
1191
1192 void GameCommand_gettaginfo(float request, float argc) // UNTESTED // todo: finish usage description for it (but, must first learn this shit)
1193 {
1194         entity tmp_entity;
1195         float i;
1196         vector v;
1197         
1198         switch(request)
1199         {
1200                 case GC_REQUEST_HELP:
1201                         print("  ^2gettaginfo^7: Get specific information about a weapon model\n");
1202                         return;
1203                         
1204                 case GC_REQUEST_COMMAND:
1205                         if(argc >= 4)
1206                         {
1207                                 tmp_entity = spawn();
1208                                 if(argv(1) == "w")
1209                                         setmodel(tmp_entity, (nextent(world)).weaponentity.model);
1210                                 else
1211                                 {
1212                                         precache_model(argv(1));
1213                                         setmodel(tmp_entity, argv(1));
1214                                 }
1215                                 tmp_entity.frame = stof(argv(2));
1216                                 if(substring(argv(3), 0, 1) == "#")
1217                                         i = stof(substring(argv(3), 1, -1));
1218                                 else
1219                                         i = gettagindex(tmp_entity, argv(3));
1220                                 if(i)
1221                                 {
1222                                         v = gettaginfo(tmp_entity, i);
1223                                         print("model ", tmp_entity.model, " frame ", ftos(tmp_entity.frame), " tag ", gettaginfo_name);
1224                                         print(" index ", ftos(i), " parent ", ftos(gettaginfo_parent), "\n");
1225                                         print(" vector = ", ftos(v_x), " ", ftos(v_y), " ", ftos(v_z), "\n");
1226                                         print(" offset = ", ftos(gettaginfo_offset_x), " ", ftos(gettaginfo_offset_y), " ", ftos(gettaginfo_offset_z), "\n");
1227                                         print(" forward = ", ftos(gettaginfo_forward_x), " ", ftos(gettaginfo_forward_y), " ", ftos(gettaginfo_forward_z), "\n");
1228                                         print(" right = ", ftos(gettaginfo_right_x), " ", ftos(gettaginfo_right_y), " ", ftos(gettaginfo_right_z), "\n");
1229                                         print(" up = ", ftos(gettaginfo_up_x), " ", ftos(gettaginfo_up_y), " ", ftos(gettaginfo_up_z), "\n");
1230                                         if(argc >= 6)
1231                                         {
1232                                                 v_y = -v_y;
1233                                                 localcmd(strcat(argv(4), vtos(v), argv(5), "\n"));
1234                                         }
1235                                 }
1236                                 else
1237                                         print("bone not found\n");
1238                                         
1239                                 remove(tmp_entity);
1240                                 return;
1241                         }
1242                         
1243                 default:
1244                         print("Incorrect parameters for ^2gettaginfo^7\n");
1245                 case GC_REQUEST_USAGE:
1246                         print("\nUsage:^3 sv_cmd gettaginfo\n");
1247                         print("  FIXME: Arguments currently unknown\n");
1248                         print("See also: ^2bbox^7\n");
1249                         return;
1250         }
1251 }
1252
1253 void GameCommand_gotomap(float request, float argc)
1254 {
1255         switch(request)
1256         {
1257                 case GC_REQUEST_HELP:
1258                         print("  ^2gotomap^7: Simple command to switch to another map\n");
1259                         return;
1260                         
1261                 case GC_REQUEST_COMMAND:
1262                         if(argc == 2)
1263                         {
1264                                 print(GotoMap(argv(1)), "\n");
1265                                 return;
1266                         }
1267                         
1268                 default:
1269                         print("Incorrect parameters for ^2gotomap^7\n");
1270                 case GC_REQUEST_USAGE:
1271                         print("\nUsage:^3 sv_cmd gotomap map\n");
1272                         print("  Where 'map' is the *.bsp file to change to.\n");
1273                         print("See also: ^2gametype^7\n");
1274                         return;
1275         }
1276 }
1277
1278 void GameCommand_ladder(float request)
1279 {
1280         switch(request)
1281         {
1282                 case GC_REQUEST_HELP:
1283                         print("  ^2ladder^7: Get information about top players if supported\n");
1284                         return;
1285                         
1286                 case GC_REQUEST_COMMAND:
1287                         print(ladder_reply);
1288                         return;
1289                         
1290                 default:
1291                 case GC_REQUEST_USAGE:
1292                         print("\nUsage:^3 sv_cmd ladder\n");
1293                         print("  No arguments required.\n");
1294                         return;
1295         }
1296 }
1297
1298 void GameCommand_lockteams(float request)
1299 {
1300         switch(request)
1301         {
1302                 case GC_REQUEST_HELP:
1303                         print("  ^2lockteams^7: Disable the ability for players to switch or enter teams\n");
1304                         return;
1305                         
1306                 case GC_REQUEST_COMMAND:
1307                         if(teamplay)
1308                         {
1309                                 lockteams = 1;
1310                                 bprint("^1The teams are now locked.\n");
1311                         }
1312                         else
1313                                 bprint("That command can only be used in a team-based gamemode.\n");
1314                         return;
1315                         
1316                 default:
1317                 case GC_REQUEST_USAGE:
1318                         print("\nUsage:^3 sv_cmd lockteams\n");
1319                         print("  No arguments required.\n");
1320                         print("See also: ^2unlockteams^7\n");
1321                         return;
1322         }
1323 }
1324
1325 void GameCommand_make_mapinfo(float request) // UNTESTED
1326 {
1327         entity tmp_entity;
1328         
1329         switch(request)
1330         {
1331                 case GC_REQUEST_HELP:
1332                         print("  ^2make_mapinfo^7: Automatically rebuild mapinfo files\n");
1333                         return;
1334                         
1335                 case GC_REQUEST_COMMAND: 
1336                         tmp_entity = spawn();
1337                         tmp_entity.classname = "make_mapinfo";
1338                         tmp_entity.think = make_mapinfo_Think;
1339                         tmp_entity.nextthink = time; // this sucks... todo: re-write this -- Use initializeentity later
1340                         MapInfo_Enumerate();
1341                         return;
1342                         
1343                 default:
1344                 case GC_REQUEST_USAGE:
1345                         print("\nUsage:^3 sv_cmd make_mapinfo\n");
1346                         print("  No arguments required.\n");
1347                         return;
1348         }
1349 }
1350
1351 void GameCommand_modelbug(float request) // UNTESTED // is this even needed anymore? 
1352 {
1353         switch(request)
1354         {
1355                 case GC_REQUEST_HELP:
1356                         print("  ^2modelbug^7: foobar\n");
1357                         return;
1358                         
1359                 case GC_REQUEST_COMMAND:
1360                         modelbug();
1361                         return;
1362                         
1363                 default:
1364                 case GC_REQUEST_USAGE:
1365                         print("\nUsage:^3 sv_cmd modelbug\n");
1366                         print("  No arguments required.\n");
1367                         return;
1368         }
1369 }
1370
1371 void GameCommand_moveplayer(float request, float argc)
1372 {
1373         entity client;
1374         string targets = argv(1);
1375         string destination = argv(2);
1376         string notify = argv(3);
1377         float i;
1378         argc = tokenizebyseparator(targets, ","); // re-use argc for the targets
1379         
1380         switch(request)
1381         {
1382                 case GC_REQUEST_HELP:
1383                         print("  ^2moveplayer^7: Change the team/status of a player\n");
1384                         return;
1385                         
1386                 case GC_REQUEST_COMMAND:
1387                         // lets see if the target(s) even actually exist.
1388                         if((targets) && (destination))
1389                         { 
1390                                 for(i = 0; i < argc; ++i)
1391                                 {
1392                                         // Check to see if the player is a valid target
1393                                         if((stof(argv(i)) < 1) | (stof(argv(i)) > maxclients)) // player_id is out of range
1394                                         {
1395                                                 print(strcat("Player ", argv(i), " doesn't exist", (((i + 1) < argc) ? ", skipping to next player.\n" : ".\n")));
1396                                                 continue; 
1397                                         }
1398                                         client = edict_num(stof(argv(i)));
1399                                         if not(client.flags & FL_CLIENT) // player entity is not a client
1400                                         {
1401                                                 print(strcat("Player ", argv(i), " doesn't exist", (((i + 1) < argc) ? ", skipping to next player.\n" : ".\n")));
1402                                                 continue;
1403                                         }
1404                                         
1405                                         // Where are we putting this player?
1406                                         if(destination == "spec" || destination == "spectator") 
1407                                         {
1408                                                 if(client.classname != "spectator" && client.classname != "observer")
1409                                                 {
1410                                                         self = client;
1411                                                         PutObserverInServer();
1412                                                 }
1413                                                 else
1414                                                 {
1415                                                         print("Player ", argv(i), " (", client.netname, ") is already spectating.\n");
1416                                                 }
1417                                                 return;
1418                                         }
1419                                         else
1420                                         {
1421                                                 if(client.classname != "spectator" && client.classname != "observer")
1422                                                 {
1423                                                         if(teamplay)
1424                                                         {
1425                                                                 // set up
1426                                                                 float team_color;
1427                                                                 float save = client.team_forced;
1428                                                                 client.team_forced = 0;
1429
1430                                                                 // find the team to move the player to
1431                                                                 team_color = ColourToNumber(destination);
1432                                                                 if(team_color == client.team) // already on the destination team
1433                                                                 {
1434                                                                         // keep the forcing undone
1435                                                                         print("Player ", argv(i), " (", client.netname, ") is already on the ", ColoredTeamName(client.team), ".\n");
1436                                                                         return;
1437                                                                 } 
1438                                                                 else if(team_color == 0)  // auto team
1439                                                                 {
1440                                                                         team_color = NumberToTeamNumber(FindSmallestTeam(client, FALSE));
1441                                                                 }
1442                                                                 else
1443                                                                 {
1444                                                                         CheckAllowedTeams(client);
1445                                                                 }
1446                                                                 client.team_forced = save;
1447                                                                 
1448                                                                 // Check to see if the destination team is even available
1449                                                                 switch(team_color) 
1450                                                                 {
1451                                                                         case COLOR_TEAM1:
1452                                                                                 if(c1 == -1) {
1453                                                                                         print("Sorry, can't move player to red team if it doesn't exist.\n");
1454                                                                                         return;
1455                                                                                 }
1456                                                                                 break;
1457
1458                                                                         case COLOR_TEAM2:
1459                                                                                 if(c2 == -1) {
1460                                                                                         print("Sorry, can't move player to blue team if it doesn't exist.\n");
1461                                                                                         return;
1462                                                                                 }
1463                                                                                 break;
1464
1465                                                                         case COLOR_TEAM3:
1466                                                                                 if(c3 == -1) {
1467                                                                                         print("Sorry, can't move player to yellow team if it doesn't exist.\n");
1468                                                                                         return;
1469                                                                                 }
1470                                                                                 break;
1471
1472                                                                         case COLOR_TEAM4:
1473                                                                                 if(c4 == -1) {
1474                                                                                         print("Sorry, can't move player to pink team if it doesn't exist.\n");
1475                                                                                         return;
1476                                                                                 }
1477                                                                                 break;
1478
1479                                                                         default:
1480                                                                                 print("Sorry, can't move player here if team ", destination, " doesn't exist.\n");
1481                                                                                 return;
1482                                                                 }
1483                                                                 
1484                                                                 // If so, lets continue and finally move the player
1485                                                                 client.team_forced = 0;
1486                                                                 MoveToTeam(client, team_color, 6, stof(notify));
1487                                                                 print("Player ", argv(i), " (", client.netname, ") has been moved to the ", ColoredTeamName(team_color), ".\n");
1488                                                                 return;
1489                                                         }
1490                                                         else
1491                                                         {
1492                                                                 print("Can't change teams when currently not playing a team game.\n");
1493                                                                 return;
1494                                                         }
1495                                                 }
1496                                                 else
1497                                                 {
1498                                                         print("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P 
1499                                                         return;
1500                                                 }
1501                                         }
1502                                 }
1503                                 print("No acceptable players given, aborting.\n");
1504                                 return; // still correct parameters so return to avoid usage print
1505                         }
1506                         
1507                 default:
1508                         print("Incorrect parameters for ^2moveplayer^7\n");
1509                 case GC_REQUEST_USAGE:
1510                         print("\nUsage:^3 sv_cmd moveplayer clientnumbers destination [notify]\n");
1511                         print("  'clientnumbers' is a list (separated by commas) of player entity ID's\n");
1512                         print("  'destination' is what to send the player to, be it team or spectating\n");
1513                         print("  Full list of destinations here: \"spec, spectator, red, blue, yellow, pink, auto.\"\n");
1514                         print("  'notify' is whether or not to send messages notifying of the move. Detail below.\n");
1515                         print("    0 (00) automove centerprint, admin message; 1 (01) automove centerprint, no admin message\n");
1516                         print("    2 (10) no centerprint, admin message; 3 (11) no centerprint, no admin message\n");
1517                         print("Examples: moveplayer 1,3,5 red 3\n");
1518                         print("          moveplayer 2 spec \n");
1519                         print("See also: ^2allspec^7\n");
1520                         return;
1521         }
1522 }
1523
1524 void GameCommand_nospectators(float request)
1525 {
1526         switch(request)
1527         {
1528                 case GC_REQUEST_HELP:
1529                         print("  ^2nospectators^7: Automatically remove spectators from a match\n");
1530                         return;
1531                         
1532                 case GC_REQUEST_COMMAND:
1533                         blockSpectators = 1;
1534                         entity plr;
1535                         FOR_EACH_CLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
1536                         {
1537                                 if(plr.classname == "spectator" || plr.classname == "observer")
1538                                 {
1539                                         plr.spectatortime = time;
1540                                         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"));
1541                                 }
1542                         }
1543                         bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n"));
1544                         return;
1545                         
1546                 default:
1547                 case GC_REQUEST_USAGE:
1548                         print("\nUsage:^3 sv_cmd nospectators\n");
1549                         print("  No arguments required.\n");
1550                         return;
1551         }
1552 }
1553
1554 void GameCommand_onslaught_updatelinks(float request) // UNTESTED // should this be here? Perhaps some mutatorhook call instead....
1555 {
1556         switch(request)
1557         {
1558                 case GC_REQUEST_HELP:
1559                         print("  ^2onslaught_updatelinks^7: Refresh link status for onslaught\n");
1560                         return;
1561                         
1562                 case GC_REQUEST_COMMAND:
1563                         onslaught_updatelinks();
1564                         print("ONS links updated\n");
1565                         return;
1566                         
1567                 default:
1568                 case GC_REQUEST_USAGE:
1569                         print("\nUsage:^3 sv_cmd onslaught_updatelinks\n");
1570                         print("  No arguments required.\n");
1571                         return;
1572         }
1573 }
1574
1575 void GameCommand_playerdemo(float request, float argc) // UNTESTED // TODO: change the if statements for sub arguments to switches
1576 {
1577         entity client;
1578         float i, n, entno;
1579         string s;
1580         
1581         switch(request)
1582         {
1583                 case GC_REQUEST_HELP:
1584                         print("  ^2playerdemo^7: Control the ability to save demos of players\n");
1585                         return;
1586                         
1587                 case GC_REQUEST_COMMAND:
1588                         if(argv(1) == "read")
1589                         {
1590                                 // TODO: Create a general command for looking this up, save a lot of space everywhere in this file
1591                                 entno = stof(argv(2));
1592                                 if((entno < 1) | (entno > maxclients)) {
1593                                         print("Player ", argv(2), " doesn't exist\n");
1594                                         return;
1595                                 }
1596                                 client = edict_num(entno);
1597                                 if(clienttype(client) != CLIENTTYPE_BOT) {
1598                                         print("Player ", client.netname, " is not a bot\n");
1599                                         return;
1600                                 }
1601                                 self = client;
1602                                 playerdemo_open_read(argv(3));
1603                                 return;
1604                         }
1605                         else if(argv(1) == "write")
1606                         {
1607                                 entno = stof(argv(2));
1608                                 if((entno < 1) | (entno > maxclients)) {
1609                                         print("Player ", argv(2), " doesn't exist\n");
1610                                         return;
1611                                 }
1612                                 client = edict_num(entno);
1613                                 self = client;
1614                                 playerdemo_open_write(argv(3));
1615                                 return;
1616                         }
1617                         else if(argv(1) == "auto_read_and_write")
1618                         {
1619                                 s = argv(2);
1620                                 n = stof(argv(3));
1621                                 cvar_set("bot_number", ftos(n));
1622                                 localcmd("wait; wait; wait\n");
1623                                 for(i = 0; i < n; ++i)
1624                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1625                                 localcmd("sv_cmd playerdemo write 1 ", ftos(n+1), "\n");
1626                                 return;
1627                         }
1628                         else if(argv(1) == "auto_read")
1629                         {
1630                                 s = argv(2);
1631                                 n = stof(argv(3));
1632                                 cvar_set("bot_number", ftos(n));
1633                                 localcmd("wait; wait; wait\n");
1634                                 for(i = 0; i < n; ++i)
1635                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1636                                 return;
1637                         }
1638                         return;
1639                         
1640                 default:
1641                         print("Incorrect parameters for ^2radarmap^7\n");
1642                 case GC_REQUEST_USAGE:
1643                         print("\nUsage:^3 sv_cmd \n");
1644                         print("  FIXME: Arguments currently unknown\n");
1645                         return;
1646         }
1647 }
1648
1649 void GameCommand_printstats(float request)
1650 {
1651         switch(request)
1652         {
1653                 case GC_REQUEST_HELP:
1654                         print("  ^2printstats^7: foobar\n");
1655                         return;
1656                         
1657                 case GC_REQUEST_COMMAND:
1658                         DumpStats(FALSE);
1659                         print("stats dumped.\n");
1660                         return;
1661                         
1662                 default:
1663                 case GC_REQUEST_USAGE:
1664                         print("\nUsage:^3 sv_cmd printstats\n");
1665                         print("  No arguments required.\n");
1666                         return;
1667         }
1668 }
1669
1670 void GameCommand_radarmap(float request, float argc)
1671 {
1672         float i;
1673         
1674         switch(request)
1675         {
1676                 case GC_REQUEST_HELP:
1677                         print("  ^2radarmap^7: Generate a radar image of the map\n");
1678                         return;
1679                         
1680                 case GC_REQUEST_COMMAND:
1681                         if(!radarmapper)
1682                         {
1683                                 radarmapper = spawn();
1684                                 radarmapper.classname = "radarmapper";
1685                                 radarmapper.think = RadarMap_Think;
1686                                 radarmapper.nextthink = time;
1687                                 radarmapper.count = 8; // default to the --trace method, as it is faster now
1688                                 radarmapper.ltime = 1;
1689                                 radarmapper.size = '512 512 1';
1690
1691                                 for(i = 1; i < argc; ++i)
1692                                 {
1693                                         switch(argv(i))
1694                                         {
1695                                                 case "--force": { radarmapper.count |= 1; break;        }
1696                                                 case "--loop": { radarmapper.count |= 2; break; }
1697                                                 case "--quit": { radarmapper.count |= 4; break; }
1698                                                 case "--block": { radarmapper.count &~= 24; break; }
1699                                                 case "--trace": { radarmapper.count &~= 24; radarmapper.count |= 8; break; }
1700                                                 case "--sample": { radarmapper.count &~= 24; radarmapper.count |= 16; break; }
1701                                                 case "--lineblock": { radarmapper.count |= 24; break; }
1702                                                 case "--flags": { ++i; radarmapper.count = stof(argv(i)); break; } // for the recursive call
1703                                                 case "--sharpen": { ++i; radarmapper.ltime = stof(argv(i)); break; } // for the recursive call
1704                                                 case "--res": // minor alias
1705                                                 case "--resolution": { ++i; radarmapper.size_x = stof(argv(i)); ++i; radarmapper.size_y = stof(argv(i)); break; }
1706                                                 case "--qual": // minor alias
1707                                                 case "--quality": { ++i; radarmapper.size_z = stof(argv(i)); break; }
1708                                                 
1709                                                 default: 
1710                                                         i = argc; 
1711                                                         remove(radarmapper);
1712                                                         radarmapper = world;
1713                                                         break;
1714                                         }
1715                                 }
1716                                 
1717                                 if(radarmapper) // after doing the arguments, see if we successfully went forward. 
1718                                 {
1719                                         print("Radarmap entity spawned.\n");
1720                                         return; // if so, don't print usage.
1721                                 }
1722                         }
1723                         
1724                 default:
1725                         print("Incorrect parameters for ^2radarmap^7\n");
1726                 case GC_REQUEST_USAGE:
1727                         print("\nUsage:^3 sv_cmd radarmap [--force] [--loop] [--quit] [--block | --trace | --sample | --lineblock] [--sharpen N] [--res W H] [--qual Q]\n");
1728                         print("  The quality factor Q is roughly proportional to the time taken.\n");
1729                         print("  trace supports no quality factor; its result should look like --block with infinite quality factor.\n");
1730                         return;
1731         }
1732 }
1733
1734 void GameCommand_rankings(float request) // this is OLD.... jeez.
1735 {
1736         switch(request)
1737         {
1738                 case GC_REQUEST_HELP:
1739                         print("  ^2rankings^7: Print information about rankings\n");
1740                         return;
1741                         
1742                 case GC_REQUEST_COMMAND:
1743                         strunzone(rankings_reply);
1744                         rankings_reply = strzone(getrankings());
1745                         print(rankings_reply);
1746                         return;
1747                         
1748                 default:
1749                 case GC_REQUEST_USAGE:
1750                         print("\nUsage:^3 sv_cmd rankings\n");
1751                         print("  No arguments required.\n");
1752                         return;
1753         }
1754 }
1755
1756 void GameCommand_records(float request)
1757 {
1758         float i;
1759         
1760         switch(request)
1761         {
1762                 case GC_REQUEST_HELP:
1763                         print("  ^2records^7: List top 10 records for the current map\n");
1764                         return;
1765                         
1766                 case GC_REQUEST_COMMAND:
1767                         for (i = 0; i < 10; ++i)
1768                                 print(records_reply[i]);
1769                         return;
1770                         
1771                 default:
1772                 case GC_REQUEST_USAGE:
1773                         print("\nUsage:^3 sv_cmd records\n");
1774                         print("  No arguments required.\n");
1775                         return;
1776         }
1777 }
1778
1779 void GameCommand_reducematchtime(float request) // todo: Perhaps allows the user to send a specific time to reduce it.
1780 {
1781         switch(request)
1782         {
1783                 case GC_REQUEST_HELP:
1784                         print("  ^2reducematchtime^7: Decrease the timelimit value incrementally\n");
1785                         return;
1786                         
1787                 case GC_REQUEST_COMMAND:
1788                         changematchtime(autocvar_timelimit_decrement*-60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
1789                         return;
1790                         
1791                 default:
1792                 case GC_REQUEST_USAGE:
1793                         print("\nUsage:^3 sv_cmd reducematchtime\n");
1794                         print("  No arguments required.\n");
1795                         print("See also: ^2extendmatchtime^7\n");
1796                         return;
1797         }
1798 }
1799
1800 void GameCommand_stuffto(float request, float argc)
1801 {
1802         // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
1803         // Because of this, it is disabled by default and must be enabled by the server owner when doing compilation. That way,
1804         // we can be certain they understand the risks of it... So to enable, compile server with -DSTUFFTO_ENABLED argument.
1805         
1806         entity client;
1807         float entno;
1808         
1809         #ifdef STUFFTO_ENABLED
1810         switch(request)
1811         {
1812                 case GC_REQUEST_HELP:
1813                         print("  ^2stuffto^7: Send a command to be executed on a client\n");
1814                         return;
1815                         
1816                 case GC_REQUEST_COMMAND:
1817                         if(argc == 3)
1818                         {
1819                                 entno = stof(argv(1));
1820                                 client = world;
1821                                 if(entno <= maxclients)
1822                                         client = edict_num(entno);
1823                                 if(client.flags & FL_CLIENT)
1824                                 {
1825                                         stuffcmd(client, strcat("\n", argv(2), "\n"));
1826                                         print(strcat("Command: \"", argv(2), "\" sent to ", client.netname, " (", argv(1) ,").\n"));
1827                                 }
1828                                 else
1829                                         print(strcat("Client (", argv(1) ,") not found.\n"));
1830                                 
1831                                 return;
1832                         }
1833                         
1834                 default:
1835                         print("Incorrect parameters for ^2stuffto^7\n");
1836                 case GC_REQUEST_USAGE:
1837                         print("\nUsage:^3 sv_cmd stuffto clientnumber command\n");
1838                         print("  FIXME: Arguments currently unknown\n");
1839                         return;
1840         }
1841         #else // give the response for missing command to fake them out ;3
1842         if(request == GC_REQUEST_COMMAND)
1843         {
1844                 print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
1845                 return;
1846         }
1847         #endif
1848 }
1849
1850 void GameCommand_teamstatus(float request)
1851 {
1852         switch(request)
1853         {
1854                 case GC_REQUEST_HELP:
1855                         print("  ^2teamstatus^7: Show information about player and team scores\n");
1856                         return;
1857                         
1858                 case GC_REQUEST_COMMAND:
1859                         Score_NicePrint(world);
1860                         return;
1861                         
1862                 default:
1863                 case GC_REQUEST_USAGE:
1864                         print("\nUsage:^3 sv_cmd teamstatus\n");
1865                         print("  No arguments required.\n");
1866                         return;
1867         }
1868 }
1869
1870 void GameCommand_trace(float request, float argc)
1871 {
1872         entity e;
1873         vector org, delta, start, end, p, q, q0, pos, vv, dv;
1874         float i, f, safe, unsafe, dq, dqf;
1875         
1876         // TODO: Clean up all of these variables and merge the code below to use only a few
1877                                                 
1878         switch(request)
1879         {
1880                 case GC_REQUEST_HELP:
1881                         print("  ^2trace^7: Various debugging tools with tracing\n");
1882                         return;
1883                         
1884                 case GC_REQUEST_COMMAND:
1885                         switch(argv(1))
1886                         {
1887                                 case "debug":
1888                                         print("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.\n");
1889                                         for(;;)
1890                                         {
1891                                                 org = world.mins;
1892                                                 delta = world.maxs - world.mins;
1893
1894                                                 start_x = org_x + random() * delta_x;
1895                                                 start_y = org_y + random() * delta_y;
1896                                                 start_z = org_z + random() * delta_z;
1897
1898                                                 end_x = org_x + random() * delta_x;
1899                                                 end_y = org_y + random() * delta_y;
1900                                                 end_z = org_z + random() * delta_z;
1901
1902                                                 start = stov(vtos(start));
1903                                                 end = stov(vtos(end));
1904
1905                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1906                                                 if(!trace_startsolid)
1907                                                 {
1908                                                         p = trace_endpos;
1909                                                         tracebox(p, PL_MIN, PL_MAX, p, MOVE_NOMONSTERS, world);
1910                                                         if(trace_startsolid || trace_fraction == 1)
1911                                                         {
1912                                                                 rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
1913                                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1914                                                                 tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1915
1916                                                                 if(trace_startsolid)
1917                                                                 {
1918                                                                         // how much do we need to back off?
1919                                                                         safe = 1;
1920                                                                         unsafe = 0;
1921                                                                         for(;;)
1922                                                                         {
1923                                                                                 pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
1924                                                                                 tracebox(pos, PL_MIN, PL_MAX, pos, MOVE_NOMONSTERS, world);
1925                                                                                 if(trace_startsolid)
1926                                                                                 {
1927                                                                                         if((safe + unsafe) * 0.5 == unsafe)
1928                                                                                                 break;
1929                                                                                         unsafe = (safe + unsafe) * 0.5;
1930                                                                                 }
1931                                                                                 else
1932                                                                                 {
1933                                                                                         if((safe + unsafe) * 0.5 == safe)
1934                                                                                                 break;
1935                                                                                         safe = (safe + unsafe) * 0.5;
1936                                                                                 }
1937                                                                         }
1938
1939                                                                         print("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
1940                                                                         print("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
1941
1942                                                                         tracebox(p, PL_MIN + '0.1 0.1 0.1', PL_MAX - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, world);
1943                                                                         if(trace_startsolid)
1944                                                                                 print("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1945                                                                         else
1946                                                                                 print("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1947                                                                         break;
1948                                                                 }
1949
1950                                                                 q0 = p;
1951                                                                 dq = 0;
1952                                                                 dqf = 1;
1953                                                                 for(;;)
1954                                                                 {
1955                                                                         q = p + normalize(end - p) * (dq + dqf);
1956                                                                         if(q == q0)
1957                                                                                 break;
1958                                                                         tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1959                                                                         if(trace_startsolid)
1960                                                                                 error("THIS ONE cannot happen");
1961                                                                         if(trace_fraction > 0)
1962                                                                                 dq += dqf * trace_fraction;
1963                                                                         dqf *= 0.5;
1964                                                                         q0 = q;
1965                                                                 }
1966                                                                 if(dq > 0)
1967                                                                 {
1968                                                                         print("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1969                                                                         print("could go ", ftos(dq), " units further to ", vtos(q), "\n");
1970                                                                         break;
1971                                                                 }
1972                                                         }
1973                                                 }
1974                                         }
1975                                         return;
1976                                         
1977                                 case "debug2":
1978                                         e = nextent(world);
1979                                         tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e);
1980                                         vv = trace_endpos;
1981                                         if(trace_fraction == 1)
1982                                         {
1983                                                 print("not above ground, aborting\n");
1984                                                 return;
1985                                         }
1986                                         f = 0;
1987                                         for(i = 0; i < 100000; ++i)
1988                                         {
1989                                                 dv = randomvec();
1990                                                 if(dv_z > 0)
1991                                                         dv = -1 * dv;
1992                                                 tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e);
1993                                                 if(trace_startsolid)
1994                                                         print("bug 1\n");
1995                                                 if(trace_fraction == 1)
1996                                                 if(dv_z < f)
1997                                                 {
1998                                                         print("bug 2: ", ftos(dv_x), " ", ftos(dv_y), " ", ftos(dv_z));
1999                                                         print(" (", ftos(asin(dv_z / vlen(dv)) * 180 / M_PI), " degrees)\n");
2000                                                         f = dv_z;
2001                                                 }
2002                                         }
2003                                         print("highest possible dist: ", ftos(f), "\n");
2004                                         return;
2005                                 
2006                                 case "walk":
2007                                         if(argc == 3)
2008                                         {
2009                                                 e = nextent(world);
2010                                                 if(tracewalk(e, stov(argv(1)), e.mins, e.maxs, stov(argv(2)), MOVE_NORMAL))
2011                                                         print("can walk\n");
2012                                                 else
2013                                                         print("cannot walk\n");
2014                                                 return;
2015                                         }
2016                                                 
2017                                 case "showline":
2018                                         if(argc == 3)
2019                                         {
2020                                                 vv = stov(argv(1));
2021                                                 dv = stov(argv(2));
2022                                                 traceline(vv, dv, MOVE_NORMAL, world);
2023                                                 trailparticles(world, particleeffectnum("TR_NEXUIZPLASMA"), vv, trace_endpos);
2024                                                 trailparticles(world, particleeffectnum("TR_CRYLINKPLASMA"), trace_endpos, dv);
2025                                                 return;
2026                                         }
2027                                         
2028                                 // no default case, just go straight to "invalid arguments"
2029                         }
2030                         
2031                 default:
2032                 case GC_REQUEST_USAGE:
2033                         print("\nUsage:^3 sv_cmd trace command [arguments]\n");
2034                         print("  FIXME: Arguments currently unknown\n");
2035                         return;
2036         }
2037 }
2038
2039
2040 // =========================================
2041 //  Main Function Called By Engine (sv_cmd)
2042 // =========================================
2043
2044 void GameCommand(string command)
2045 {
2046         // ===== TODO list =====
2047         // Finish adding the rest of the commands
2048         
2049         // find all FIXME's and TODO'S and UNTESTED'S and finish them :P
2050
2051         float search_request_type;
2052         float argc = tokenize_console(command);
2053
2054         if(argv(0) == "help") 
2055         {
2056                 if(argc == 1) 
2057                 {
2058                         print("\nUsage:^3 sv_cmd COMMAND...^7, where possible commands are:\n");
2059                         GameCommand_adminmsg(GC_REQUEST_HELP, 0);
2060                         GameCommand_allready(GC_REQUEST_HELP);
2061                         GameCommand_allspec(GC_REQUEST_HELP);
2062                         GameCommand_anticheat(GC_REQUEST_HELP, 0);
2063                         GameCommand_bbox(GC_REQUEST_HELP);
2064                         GameCommand_bot_cmd(GC_REQUEST_HELP, 0);
2065                         GameCommand_cointoss(GC_REQUEST_HELP);
2066                         GameCommand_cvar_changes(GC_REQUEST_HELP);
2067                         GameCommand_cvar_purechanges(GC_REQUEST_HELP);
2068                         GameCommand_database(GC_REQUEST_HELP, 0);
2069                         GameCommand_defer_clear(GC_REQUEST_HELP, 0);
2070                         GameCommand_defer_clear_all(GC_REQUEST_HELP);
2071                         GameCommand_delrec(GC_REQUEST_HELP, 0);
2072                         GameCommand_effectindexdump(GC_REQUEST_HELP);
2073                         GameCommand_extendmatchtime(GC_REQUEST_HELP);
2074                         GameCommand_find(GC_REQUEST_HELP, 0);
2075                         GameCommand_gametype(GC_REQUEST_HELP, 0);
2076                         GameCommand_gettaginfo(GC_REQUEST_HELP, 0);
2077                         GameCommand_gotomap(GC_REQUEST_HELP, 0);
2078                         GameCommand_ladder(GC_REQUEST_HELP);
2079                         GameCommand_lockteams(GC_REQUEST_HELP);
2080                         GameCommand_make_mapinfo(GC_REQUEST_HELP);
2081                         GameCommand_modelbug(GC_REQUEST_HELP);
2082                         GameCommand_moveplayer(GC_REQUEST_HELP, 0);
2083                         GameCommand_nospectators(GC_REQUEST_HELP);
2084                         GameCommand_onslaught_updatelinks(GC_REQUEST_HELP);
2085                         GameCommand_playerdemo(GC_REQUEST_HELP, 0);
2086                         GameCommand_printstats(GC_REQUEST_HELP);
2087                         GameCommand_radarmap(GC_REQUEST_HELP, 0);
2088                         GameCommand_rankings(GC_REQUEST_HELP);
2089                         GameCommand_records(GC_REQUEST_HELP);
2090                         GameCommand_reducematchtime(GC_REQUEST_HELP);
2091                         GameCommand_stuffto(GC_REQUEST_HELP, 0);
2092                         GameCommand_teamstatus(GC_REQUEST_HELP);
2093                         GameCommand_trace(GC_REQUEST_HELP, 0);
2094                         GameCommand_Vote("help", world);
2095                         GameCommand_Ban("help");
2096                         GameCommand_Generic("help");
2097                         print("For help about specific commands, type sv_cmd help COMMAND\n");
2098                         return;
2099                 } 
2100                 else
2101                         search_request_type = GC_REQUEST_USAGE; // Instead of trying to call a command, we're going to see detailed information about it
2102         } 
2103         else if(GameCommand_Vote(command, world)) 
2104         {
2105                 return; // handled by server/vote.qc 
2106         }
2107         else if(GameCommand_Ban(command)) 
2108         {
2109                 return; // handled by server/ipban.qc
2110         }
2111         else if(GameCommand_Generic(command)) 
2112         {
2113                 return; // handled by common/gamecommand.qc
2114         }
2115         else
2116                 search_request_type = GC_REQUEST_COMMAND; // continue as usual and scan for normal commands
2117                 
2118         switch( ((argv(0) == "help") ? argv(1) : argv(0)) ) // if first argument is help, then search for the second argument. Else, search for first. 
2119         {
2120                 // Do not hard code aliases for these, instead create them in defaultXonotic.cfg
2121                 // also: keep in alphabetical order, please ;)
2122                 
2123                 case "adminmsg": GameCommand_adminmsg(search_request_type, argc); break;
2124                 case "allready": GameCommand_allready(search_request_type); break;
2125                 case "allspec": GameCommand_allspec(search_request_type); break;
2126                 case "anticheat": GameCommand_anticheat(search_request_type, argc); break;
2127                 case "bbox": GameCommand_bbox(search_request_type); break;
2128                 case "bot_cmd": GameCommand_bot_cmd(search_request_type, argc); break;
2129                 case "cointoss": GameCommand_cointoss(search_request_type); break; 
2130                 case "cvar_changes": GameCommand_cvar_changes(search_request_type); break; 
2131                 case "cvar_purechanges": GameCommand_cvar_purechanges(search_request_type); break; 
2132                 case "database": GameCommand_database(search_request_type, argc); break;
2133                 case "defer_clear": GameCommand_defer_clear(search_request_type, argc); break;
2134                 case "defer_clear_all": GameCommand_defer_clear_all(search_request_type); break;
2135                 case "delrec": GameCommand_delrec(search_request_type, argc); break;
2136                 case "effectindexdump": GameCommand_effectindexdump(search_request_type); break;
2137                 case "extendmatchtime": GameCommand_extendmatchtime(search_request_type); break;
2138                 case "find": GameCommand_find(search_request_type, argc); break; 
2139                 case "gametype": GameCommand_gametype(search_request_type, argc); break;
2140                 case "gettaginfo": GameCommand_gettaginfo(search_request_type, argc); break;
2141                 case "gotomap": GameCommand_gotomap(search_request_type, argc); break;
2142                 case "ladder": GameCommand_ladder(search_request_type); break;
2143                 case "lockteams": GameCommand_lockteams(search_request_type); break;
2144                 case "make_mapinfo": GameCommand_make_mapinfo(search_request_type); break;
2145                 case "modelbug": GameCommand_modelbug(search_request_type); break;
2146                 case "moveplayer": GameCommand_moveplayer(search_request_type, argc); break;
2147                 case "nospectators": GameCommand_nospectators(search_request_type); break;
2148                 case "onslaught_updatelinks": GameCommand_onslaught_updatelinks(search_request_type); break;
2149                 case "playerdemo": GameCommand_playerdemo(search_request_type, argc); break;
2150                 case "printstats": GameCommand_printstats(search_request_type); break;
2151                 case "radarmap": GameCommand_radarmap(search_request_type, argc); break;
2152                 case "rankings": GameCommand_rankings(search_request_type); break;
2153                 case "records": GameCommand_records(search_request_type); break;
2154                 case "reducematchtime": GameCommand_reducematchtime(search_request_type); break;
2155                 case "stuffto": GameCommand_stuffto(search_request_type, argc); break;
2156                 case "teamstatus": GameCommand_teamstatus(search_request_type); break;
2157                 case "trace": GameCommand_trace(search_request_type, argc); break;
2158                 
2159                 default:
2160                         print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
2161         }
2162 }