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