]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - host_cmd.c
now begins loading plaque at all the appropriate places, which also means an end...
[xonotic/darkplaces.git] / host_cmd.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 int current_skill;
24 cvar_t sv_cheats = {0, "sv_cheats", "0"};
25 qboolean allowcheats = false;
26
27 mfunction_t *ED_FindFunction (char *name);
28
29 /*
30 ==================
31 Host_Quit_f
32 ==================
33 */
34
35 extern qboolean host_shuttingdown;
36 void Host_Quit_f (void)
37 {
38         host_shuttingdown = true;
39         CL_Disconnect ();
40         Host_ShutdownServer(false);
41
42         Sys_Quit ();
43 }
44
45
46 /*
47 ==================
48 Host_Status_f
49 ==================
50 */
51 void Host_Status_f (void)
52 {
53         const char *protocolname;
54         client_t *client;
55         int seconds, minutes, hours = 0, j, players;
56         void (*print) (const char *fmt, ...);
57
58         if (cmd_source == src_command)
59         {
60                 if (!sv.active)
61                 {
62                         Cmd_ForwardToServer ();
63                         return;
64                 }
65                 print = Con_Printf;
66         }
67         else
68                 print = SV_ClientPrintf;
69
70         for (players = 0, j = 0;j < svs.maxclients;j++)
71                 if (svs.clients[j].active)
72                         players++;
73         print ("host:    %s\n", Cvar_VariableString ("hostname"));
74         print ("version: %s build %s\n", gamename, buildstring);
75         switch(sv.protocol)
76         {
77                 case PROTOCOL_QUAKE: protocolname = sv.netquakecompatible ? "QUAKE" : "QUAKEDP";break;
78                 case PROTOCOL_DARKPLACES1: protocolname = "PROTOCOL_DARKPLACES1";break;
79                 case PROTOCOL_DARKPLACES2: protocolname = "PROTOCOL_DARKPLACES2";break;
80                 case PROTOCOL_DARKPLACES3: protocolname = "PROTOCOL_DARKPLACES3";break;
81                 case PROTOCOL_DARKPLACES4: protocolname = "PROTOCOL_DARKPLACES4";break;
82                 case PROTOCOL_DARKPLACES5: protocolname = "PROTOCOL_DARKPLACES5";break;
83                 default: protocolname = "PROTOCOL_UNKNOWN";break;
84         }
85         print ("protocol: %i (%s)\n", sv.protocol, protocolname);
86         print ("map:     %s\n", sv.name);
87         print ("players: %i active (%i max)\n\n", players, svs.maxclients);
88         for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
89         {
90                 if (!client->active)
91                         continue;
92                 seconds = (int)(realtime - client->netconnection->connecttime);
93                 minutes = seconds / 60;
94                 if (minutes)
95                 {
96                         seconds -= (minutes * 60);
97                         hours = minutes / 60;
98                         if (hours)
99                                 minutes -= (hours * 60);
100                 }
101                 else
102                         hours = 0;
103                 print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v->frags, hours, minutes, seconds);
104                 print ("   %s\n", client->netconnection->address);
105         }
106 }
107
108
109 /*
110 ==================
111 Host_God_f
112
113 Sets client to godmode
114 ==================
115 */
116 void Host_God_f (void)
117 {
118         if (cmd_source == src_command)
119         {
120                 Cmd_ForwardToServer ();
121                 return;
122         }
123
124         if (!sv_player)
125                 return;
126
127         if (!allowcheats)
128         {
129                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
130                 return;
131         }
132
133         sv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE;
134         if (!((int)sv_player->v->flags & FL_GODMODE) )
135                 SV_ClientPrint("godmode OFF\n");
136         else
137                 SV_ClientPrint("godmode ON\n");
138 }
139
140 void Host_Notarget_f (void)
141 {
142         if (cmd_source == src_command)
143         {
144                 Cmd_ForwardToServer ();
145                 return;
146         }
147
148         if (!sv_player)
149                 return;
150
151         if (!allowcheats)
152         {
153                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
154                 return;
155         }
156
157         sv_player->v->flags = (int)sv_player->v->flags ^ FL_NOTARGET;
158         if (!((int)sv_player->v->flags & FL_NOTARGET) )
159                 SV_ClientPrint("notarget OFF\n");
160         else
161                 SV_ClientPrint("notarget ON\n");
162 }
163
164 qboolean noclip_anglehack;
165
166 void Host_Noclip_f (void)
167 {
168         if (cmd_source == src_command)
169         {
170                 Cmd_ForwardToServer ();
171                 return;
172         }
173
174         if (!sv_player)
175                 return;
176
177         if (!allowcheats)
178         {
179                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
180                 return;
181         }
182
183         if (sv_player->v->movetype != MOVETYPE_NOCLIP)
184         {
185                 noclip_anglehack = true;
186                 sv_player->v->movetype = MOVETYPE_NOCLIP;
187                 SV_ClientPrint("noclip ON\n");
188         }
189         else
190         {
191                 noclip_anglehack = false;
192                 sv_player->v->movetype = MOVETYPE_WALK;
193                 SV_ClientPrint("noclip OFF\n");
194         }
195 }
196
197 /*
198 ==================
199 Host_Fly_f
200
201 Sets client to flymode
202 ==================
203 */
204 void Host_Fly_f (void)
205 {
206         if (cmd_source == src_command)
207         {
208                 Cmd_ForwardToServer ();
209                 return;
210         }
211
212         if (!sv_player)
213                 return;
214
215         if (!allowcheats)
216         {
217                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
218                 return;
219         }
220
221         if (sv_player->v->movetype != MOVETYPE_FLY)
222         {
223                 sv_player->v->movetype = MOVETYPE_FLY;
224                 SV_ClientPrint("flymode ON\n");
225         }
226         else
227         {
228                 sv_player->v->movetype = MOVETYPE_WALK;
229                 SV_ClientPrint("flymode OFF\n");
230         }
231 }
232
233
234 /*
235 ==================
236 Host_Ping_f
237
238 ==================
239 */
240 void Host_Ping_f (void)
241 {
242         int             i, j;
243         float   total;
244         client_t        *client;
245
246         if (cmd_source == src_command)
247         {
248                 Cmd_ForwardToServer ();
249                 return;
250         }
251
252         SV_ClientPrint("Client ping times:\n");
253         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
254         {
255                 if (!client->active)
256                         continue;
257                 total = 0;
258                 for (j=0 ; j<NUM_PING_TIMES ; j++)
259                         total+=client->ping_times[j];
260                 total /= NUM_PING_TIMES;
261                 SV_ClientPrintf("%4i %s\n", (int)(total*1000), client->name);
262         }
263 }
264
265 /*
266 ===============================================================================
267
268 SERVER TRANSITIONS
269
270 ===============================================================================
271 */
272
273 /*
274 ======================
275 Host_Map_f
276
277 handle a
278 map <servername>
279 command from the console.  Active clients are kicked off.
280 ======================
281 */
282 void Host_Map_f (void)
283 {
284         char level[MAX_QPATH];
285
286         if (Cmd_Argc() != 2)
287         {
288                 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
289                 return;
290         }
291
292         if (cmd_source != src_command)
293                 return;
294
295         SCR_BeginLoadingPlaque ();
296         cls.demonum = -1;               // stop demo loop in case this fails
297
298         CL_Disconnect ();
299         Host_ShutdownServer(false);
300
301         // remove console or menu
302         key_dest = key_game;
303         key_consoleactive = 0;
304
305         svs.serverflags = 0;                    // haven't completed an episode yet
306         allowcheats = sv_cheats.integer != 0;
307         strcpy(level, Cmd_Argv(1));
308         SV_SpawnServer(level);
309         if (sv.active && cls.state == ca_disconnected)
310                 CL_EstablishConnection("local:1");
311 }
312
313 /*
314 ==================
315 Host_Changelevel_f
316
317 Goes to a new map, taking all clients along
318 ==================
319 */
320 void Host_Changelevel_f (void)
321 {
322         char level[MAX_QPATH];
323         
324         if (Cmd_Argc() != 2)
325         {
326                 Con_Print("changelevel <levelname> : continue game on a new level\n");
327                 return;
328         }
329         if (!sv.active || cls.demoplayback)
330         {
331                 Con_Print("Only the server may changelevel\n");
332                 return;
333         }
334         if (cmd_source != src_command)
335                 return;
336
337         // remove console or menu
338         key_dest = key_game;
339         key_consoleactive = 0;
340
341         SV_SaveSpawnparms ();
342         SCR_BeginLoadingPlaque();
343         allowcheats = sv_cheats.integer != 0;
344         strcpy(level, Cmd_Argv(1));
345         SV_SpawnServer(level);
346         if (sv.active && cls.state == ca_disconnected)
347                 CL_EstablishConnection("local:1");
348 }
349
350 /*
351 ==================
352 Host_Restart_f
353
354 Restarts the current server for a dead player
355 ==================
356 */
357 void Host_Restart_f (void)
358 {
359         char mapname[MAX_QPATH];
360         
361         if (Cmd_Argc() != 1)
362         {
363                 Con_Print("restart : restart current level\n");
364                 return;
365         }
366         if (!sv.active || cls.demoplayback)
367         {
368                 Con_Print("Only the server may restart\n");
369                 return;
370         }
371         if (cmd_source != src_command)
372                 return;
373
374         // remove console or menu
375         key_dest = key_game;
376         key_consoleactive = 0;
377
378         SCR_BeginLoadingPlaque();
379         allowcheats = sv_cheats.integer != 0;
380         strcpy(mapname, sv.name);
381         SV_SpawnServer(mapname);
382         if (sv.active && cls.state == ca_disconnected)
383                 CL_EstablishConnection("local:1");
384 }
385
386 /*
387 ==================
388 Host_Reconnect_f
389
390 This command causes the client to wait for the signon messages again.
391 This is sent just before a server changes levels
392 ==================
393 */
394 void Host_Reconnect_f (void)
395 {
396         if (Cmd_Argc() != 1)
397         {
398                 Con_Print("reconnect : wait for signon messages again\n");
399                 return;
400         }
401         if (cmd_source == src_command)
402         {
403                 Con_Print("reconnect not valid from console\n");
404                 return;
405         }
406         if (!cls.signon)
407         {
408                 Con_Print("reconnect: no signon, ignoring reconnect\n");
409                 return;
410         }
411         SCR_BeginLoadingPlaque();
412         cls.signon = 0;         // need new connection messages
413 }
414
415 /*
416 =====================
417 Host_Connect_f
418
419 User command to connect to server
420 =====================
421 */
422 void Host_Connect_f (void)
423 {
424         if (Cmd_Argc() != 2)
425         {
426                 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
427                 return;
428         }
429         CL_EstablishConnection(Cmd_Argv(1));
430 }
431
432
433 /*
434 ===============================================================================
435
436 LOAD / SAVE GAME
437
438 ===============================================================================
439 */
440
441 #define SAVEGAME_VERSION        5
442
443 /*
444 ===============
445 Host_SavegameComment
446
447 Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
448 ===============
449 */
450 void Host_SavegameComment (char *text)
451 {
452         int             i;
453         char    kills[20];
454
455         for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
456                 text[i] = ' ';
457         memcpy (text, cl.levelname, strlen(cl.levelname));
458         sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
459         memcpy (text+22, kills, strlen(kills));
460 // convert space to _ to make stdio happy
461         for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
462                 if (text[i] == ' ')
463                         text[i] = '_';
464         text[SAVEGAME_COMMENT_LENGTH] = '\0';
465 }
466
467
468 /*
469 ===============
470 Host_Savegame_f
471 ===============
472 */
473 void Host_Savegame_f (void)
474 {
475         char    name[256];
476         qfile_t *f;
477         int             i;
478         char    comment[SAVEGAME_COMMENT_LENGTH+1];
479
480         if (cmd_source != src_command)
481                 return;
482
483         if (cls.state != ca_connected || !sv.active)
484         {
485                 Con_Print("Not playing a local game.\n");
486                 return;
487         }
488
489         if (cl.intermission)
490         {
491                 Con_Print("Can't save in intermission.\n");
492                 return;
493         }
494
495         for (i = 0;i < svs.maxclients;i++)
496         {
497                 if (svs.clients[i].active)
498                 {
499                         if (i > 0)
500                         {
501                                 Con_Print("Can't save multiplayer games.\n");
502                                 return;
503                         }
504                         if (svs.clients[i].edict->v->deadflag)
505                         {
506                                 Con_Print("Can't savegame with a dead player\n");
507                                 return;
508                         }
509                 }
510         }
511
512         if (Cmd_Argc() != 2)
513         {
514                 Con_Print("save <savename> : save a game\n");
515                 return;
516         }
517
518         if (strstr(Cmd_Argv(1), ".."))
519         {
520                 Con_Print("Relative pathnames are not allowed.\n");
521                 return;
522         }
523
524         strlcpy (name, Cmd_Argv(1), sizeof (name));
525         FS_DefaultExtension (name, ".sav", sizeof (name));
526
527         Con_Printf("Saving game to %s...\n", name);
528         f = FS_Open (name, "w", false);
529         if (!f)
530         {
531                 Con_Print("ERROR: couldn't open.\n");
532                 return;
533         }
534
535         FS_Printf(f, "%i\n", SAVEGAME_VERSION);
536         Host_SavegameComment (comment);
537         FS_Printf(f, "%s\n", comment);
538         for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
539                 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
540         FS_Printf(f, "%d\n", current_skill);
541         FS_Printf(f, "%s\n", sv.name);
542         FS_Printf(f, "%f\n",sv.time);
543
544 // write the light styles
545
546         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
547         {
548                 if (sv.lightstyles[i])
549                         FS_Printf(f, "%s\n", sv.lightstyles[i]);
550                 else
551                         FS_Print(f,"m\n");
552         }
553
554
555         ED_WriteGlobals (f);
556         for (i=0 ; i<sv.num_edicts ; i++)
557         {
558                 ED_Write (f, EDICT_NUM(i));
559                 FS_Flush (f);
560         }
561         FS_Close (f);
562         Con_Print("done.\n");
563 }
564
565
566 extern mempool_t *edictstring_mempool;
567
568 /*
569 ===============
570 Host_Loadgame_f
571 ===============
572 */
573 void Host_Loadgame_f (void)
574 {
575         qfile_t *f;
576         char filename[MAX_QPATH];
577         char mapname[MAX_QPATH];
578         float time, tfloat;
579         char buf[32768];
580         const char *start;
581         char *str;
582         int i, r;
583         edict_t *ent;
584         int entnum;
585         int version;
586         float spawn_parms[NUM_SPAWN_PARMS];
587
588         if (cmd_source != src_command)
589                 return;
590
591         if (Cmd_Argc() != 2)
592         {
593                 Con_Print("load <savename> : load a game\n");
594                 return;
595         }
596
597         strcpy (filename, Cmd_Argv(1));
598         FS_DefaultExtension (filename, ".sav", sizeof (filename));
599
600         Con_Printf("Loading game from %s...\n", filename);
601
602         cls.demonum = -1;               // stop demo loop in case this fails
603
604         f = FS_Open (filename, "r", false);
605         if (!f)
606         {
607                 Con_Print("ERROR: couldn't open.\n");
608                 return;
609         }
610
611         str = FS_Getline (f);
612         sscanf (str, "%i\n", &version);
613         if (version != SAVEGAME_VERSION)
614         {
615                 FS_Close (f);
616                 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
617                 return;
618         }
619
620         SCR_BeginLoadingPlaque ();
621
622         str = FS_Getline (f);
623         for (i = 0;i < NUM_SPAWN_PARMS;i++)
624         {
625                 str = FS_Getline (f);
626                 sscanf (str, "%f\n", &spawn_parms[i]);
627         }
628 // this silliness is so we can load 1.06 save files, which have float skill values
629         str = FS_Getline (f);
630         sscanf (str, "%f\n", &tfloat);
631         current_skill = (int)(tfloat + 0.1);
632         Cvar_SetValue ("skill", (float)current_skill);
633
634         strcpy (mapname, FS_Getline (f));
635
636         str = FS_Getline (f);
637         sscanf (str, "%f\n",&time);
638
639         allowcheats = sv_cheats.integer != 0;
640         SV_SpawnServer (mapname);
641         if (!sv.active)
642         {
643                 Con_Print("Couldn't load map\n");
644                 return;
645         }
646         sv.paused = true;               // pause until all clients connect
647         sv.loadgame = true;
648
649 // load the light styles
650
651         for (i = 0;i < MAX_LIGHTSTYLES;i++)
652         {
653                 str = FS_Getline (f);
654                 sv.lightstyles[i] = Mem_Alloc(edictstring_mempool, strlen(str)+1);
655                 strcpy (sv.lightstyles[i], str);
656         }
657
658 // load the edicts out of the savegame file
659         // -1 is the globals
660         entnum = -1;
661         while (!FS_Eof (f))
662         {
663                 for (i = 0;i < (int)sizeof(buf) - 1;i++)
664                 {
665                         r = FS_Getc (f);
666                         if (r == EOF || !r)
667                                 break;
668                         buf[i] = r;
669                         if (r == '}')
670                         {
671                                 i++;
672                                 break;
673                         }
674                 }
675                 if (i == sizeof(buf)-1)
676                         Host_Error ("Loadgame buffer overflow");
677                 buf[i] = 0;
678                 start = buf;
679                 if (!COM_ParseToken(&start, false))
680                 {
681                         // end of file
682                         break;
683                 }
684                 if (strcmp(com_token,"{"))
685                         Host_Error ("First token isn't a brace");
686
687                 if (entnum == -1)
688                 {
689                         // parse the global vars
690                         ED_ParseGlobals (start);
691                 }
692                 else
693                 {
694                         // parse an edict
695                         if (entnum >= MAX_EDICTS)
696                                 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)\n", MAX_EDICTS);
697                         while (entnum >= sv.max_edicts)
698                                 SV_IncreaseEdicts();
699                         ent = EDICT_NUM(entnum);
700                         memset (ent->v, 0, progs->entityfields * 4);
701                         ent->e->free = false;
702                         ED_ParseEdict (start, ent);
703
704                         // link it into the bsp tree
705                         if (!ent->e->free)
706                                 SV_LinkEdict (ent, false);
707                 }
708
709                 entnum++;
710         }
711
712         sv.num_edicts = entnum;
713         sv.time = time;
714
715         FS_Close (f);
716
717         for (i = 0;i < NUM_SPAWN_PARMS;i++)
718                 svs.clients[0].spawn_parms[i] = spawn_parms[i];
719
720         // make sure we're connected to loopback
721         if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP))
722                 CL_EstablishConnection("local:1");
723 }
724
725 //============================================================================
726
727 /*
728 ======================
729 Host_Name_f
730 ======================
731 */
732 cvar_t cl_name = {CVAR_SAVE, "_cl_name", "player"};
733 void Host_Name_f (void)
734 {
735         int i, j;
736         char newName[sizeof(host_client->name)];
737
738         if (Cmd_Argc () == 1)
739         {
740                 Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
741                 return;
742         }
743
744         if (Cmd_Argc () == 2)
745                 strlcpy (newName, Cmd_Argv(1), sizeof (newName));
746         else
747                 strlcpy (newName, Cmd_Args(), sizeof (newName));
748
749         for (i = 0, j = 0;newName[i];i++)
750                 if (newName[i] != '\r' && newName[i] != '\n')
751                         newName[j++] = newName[i];
752         newName[j] = 0;
753
754         if (cmd_source == src_command)
755         {
756                 Cvar_Set ("_cl_name", newName);
757                 if (cls.state == ca_connected)
758                         Cmd_ForwardToServer ();
759                 return;
760         }
761
762         if (sv.time < host_client->nametime)
763         {
764                 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
765                 return;
766         }
767         
768         host_client->nametime = sv.time + 5;
769
770         if (strcmp(host_client->name, newName) && host_client->name[0] && strcmp(host_client->name, "unconnected"))
771                 SV_BroadcastPrintf("%s changed name to %s\n", host_client->name, newName);
772         strcpy(host_client->name, newName);
773         strcpy(host_client->old_name, newName);
774         if (sv_player)
775                 sv_player->v->netname = PR_SetString(host_client->name);
776         //Con_Printf("Host_Name_f: host_client->edict->netname = %s, sv_player->netname = %s, host_client->name = %s\n", PR_GetString(host_client->edict->v->netname), PR_GetString(sv_player->v->netname), host_client->name);
777
778 // send notification to all clients
779
780         MSG_WriteByte(&sv.reliable_datagram, svc_updatename);
781         MSG_WriteByte(&sv.reliable_datagram, host_client->number);
782         MSG_WriteString(&sv.reliable_datagram, host_client->name);
783 }
784
785
786 void Host_Version_f (void)
787 {
788         Con_Printf("Version: %s build %s\n", gamename, buildstring);
789 }
790
791 void Host_Say(qboolean teamonly)
792 {
793         client_t *save;
794         int j;
795         const char *p1, *p2;
796         // LordHavoc: 256 char say messages
797         unsigned char text[256];
798         qboolean fromServer = false;
799
800         if (cmd_source == src_command)
801         {
802                 if (cls.state == ca_dedicated)
803                 {
804                         fromServer = true;
805                         teamonly = false;
806                 }
807                 else
808                 {
809                         Cmd_ForwardToServer ();
810                         return;
811                 }
812         }
813
814         if (Cmd_Argc () < 2)
815                 return;
816
817         save = host_client;
818
819 // turn on color set 1
820         if (!fromServer)
821                 sprintf (text, "%c%s: ", 1, host_client->name);
822         else
823                 sprintf (text, "%c<%s> ", 1, hostname.string);
824
825         p1 = Cmd_Args();
826         p2 = p1 + strlen(p1);
827         // remove trailing newlines
828         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
829                 p2--;
830         // remove quotes if present
831         if (*p1 == '"')
832         {
833                 p1++;
834                 if (p2[-1] == '"')
835                         p2--;
836                 else if (fromServer)
837                         Con_Print("Host_Say: missing end quote\n");
838                 else
839                         SV_ClientPrint("Host_Say: missing end quote\n");
840         }
841         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
842                 p2--;
843         for (j = strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
844                 text[j++] = *p1++;
845         text[j++] = '\n';
846         text[j++] = 0;
847
848         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
849                 if (host_client->spawned && (!teamplay.integer || host_client->edict->v->team == save->edict->v->team))
850                         SV_ClientPrint(text);
851         host_client = save;
852
853         Sys_Print(&text[1]);
854 }
855
856
857 void Host_Say_f(void)
858 {
859         Host_Say(false);
860 }
861
862
863 void Host_Say_Team_f(void)
864 {
865         Host_Say(true);
866 }
867
868
869 void Host_Tell_f(void)
870 {
871         client_t *save;
872         int j;
873         const char *p1, *p2;
874         char text[1024]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
875         qboolean fromServer = false;
876
877         if (cmd_source == src_command)
878         {
879                 if (cls.state == ca_dedicated)
880                         fromServer = true;
881                 else
882                 {
883                         Cmd_ForwardToServer ();
884                         return;
885                 }
886         }
887
888         if (Cmd_Argc () < 3)
889                 return;
890
891         if (!fromServer)
892                 sprintf (text, "%s: ", host_client->name);
893         else
894                 sprintf (text, "<%s> ", hostname.string);
895
896         p1 = Cmd_Args();
897         p2 = p1 + strlen(p1);
898         // remove the target name
899         while (p1 < p2 && *p1 != ' ')
900                 p1++;
901         while (p1 < p2 && *p1 == ' ')
902                 p1++;
903         // remove trailing newlines
904         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
905                 p2--;
906         // remove quotes if present
907         if (*p1 == '"')
908         {
909                 p1++;
910                 if (p2[-1] == '"')
911                         p2--;
912                 else if (fromServer)
913                         Con_Print("Host_Tell: missing end quote\n");
914                 else
915                         SV_ClientPrint("Host_Tell: missing end quote\n");
916         }
917         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
918                 p2--;
919         for (j = strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
920                 text[j++] = *p1++;
921         text[j++] = '\n';
922         text[j++] = 0;
923
924         save = host_client;
925         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
926                 if (host_client->spawned && !strcasecmp(host_client->name, Cmd_Argv(1)))
927                         SV_ClientPrint(text);
928         host_client = save;
929 }
930
931
932 /*
933 ==================
934 Host_Color_f
935 ==================
936 */
937 cvar_t cl_color = {CVAR_SAVE, "_cl_color", "0"};
938 void Host_Color_f(void)
939 {
940         int             top, bottom;
941         int             playercolor;
942         mfunction_t *f;
943         func_t  SV_ChangeTeam;
944
945         if (Cmd_Argc() == 1)
946         {
947                 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
948                 Con_Print("color <0-15> [0-15]\n");
949                 return;
950         }
951
952         if (Cmd_Argc() == 2)
953                 top = bottom = atoi(Cmd_Argv(1));
954         else
955         {
956                 top = atoi(Cmd_Argv(1));
957                 bottom = atoi(Cmd_Argv(2));
958         }
959
960         top &= 15;
961         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
962         if (top > 15)
963                 top = 15;
964         bottom &= 15;
965         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
966         if (bottom > 15)
967                 bottom = 15;
968
969         playercolor = top*16 + bottom;
970
971         if (cmd_source == src_command)
972         {
973                 Cvar_SetValue ("_cl_color", playercolor);
974                 if (cls.state == ca_connected)
975                         Cmd_ForwardToServer ();
976                 return;
977         }
978
979         if (sv_player && (f = ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - pr_functions)))
980         {
981                 Con_DPrint("Calling SV_ChangeTeam\n");
982                 pr_global_struct->time = sv.time;
983                 pr_globals[OFS_PARM0] = playercolor;
984                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
985                 PR_ExecuteProgram (SV_ChangeTeam, "");
986         }
987         else
988         {
989                 eval_t *val;
990                 if (sv_player)
991                 {
992                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_clientcolors)))
993                                 val->_float = playercolor;
994                         sv_player->v->team = bottom + 1;
995                 }
996                 host_client->colors = playercolor;
997                 host_client->old_colors = playercolor;
998
999                 // send notification to all clients
1000                 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1001                 MSG_WriteByte (&sv.reliable_datagram, host_client->number);
1002                 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1003         }
1004 }
1005
1006 cvar_t cl_rate = {CVAR_SAVE, "_cl_rate", "10000"};
1007 void Host_Rate_f(void)
1008 {
1009         int rate;
1010
1011         if (Cmd_Argc() != 2)
1012         {
1013                 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1014                 Con_Print("rate <500-25000>\n");
1015                 return;
1016         }
1017
1018         rate = atoi(Cmd_Argv(1));
1019
1020         if (cmd_source == src_command)
1021         {
1022                 Cvar_SetValue ("_cl_rate", bound(NET_MINRATE, rate, NET_MAXRATE));
1023                 if (cls.state == ca_connected)
1024                         Cmd_ForwardToServer ();
1025                 return;
1026         }
1027
1028         host_client->netconnection->rate = rate;
1029 }
1030
1031 /*
1032 ==================
1033 Host_Kill_f
1034 ==================
1035 */
1036 void Host_Kill_f (void)
1037 {
1038         if (cmd_source == src_command)
1039         {
1040                 Cmd_ForwardToServer ();
1041                 return;
1042         }
1043
1044         if (!sv_player || sv_player->v->health <= 0)
1045         {
1046                 SV_ClientPrint("Can't suicide -- already dead!\n");
1047                 return;
1048         }
1049
1050         pr_global_struct->time = sv.time;
1051         pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
1052         PR_ExecuteProgram (pr_global_struct->ClientKill, "QC function ClientKill is missing");
1053 }
1054
1055
1056 /*
1057 ==================
1058 Host_Pause_f
1059 ==================
1060 */
1061 void Host_Pause_f (void)
1062 {
1063
1064         if (cmd_source == src_command)
1065         {
1066                 Cmd_ForwardToServer ();
1067                 return;
1068         }
1069         if (!pausable.integer)
1070                 SV_ClientPrint("Pause not allowed.\n");
1071         else
1072         {
1073                 sv.paused ^= 1;
1074                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1075                 // send notification to all clients
1076                 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1077                 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1078         }
1079 }
1080
1081 /*
1082 ======================
1083 Host_PModel_f
1084 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1085 ======================
1086 */
1087 cvar_t cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
1088 static void Host_PModel_f (void)
1089 {
1090         int i;
1091         eval_t *val;
1092
1093         if (Cmd_Argc () == 1)
1094         {
1095                 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1096                 return;
1097         }
1098         i = atoi(Cmd_Argv(1));
1099
1100         if (cmd_source == src_command)
1101         {
1102                 if (cl_pmodel.integer == i)
1103                         return;
1104                 Cvar_SetValue ("_cl_pmodel", i);
1105                 if (cls.state == ca_connected)
1106                         Cmd_ForwardToServer ();
1107                 return;
1108         }
1109
1110         host_client->pmodel = i;
1111         if (sv_player && (val = GETEDICTFIELDVALUE(sv_player, eval_pmodel)))
1112                 val->_float = i;
1113 }
1114
1115 //===========================================================================
1116
1117
1118 /*
1119 ==================
1120 Host_PreSpawn_f
1121 ==================
1122 */
1123 void Host_PreSpawn_f (void)
1124 {
1125         if (cmd_source == src_command)
1126         {
1127                 Con_Print("prespawn is not valid from the console\n");
1128                 return;
1129         }
1130
1131         if (host_client->spawned)
1132         {
1133                 Con_Print("prespawn not valid -- already spawned\n");
1134                 return;
1135         }
1136
1137         SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
1138         MSG_WriteByte (&host_client->message, svc_signonnum);
1139         MSG_WriteByte (&host_client->message, 2);
1140         host_client->sendsignon = true;
1141 }
1142
1143 /*
1144 ==================
1145 Host_Spawn_f
1146 ==================
1147 */
1148 void Host_Spawn_f (void)
1149 {
1150         int i;
1151         client_t *client;
1152         func_t RestoreGame;
1153         mfunction_t *f;
1154
1155         if (cmd_source == src_command)
1156         {
1157                 Con_Print("spawn is not valid from the console\n");
1158                 return;
1159         }
1160
1161         if (host_client->spawned)
1162         {
1163                 Con_Print("Spawn not valid -- already spawned\n");
1164                 return;
1165         }
1166
1167         if (!sv_player)
1168         {
1169                 Con_Print("Host_Spawn: no edict??\n");
1170                 return;
1171         }
1172
1173         host_client->nametime = 0;
1174
1175         // LordHavoc: moved this above the QC calls at FrikaC's request
1176         // send all current names, colors, and frag counts
1177         SZ_Clear (&host_client->message);
1178
1179         // run the entrance script
1180         if (sv.loadgame)
1181         {
1182                 // loaded games are fully initialized already
1183                 // if this is the last client to be connected, unpause
1184                 sv.paused = false;
1185
1186                 if ((f = ED_FindFunction ("RestoreGame")))
1187                 if ((RestoreGame = (func_t)(f - pr_functions)))
1188                 {
1189                         Con_DPrint("Calling RestoreGame\n");
1190                         pr_global_struct->time = sv.time;
1191                         pr_global_struct->self = EDICT_TO_PROG(sv_player);
1192                         PR_ExecuteProgram (RestoreGame, "");
1193                 }
1194         }
1195         else
1196         {
1197                 // set up the edict
1198                 ED_ClearEdict(sv_player);
1199
1200                 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, sv_player->netname = %s, host_client->name = %s\n", PR_GetString(host_client->edict->v->netname), PR_GetString(sv_player->v->netname), host_client->name);
1201
1202                 // copy spawn parms out of the client_t
1203                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1204                         (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
1205
1206                 // call the spawn function
1207                 pr_global_struct->time = sv.time;
1208                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
1209                 PR_ExecuteProgram (pr_global_struct->ClientConnect, "QC function ClientConnect is missing");
1210
1211                 if ((Sys_DoubleTime() - host_client->netconnection->connecttime) <= sv.time)
1212                         Sys_Printf("%s entered the game\n", host_client->name);
1213
1214                 PR_ExecuteProgram (pr_global_struct->PutClientInServer, "QC function PutClientInServer is missing");
1215         }
1216
1217
1218         // send time of update
1219         MSG_WriteByte (&host_client->message, svc_time);
1220         MSG_WriteFloat (&host_client->message, sv.time);
1221
1222         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1223         {
1224                 if (!client->active)
1225                         continue;
1226                 MSG_WriteByte (&host_client->message, svc_updatename);
1227                 MSG_WriteByte (&host_client->message, i);
1228                 MSG_WriteString (&host_client->message, client->old_name);
1229                 MSG_WriteByte (&host_client->message, svc_updatefrags);
1230                 MSG_WriteByte (&host_client->message, i);
1231                 MSG_WriteShort (&host_client->message, client->old_frags);
1232                 MSG_WriteByte (&host_client->message, svc_updatecolors);
1233                 MSG_WriteByte (&host_client->message, i);
1234                 MSG_WriteByte (&host_client->message, client->old_colors);
1235         }
1236
1237         // send all current light styles
1238         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1239         {
1240                 MSG_WriteByte (&host_client->message, svc_lightstyle);
1241                 MSG_WriteByte (&host_client->message, (char)i);
1242                 MSG_WriteString (&host_client->message, sv.lightstyles[i]);
1243         }
1244
1245         // send some stats
1246         MSG_WriteByte (&host_client->message, svc_updatestat);
1247         MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
1248         MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
1249
1250         MSG_WriteByte (&host_client->message, svc_updatestat);
1251         MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
1252         MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
1253
1254         MSG_WriteByte (&host_client->message, svc_updatestat);
1255         MSG_WriteByte (&host_client->message, STAT_SECRETS);
1256         MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
1257
1258         MSG_WriteByte (&host_client->message, svc_updatestat);
1259         MSG_WriteByte (&host_client->message, STAT_MONSTERS);
1260         MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
1261
1262         // send a fixangle
1263         // Never send a roll angle, because savegames can catch the server
1264         // in a state where it is expecting the client to correct the angle
1265         // and it won't happen if the game was just loaded, so you wind up
1266         // with a permanent head tilt
1267         MSG_WriteByte (&host_client->message, svc_setangle);
1268         MSG_WriteAngle (&host_client->message, sv_player->v->angles[0], sv.protocol);
1269         MSG_WriteAngle (&host_client->message, sv_player->v->angles[1], sv.protocol);
1270         MSG_WriteAngle (&host_client->message, 0, sv.protocol);
1271
1272         SV_WriteClientdataToMessage (sv_player, &host_client->message);
1273
1274         MSG_WriteByte (&host_client->message, svc_signonnum);
1275         MSG_WriteByte (&host_client->message, 3);
1276         host_client->sendsignon = true;
1277 }
1278
1279 /*
1280 ==================
1281 Host_Begin_f
1282 ==================
1283 */
1284 void Host_Begin_f (void)
1285 {
1286         if (cmd_source == src_command)
1287         {
1288                 Con_Print("begin is not valid from the console\n");
1289                 return;
1290         }
1291
1292         host_client->spawned = true;
1293 }
1294
1295 //===========================================================================
1296
1297
1298 /*
1299 ==================
1300 Host_Kick_f
1301
1302 Kicks a user off of the server
1303 ==================
1304 */
1305 void Host_Kick_f (void)
1306 {
1307         char *who;
1308         const char *message = NULL;
1309         client_t *save;
1310         int i;
1311         qboolean byNumber = false;
1312
1313         if (cmd_source != src_command || !sv.active)
1314                 return;
1315
1316         save = host_client;
1317
1318         if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1319         {
1320                 i = atof(Cmd_Argv(2)) - 1;
1321                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1322                         return;
1323                 byNumber = true;
1324         }
1325         else
1326         {
1327                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1328                 {
1329                         if (!host_client->active)
1330                                 continue;
1331                         if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1332                                 break;
1333                 }
1334         }
1335
1336         if (i < svs.maxclients)
1337         {
1338                 if (cmd_source == src_command)
1339                 {
1340                         if (cls.state == ca_dedicated)
1341                                 who = "Console";
1342                         else
1343                                 who = cl_name.string;
1344                 }
1345                 else
1346                         who = save->name;
1347
1348                 // can't kick yourself!
1349                 if (host_client == save)
1350                         return;
1351
1352                 if (Cmd_Argc() > 2)
1353                 {
1354                         message = Cmd_Args();
1355                         COM_ParseToken(&message, false);
1356                         if (byNumber)
1357                         {
1358                                 message++;                                                      // skip the #
1359                                 while (*message == ' ')                         // skip white space
1360                                         message++;
1361                                 message += strlen(Cmd_Argv(2)); // skip the number
1362                         }
1363                         while (*message && *message == ' ')
1364                                 message++;
1365                 }
1366                 if (message)
1367                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1368                 else
1369                         SV_ClientPrintf("Kicked by %s\n", who);
1370                 SV_DropClient (false); // kicked
1371         }
1372
1373         host_client = save;
1374 }
1375
1376 /*
1377 ===============================================================================
1378
1379 DEBUGGING TOOLS
1380
1381 ===============================================================================
1382 */
1383
1384 /*
1385 ==================
1386 Host_Give_f
1387 ==================
1388 */
1389 void Host_Give_f (void)
1390 {
1391         const char *t;
1392         int v;
1393         eval_t *val;
1394
1395         if (cmd_source == src_command)
1396         {
1397                 Cmd_ForwardToServer ();
1398                 return;
1399         }
1400
1401         if (!sv_player)
1402                 return;
1403
1404         if (!allowcheats)
1405         {
1406                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1407                 return;
1408         }
1409
1410         t = Cmd_Argv(1);
1411         v = atoi (Cmd_Argv(2));
1412
1413         switch (t[0])
1414         {
1415         case '0':
1416         case '1':
1417         case '2':
1418         case '3':
1419         case '4':
1420         case '5':
1421         case '6':
1422         case '7':
1423         case '8':
1424         case '9':
1425                 // MED 01/04/97 added hipnotic give stuff
1426                 if (gamemode == GAME_HIPNOTIC)
1427                 {
1428                         if (t[0] == '6')
1429                         {
1430                                 if (t[1] == 'a')
1431                                         sv_player->v->items = (int)sv_player->v->items | HIT_PROXIMITY_GUN;
1432                                 else
1433                                         sv_player->v->items = (int)sv_player->v->items | IT_GRENADE_LAUNCHER;
1434                         }
1435                         else if (t[0] == '9')
1436                                 sv_player->v->items = (int)sv_player->v->items | HIT_LASER_CANNON;
1437                         else if (t[0] == '0')
1438                                 sv_player->v->items = (int)sv_player->v->items | HIT_MJOLNIR;
1439                         else if (t[0] >= '2')
1440                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1441                 }
1442                 else
1443                 {
1444                         if (t[0] >= '2')
1445                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1446                 }
1447                 break;
1448
1449         case 's':
1450                 if (gamemode == GAME_ROGUE && (val = GETEDICTFIELDVALUE(sv_player, eval_ammo_shells1)))
1451                         val->_float = v;
1452
1453                 sv_player->v->ammo_shells = v;
1454                 break;
1455         case 'n':
1456                 if (gamemode == GAME_ROGUE)
1457                 {
1458                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_ammo_nails1)))
1459                         {
1460                                 val->_float = v;
1461                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1462                                         sv_player->v->ammo_nails = v;
1463                         }
1464                 }
1465                 else
1466                 {
1467                         sv_player->v->ammo_nails = v;
1468                 }
1469                 break;
1470         case 'l':
1471                 if (gamemode == GAME_ROGUE)
1472                 {
1473                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_lava_nails);
1474                         if (val)
1475                         {
1476                                 val->_float = v;
1477                                 if (sv_player->v->weapon > IT_LIGHTNING)
1478                                         sv_player->v->ammo_nails = v;
1479                         }
1480                 }
1481                 break;
1482         case 'r':
1483                 if (gamemode == GAME_ROGUE)
1484                 {
1485                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_rockets1);
1486                         if (val)
1487                         {
1488                                 val->_float = v;
1489                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1490                                         sv_player->v->ammo_rockets = v;
1491                         }
1492                 }
1493                 else
1494                 {
1495                         sv_player->v->ammo_rockets = v;
1496                 }
1497                 break;
1498         case 'm':
1499                 if (gamemode == GAME_ROGUE)
1500                 {
1501                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_multi_rockets);
1502                         if (val)
1503                         {
1504                                 val->_float = v;
1505                                 if (sv_player->v->weapon > IT_LIGHTNING)
1506                                         sv_player->v->ammo_rockets = v;
1507                         }
1508                 }
1509                 break;
1510         case 'h':
1511                 sv_player->v->health = v;
1512                 break;
1513         case 'c':
1514                 if (gamemode == GAME_ROGUE)
1515                 {
1516                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_cells1);
1517                         if (val)
1518                         {
1519                                 val->_float = v;
1520                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1521                                         sv_player->v->ammo_cells = v;
1522                         }
1523                 }
1524                 else
1525                 {
1526                         sv_player->v->ammo_cells = v;
1527                 }
1528                 break;
1529         case 'p':
1530                 if (gamemode == GAME_ROGUE)
1531                 {
1532                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_plasma);
1533                         if (val)
1534                         {
1535                                 val->_float = v;
1536                                 if (sv_player->v->weapon > IT_LIGHTNING)
1537                                         sv_player->v->ammo_cells = v;
1538                         }
1539                 }
1540                 break;
1541         }
1542 }
1543
1544 edict_t *FindViewthing (void)
1545 {
1546         int             i;
1547         edict_t *e;
1548
1549         for (i=0 ; i<sv.num_edicts ; i++)
1550         {
1551                 e = EDICT_NUM(i);
1552                 if (!strcmp (PR_GetString(e->v->classname), "viewthing"))
1553                         return e;
1554         }
1555         Con_Print("No viewthing on map\n");
1556         return NULL;
1557 }
1558
1559 /*
1560 ==================
1561 Host_Viewmodel_f
1562 ==================
1563 */
1564 void Host_Viewmodel_f (void)
1565 {
1566         edict_t *e;
1567         model_t *m;
1568
1569         e = FindViewthing ();
1570         if (!e)
1571                 return;
1572
1573         m = Mod_ForName (Cmd_Argv(1), false, true, false);
1574         if (!m)
1575         {
1576                 Con_Printf("Can't load %s\n", Cmd_Argv(1));
1577                 return;
1578         }
1579
1580         e->v->frame = 0;
1581         cl.model_precache[(int)e->v->modelindex] = m;
1582 }
1583
1584 /*
1585 ==================
1586 Host_Viewframe_f
1587 ==================
1588 */
1589 void Host_Viewframe_f (void)
1590 {
1591         edict_t *e;
1592         int             f;
1593         model_t *m;
1594
1595         e = FindViewthing ();
1596         if (!e)
1597                 return;
1598         m = cl.model_precache[(int)e->v->modelindex];
1599
1600         f = atoi(Cmd_Argv(1));
1601         if (f >= m->numframes)
1602                 f = m->numframes-1;
1603
1604         e->v->frame = f;
1605 }
1606
1607
1608 void PrintFrameName (model_t *m, int frame)
1609 {
1610         if (m->animscenes)
1611                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
1612         else
1613                 Con_Printf("frame %i\n", frame);
1614 }
1615
1616 /*
1617 ==================
1618 Host_Viewnext_f
1619 ==================
1620 */
1621 void Host_Viewnext_f (void)
1622 {
1623         edict_t *e;
1624         model_t *m;
1625
1626         e = FindViewthing ();
1627         if (!e)
1628                 return;
1629         m = cl.model_precache[(int)e->v->modelindex];
1630
1631         e->v->frame = e->v->frame + 1;
1632         if (e->v->frame >= m->numframes)
1633                 e->v->frame = m->numframes - 1;
1634
1635         PrintFrameName (m, e->v->frame);
1636 }
1637
1638 /*
1639 ==================
1640 Host_Viewprev_f
1641 ==================
1642 */
1643 void Host_Viewprev_f (void)
1644 {
1645         edict_t *e;
1646         model_t *m;
1647
1648         e = FindViewthing ();
1649         if (!e)
1650                 return;
1651
1652         m = cl.model_precache[(int)e->v->modelindex];
1653
1654         e->v->frame = e->v->frame - 1;
1655         if (e->v->frame < 0)
1656                 e->v->frame = 0;
1657
1658         PrintFrameName (m, e->v->frame);
1659 }
1660
1661 /*
1662 ===============================================================================
1663
1664 DEMO LOOP CONTROL
1665
1666 ===============================================================================
1667 */
1668
1669
1670 /*
1671 ==================
1672 Host_Startdemos_f
1673 ==================
1674 */
1675 void Host_Startdemos_f (void)
1676 {
1677         int             i, c;
1678
1679         if (cls.state == ca_dedicated || COM_CheckParm("-listen"))
1680         {
1681                 if (!sv.active)
1682                 {
1683                         if (gamemode == GAME_TRANSFUSION)
1684                                 Cbuf_AddText ("map bb1\n");
1685                         else
1686                                 Cbuf_AddText ("map start\n");
1687                 }
1688                 return;
1689         }
1690
1691         c = Cmd_Argc() - 1;
1692         if (c > MAX_DEMOS)
1693         {
1694                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
1695                 c = MAX_DEMOS;
1696         }
1697         Con_DPrintf("%i demo(s) in loop\n", c);
1698
1699         for (i=1 ; i<c+1 ; i++)
1700                 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
1701
1702         // LordHavoc: clear the remaining slots
1703         for (;i <= MAX_DEMOS;i++)
1704                 cls.demos[i-1][0] = 0;
1705
1706         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
1707         {
1708                 cls.demonum = 0;
1709                 CL_NextDemo ();
1710         }
1711         else
1712                 cls.demonum = -1;
1713 }
1714
1715
1716 /*
1717 ==================
1718 Host_Demos_f
1719
1720 Return to looping demos
1721 ==================
1722 */
1723 void Host_Demos_f (void)
1724 {
1725         if (cls.state == ca_dedicated)
1726                 return;
1727         if (cls.demonum == -1)
1728                 cls.demonum = 1;
1729         CL_Disconnect_f ();
1730         CL_NextDemo ();
1731 }
1732
1733 /*
1734 ==================
1735 Host_Stopdemo_f
1736
1737 Return to looping demos
1738 ==================
1739 */
1740 void Host_Stopdemo_f (void)
1741 {
1742         if (!cls.demoplayback)
1743                 return;
1744         CL_Disconnect ();
1745         Host_ShutdownServer (false);
1746 }
1747
1748 static void MaxPlayers_f(void)
1749 {
1750         int n;
1751
1752         if (Cmd_Argc() != 2)
1753         {
1754                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
1755                 return;
1756         }
1757
1758         if (sv.active)
1759         {
1760                 Con_Print("maxplayers can not be changed while a server is running.\n");
1761                 return;
1762         }
1763
1764         n = atoi(Cmd_Argv(1));
1765         n = bound(1, n, MAX_SCOREBOARD);
1766         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1767
1768         if (svs.clients)
1769                 Mem_Free(svs.clients);
1770         svs.maxclients = n;
1771         svs.clients = Mem_Alloc(sv_clients_mempool, sizeof(client_t) * svs.maxclients);
1772         if (n == 1)
1773                 Cvar_Set ("deathmatch", "0");
1774         else
1775                 Cvar_Set ("deathmatch", "1");
1776 }
1777
1778 //=============================================================================
1779
1780 /*
1781 ==================
1782 Host_InitCommands
1783 ==================
1784 */
1785 void Host_InitCommands (void)
1786 {
1787         Cmd_AddCommand ("status", Host_Status_f);
1788         Cmd_AddCommand ("quit", Host_Quit_f);
1789         if (gamemode == GAME_NEHAHRA)
1790         {
1791                 Cmd_AddCommand ("max", Host_God_f);
1792                 Cmd_AddCommand ("monster", Host_Notarget_f);
1793                 Cmd_AddCommand ("scrag", Host_Fly_f);
1794                 Cmd_AddCommand ("wraith", Host_Noclip_f);
1795                 Cmd_AddCommand ("gimme", Host_Give_f);
1796         }
1797         else
1798         {
1799                 Cmd_AddCommand ("god", Host_God_f);
1800                 Cmd_AddCommand ("notarget", Host_Notarget_f);
1801                 Cmd_AddCommand ("fly", Host_Fly_f);
1802                 Cmd_AddCommand ("noclip", Host_Noclip_f);
1803                 Cmd_AddCommand ("give", Host_Give_f);
1804         }
1805         Cmd_AddCommand ("map", Host_Map_f);
1806         Cmd_AddCommand ("restart", Host_Restart_f);
1807         Cmd_AddCommand ("changelevel", Host_Changelevel_f);
1808         Cmd_AddCommand ("connect", Host_Connect_f);
1809         Cmd_AddCommand ("reconnect", Host_Reconnect_f);
1810         Cmd_AddCommand ("version", Host_Version_f);
1811         Cmd_AddCommand ("say", Host_Say_f);
1812         Cmd_AddCommand ("say_team", Host_Say_Team_f);
1813         Cmd_AddCommand ("tell", Host_Tell_f);
1814         Cmd_AddCommand ("kill", Host_Kill_f);
1815         Cmd_AddCommand ("pause", Host_Pause_f);
1816         Cmd_AddCommand ("kick", Host_Kick_f);
1817         Cmd_AddCommand ("ping", Host_Ping_f);
1818         Cmd_AddCommand ("load", Host_Loadgame_f);
1819         Cmd_AddCommand ("save", Host_Savegame_f);
1820
1821         Cmd_AddCommand ("startdemos", Host_Startdemos_f);
1822         Cmd_AddCommand ("demos", Host_Demos_f);
1823         Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
1824
1825         Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
1826         Cmd_AddCommand ("viewframe", Host_Viewframe_f);
1827         Cmd_AddCommand ("viewnext", Host_Viewnext_f);
1828         Cmd_AddCommand ("viewprev", Host_Viewprev_f);
1829
1830         Cvar_RegisterVariable (&cl_name);
1831         Cmd_AddCommand ("name", Host_Name_f);
1832         Cvar_RegisterVariable (&cl_color);
1833         Cmd_AddCommand ("color", Host_Color_f);
1834         Cvar_RegisterVariable (&cl_rate);
1835         Cmd_AddCommand ("rate", Host_Rate_f);
1836         if (gamemode == GAME_NEHAHRA)
1837         {
1838                 Cvar_RegisterVariable (&cl_pmodel);
1839                 Cmd_AddCommand ("pmodel", Host_PModel_f);
1840         }
1841         Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
1842         Cmd_AddCommand ("spawn", Host_Spawn_f);
1843         Cmd_AddCommand ("begin", Host_Begin_f);
1844         Cmd_AddCommand ("maxplayers", MaxPlayers_f);
1845
1846         Cvar_RegisterVariable(&sv_cheats);
1847 }
1848