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