]> icculus.org git repositories - divverent/darkplaces.git/blob - host_cmd.c
added "QC function <name> is missing" warnings to more PR_ExecuteProgram calls
[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 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;
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         if (!teamplay.integer)
818                 teamonly = false;
819
820 // turn on color set 1
821         p1 = Cmd_Args();
822         if (!fromServer)
823                 snprintf (text, sizeof(text), "%c%s: %s\n", 1, host_client->name, p1);
824         else
825                 snprintf (text, sizeof(text), "%c<%s> %s\n", 1, hostname.string, p1);
826
827         // note: save is not a valid edict if fromServer is true
828         save = host_client;
829         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
830                 if (host_client->spawned && (!teamonly || host_client->edict->v->team == save->edict->v->team))
831                         SV_ClientPrint(text);
832         host_client = save;
833
834         Sys_Print(&text[1]);
835 }
836
837
838 void Host_Say_f(void)
839 {
840         Host_Say(false);
841 }
842
843
844 void Host_Say_Team_f(void)
845 {
846         Host_Say(true);
847 }
848
849
850 void Host_Tell_f(void)
851 {
852         client_t *save;
853         int j;
854         const char *p1, *p2;
855         char text[1024]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
856         qboolean fromServer = false;
857
858         if (cmd_source == src_command)
859         {
860                 if (cls.state == ca_dedicated)
861                         fromServer = true;
862                 else
863                 {
864                         Cmd_ForwardToServer ();
865                         return;
866                 }
867         }
868
869         if (Cmd_Argc () < 3)
870                 return;
871
872         if (!fromServer)
873                 sprintf (text, "%s: ", host_client->name);
874         else
875                 sprintf (text, "<%s> ", hostname.string);
876
877         p1 = Cmd_Args();
878         p2 = p1 + strlen(p1);
879         // remove the target name
880         while (p1 < p2 && *p1 != ' ')
881                 p1++;
882         while (p1 < p2 && *p1 == ' ')
883                 p1++;
884         // remove trailing newlines
885         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
886                 p2--;
887         // remove quotes if present
888         if (*p1 == '"')
889         {
890                 p1++;
891                 if (p2[-1] == '"')
892                         p2--;
893                 else if (fromServer)
894                         Con_Print("Host_Tell: missing end quote\n");
895                 else
896                         SV_ClientPrint("Host_Tell: missing end quote\n");
897         }
898         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
899                 p2--;
900         for (j = strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
901                 text[j++] = *p1++;
902         text[j++] = '\n';
903         text[j++] = 0;
904
905         save = host_client;
906         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
907                 if (host_client->spawned && !strcasecmp(host_client->name, Cmd_Argv(1)))
908                         SV_ClientPrint(text);
909         host_client = save;
910 }
911
912
913 /*
914 ==================
915 Host_Color_f
916 ==================
917 */
918 cvar_t cl_color = {CVAR_SAVE, "_cl_color", "0"};
919 void Host_Color_f(void)
920 {
921         int             top, bottom;
922         int             playercolor;
923         mfunction_t *f;
924         func_t  SV_ChangeTeam;
925
926         if (Cmd_Argc() == 1)
927         {
928                 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
929                 Con_Print("color <0-15> [0-15]\n");
930                 return;
931         }
932
933         if (Cmd_Argc() == 2)
934                 top = bottom = atoi(Cmd_Argv(1));
935         else
936         {
937                 top = atoi(Cmd_Argv(1));
938                 bottom = atoi(Cmd_Argv(2));
939         }
940
941         top &= 15;
942         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
943         if (top > 15)
944                 top = 15;
945         bottom &= 15;
946         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
947         if (bottom > 15)
948                 bottom = 15;
949
950         playercolor = top*16 + bottom;
951
952         if (cmd_source == src_command)
953         {
954                 Cvar_SetValue ("_cl_color", playercolor);
955                 if (cls.state == ca_connected)
956                         Cmd_ForwardToServer ();
957                 return;
958         }
959
960         if (sv_player && (f = ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - pr_functions)))
961         {
962                 Con_DPrint("Calling SV_ChangeTeam\n");
963                 pr_global_struct->time = sv.time;
964                 pr_globals[OFS_PARM0] = playercolor;
965                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
966                 PR_ExecuteProgram (SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
967         }
968         else
969         {
970                 eval_t *val;
971                 if (sv_player)
972                 {
973                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_clientcolors)))
974                                 val->_float = playercolor;
975                         sv_player->v->team = bottom + 1;
976                 }
977                 host_client->colors = playercolor;
978                 host_client->old_colors = playercolor;
979
980                 // send notification to all clients
981                 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
982                 MSG_WriteByte (&sv.reliable_datagram, host_client->number);
983                 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
984         }
985 }
986
987 cvar_t cl_rate = {CVAR_SAVE, "_cl_rate", "10000"};
988 void Host_Rate_f(void)
989 {
990         int rate;
991
992         if (Cmd_Argc() != 2)
993         {
994                 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
995                 Con_Print("rate <500-25000>\n");
996                 return;
997         }
998
999         rate = atoi(Cmd_Argv(1));
1000
1001         if (cmd_source == src_command)
1002         {
1003                 Cvar_SetValue ("_cl_rate", bound(NET_MINRATE, rate, NET_MAXRATE));
1004                 if (cls.state == ca_connected)
1005                         Cmd_ForwardToServer ();
1006                 return;
1007         }
1008
1009         host_client->netconnection->rate = rate;
1010 }
1011
1012 /*
1013 ==================
1014 Host_Kill_f
1015 ==================
1016 */
1017 void Host_Kill_f (void)
1018 {
1019         if (cmd_source == src_command)
1020         {
1021                 Cmd_ForwardToServer ();
1022                 return;
1023         }
1024
1025         if (!sv_player || sv_player->v->health <= 0)
1026         {
1027                 SV_ClientPrint("Can't suicide -- already dead!\n");
1028                 return;
1029         }
1030
1031         pr_global_struct->time = sv.time;
1032         pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
1033         PR_ExecuteProgram (pr_global_struct->ClientKill, "QC function ClientKill is missing");
1034 }
1035
1036
1037 /*
1038 ==================
1039 Host_Pause_f
1040 ==================
1041 */
1042 void Host_Pause_f (void)
1043 {
1044
1045         if (cmd_source == src_command)
1046         {
1047                 Cmd_ForwardToServer ();
1048                 return;
1049         }
1050         if (!pausable.integer)
1051                 SV_ClientPrint("Pause not allowed.\n");
1052         else
1053         {
1054                 sv.paused ^= 1;
1055                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1056                 // send notification to all clients
1057                 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1058                 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1059         }
1060 }
1061
1062 /*
1063 ======================
1064 Host_PModel_f
1065 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1066 ======================
1067 */
1068 cvar_t cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
1069 static void Host_PModel_f (void)
1070 {
1071         int i;
1072         eval_t *val;
1073
1074         if (Cmd_Argc () == 1)
1075         {
1076                 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1077                 return;
1078         }
1079         i = atoi(Cmd_Argv(1));
1080
1081         if (cmd_source == src_command)
1082         {
1083                 if (cl_pmodel.integer == i)
1084                         return;
1085                 Cvar_SetValue ("_cl_pmodel", i);
1086                 if (cls.state == ca_connected)
1087                         Cmd_ForwardToServer ();
1088                 return;
1089         }
1090
1091         host_client->pmodel = i;
1092         if (sv_player && (val = GETEDICTFIELDVALUE(sv_player, eval_pmodel)))
1093                 val->_float = i;
1094 }
1095
1096 //===========================================================================
1097
1098
1099 /*
1100 ==================
1101 Host_PreSpawn_f
1102 ==================
1103 */
1104 void Host_PreSpawn_f (void)
1105 {
1106         if (cmd_source == src_command)
1107         {
1108                 Con_Print("prespawn is not valid from the console\n");
1109                 return;
1110         }
1111
1112         if (host_client->spawned)
1113         {
1114                 Con_Print("prespawn not valid -- already spawned\n");
1115                 return;
1116         }
1117
1118         SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
1119         MSG_WriteByte (&host_client->message, svc_signonnum);
1120         MSG_WriteByte (&host_client->message, 2);
1121         host_client->sendsignon = true;
1122 }
1123
1124 /*
1125 ==================
1126 Host_Spawn_f
1127 ==================
1128 */
1129 void Host_Spawn_f (void)
1130 {
1131         int i;
1132         client_t *client;
1133         func_t RestoreGame;
1134         mfunction_t *f;
1135
1136         if (cmd_source == src_command)
1137         {
1138                 Con_Print("spawn is not valid from the console\n");
1139                 return;
1140         }
1141
1142         if (host_client->spawned)
1143         {
1144                 Con_Print("Spawn not valid -- already spawned\n");
1145                 return;
1146         }
1147
1148         if (!sv_player)
1149         {
1150                 Con_Print("Host_Spawn: no edict??\n");
1151                 return;
1152         }
1153
1154         host_client->nametime = 0;
1155
1156         // LordHavoc: moved this above the QC calls at FrikaC's request
1157         // send all current names, colors, and frag counts
1158         SZ_Clear (&host_client->message);
1159
1160         // run the entrance script
1161         if (sv.loadgame)
1162         {
1163                 // loaded games are fully initialized already
1164                 // if this is the last client to be connected, unpause
1165                 sv.paused = false;
1166
1167                 if ((f = ED_FindFunction ("RestoreGame")))
1168                 if ((RestoreGame = (func_t)(f - pr_functions)))
1169                 {
1170                         Con_DPrint("Calling RestoreGame\n");
1171                         pr_global_struct->time = sv.time;
1172                         pr_global_struct->self = EDICT_TO_PROG(sv_player);
1173                         PR_ExecuteProgram (RestoreGame, "QC function RestoreGame is missing");
1174                 }
1175         }
1176         else
1177         {
1178                 // set up the edict
1179                 ED_ClearEdict(sv_player);
1180
1181                 //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);
1182
1183                 // copy spawn parms out of the client_t
1184                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1185                         (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
1186
1187                 // call the spawn function
1188                 pr_global_struct->time = sv.time;
1189                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
1190                 PR_ExecuteProgram (pr_global_struct->ClientConnect, "QC function ClientConnect is missing");
1191
1192                 if ((Sys_DoubleTime() - host_client->netconnection->connecttime) <= sv.time)
1193                         Sys_Printf("%s entered the game\n", host_client->name);
1194
1195                 PR_ExecuteProgram (pr_global_struct->PutClientInServer, "QC function PutClientInServer is missing");
1196         }
1197
1198
1199         // send time of update
1200         MSG_WriteByte (&host_client->message, svc_time);
1201         MSG_WriteFloat (&host_client->message, sv.time);
1202
1203         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1204         {
1205                 if (!client->active)
1206                         continue;
1207                 MSG_WriteByte (&host_client->message, svc_updatename);
1208                 MSG_WriteByte (&host_client->message, i);
1209                 MSG_WriteString (&host_client->message, client->old_name);
1210                 MSG_WriteByte (&host_client->message, svc_updatefrags);
1211                 MSG_WriteByte (&host_client->message, i);
1212                 MSG_WriteShort (&host_client->message, client->old_frags);
1213                 MSG_WriteByte (&host_client->message, svc_updatecolors);
1214                 MSG_WriteByte (&host_client->message, i);
1215                 MSG_WriteByte (&host_client->message, client->old_colors);
1216         }
1217
1218         // send all current light styles
1219         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1220         {
1221                 MSG_WriteByte (&host_client->message, svc_lightstyle);
1222                 MSG_WriteByte (&host_client->message, (char)i);
1223                 MSG_WriteString (&host_client->message, sv.lightstyles[i]);
1224         }
1225
1226         // send some stats
1227         MSG_WriteByte (&host_client->message, svc_updatestat);
1228         MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
1229         MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
1230
1231         MSG_WriteByte (&host_client->message, svc_updatestat);
1232         MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
1233         MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
1234
1235         MSG_WriteByte (&host_client->message, svc_updatestat);
1236         MSG_WriteByte (&host_client->message, STAT_SECRETS);
1237         MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
1238
1239         MSG_WriteByte (&host_client->message, svc_updatestat);
1240         MSG_WriteByte (&host_client->message, STAT_MONSTERS);
1241         MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
1242
1243         // send a fixangle
1244         // Never send a roll angle, because savegames can catch the server
1245         // in a state where it is expecting the client to correct the angle
1246         // and it won't happen if the game was just loaded, so you wind up
1247         // with a permanent head tilt
1248         MSG_WriteByte (&host_client->message, svc_setangle);
1249         MSG_WriteAngle (&host_client->message, sv_player->v->angles[0], sv.protocol);
1250         MSG_WriteAngle (&host_client->message, sv_player->v->angles[1], sv.protocol);
1251         MSG_WriteAngle (&host_client->message, 0, sv.protocol);
1252
1253         SV_WriteClientdataToMessage (sv_player, &host_client->message);
1254
1255         MSG_WriteByte (&host_client->message, svc_signonnum);
1256         MSG_WriteByte (&host_client->message, 3);
1257         host_client->sendsignon = true;
1258 }
1259
1260 /*
1261 ==================
1262 Host_Begin_f
1263 ==================
1264 */
1265 void Host_Begin_f (void)
1266 {
1267         if (cmd_source == src_command)
1268         {
1269                 Con_Print("begin is not valid from the console\n");
1270                 return;
1271         }
1272
1273         host_client->spawned = true;
1274 }
1275
1276 //===========================================================================
1277
1278
1279 /*
1280 ==================
1281 Host_Kick_f
1282
1283 Kicks a user off of the server
1284 ==================
1285 */
1286 void Host_Kick_f (void)
1287 {
1288         char *who;
1289         const char *message = NULL;
1290         client_t *save;
1291         int i;
1292         qboolean byNumber = false;
1293
1294         if (cmd_source != src_command || !sv.active)
1295                 return;
1296
1297         save = host_client;
1298
1299         if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1300         {
1301                 i = atof(Cmd_Argv(2)) - 1;
1302                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1303                         return;
1304                 byNumber = true;
1305         }
1306         else
1307         {
1308                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1309                 {
1310                         if (!host_client->active)
1311                                 continue;
1312                         if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1313                                 break;
1314                 }
1315         }
1316
1317         if (i < svs.maxclients)
1318         {
1319                 if (cmd_source == src_command)
1320                 {
1321                         if (cls.state == ca_dedicated)
1322                                 who = "Console";
1323                         else
1324                                 who = cl_name.string;
1325                 }
1326                 else
1327                         who = save->name;
1328
1329                 // can't kick yourself!
1330                 if (host_client == save)
1331                         return;
1332
1333                 if (Cmd_Argc() > 2)
1334                 {
1335                         message = Cmd_Args();
1336                         COM_ParseToken(&message, false);
1337                         if (byNumber)
1338                         {
1339                                 message++;                                                      // skip the #
1340                                 while (*message == ' ')                         // skip white space
1341                                         message++;
1342                                 message += strlen(Cmd_Argv(2)); // skip the number
1343                         }
1344                         while (*message && *message == ' ')
1345                                 message++;
1346                 }
1347                 if (message)
1348                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1349                 else
1350                         SV_ClientPrintf("Kicked by %s\n", who);
1351                 SV_DropClient (false); // kicked
1352         }
1353
1354         host_client = save;
1355 }
1356
1357 /*
1358 ===============================================================================
1359
1360 DEBUGGING TOOLS
1361
1362 ===============================================================================
1363 */
1364
1365 /*
1366 ==================
1367 Host_Give_f
1368 ==================
1369 */
1370 void Host_Give_f (void)
1371 {
1372         const char *t;
1373         int v;
1374         eval_t *val;
1375
1376         if (cmd_source == src_command)
1377         {
1378                 Cmd_ForwardToServer ();
1379                 return;
1380         }
1381
1382         if (!sv_player)
1383                 return;
1384
1385         if (!allowcheats)
1386         {
1387                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1388                 return;
1389         }
1390
1391         t = Cmd_Argv(1);
1392         v = atoi (Cmd_Argv(2));
1393
1394         switch (t[0])
1395         {
1396         case '0':
1397         case '1':
1398         case '2':
1399         case '3':
1400         case '4':
1401         case '5':
1402         case '6':
1403         case '7':
1404         case '8':
1405         case '9':
1406                 // MED 01/04/97 added hipnotic give stuff
1407                 if (gamemode == GAME_HIPNOTIC)
1408                 {
1409                         if (t[0] == '6')
1410                         {
1411                                 if (t[1] == 'a')
1412                                         sv_player->v->items = (int)sv_player->v->items | HIT_PROXIMITY_GUN;
1413                                 else
1414                                         sv_player->v->items = (int)sv_player->v->items | IT_GRENADE_LAUNCHER;
1415                         }
1416                         else if (t[0] == '9')
1417                                 sv_player->v->items = (int)sv_player->v->items | HIT_LASER_CANNON;
1418                         else if (t[0] == '0')
1419                                 sv_player->v->items = (int)sv_player->v->items | HIT_MJOLNIR;
1420                         else if (t[0] >= '2')
1421                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1422                 }
1423                 else
1424                 {
1425                         if (t[0] >= '2')
1426                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1427                 }
1428                 break;
1429
1430         case 's':
1431                 if (gamemode == GAME_ROGUE && (val = GETEDICTFIELDVALUE(sv_player, eval_ammo_shells1)))
1432                         val->_float = v;
1433
1434                 sv_player->v->ammo_shells = v;
1435                 break;
1436         case 'n':
1437                 if (gamemode == GAME_ROGUE)
1438                 {
1439                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_ammo_nails1)))
1440                         {
1441                                 val->_float = v;
1442                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1443                                         sv_player->v->ammo_nails = v;
1444                         }
1445                 }
1446                 else
1447                 {
1448                         sv_player->v->ammo_nails = v;
1449                 }
1450                 break;
1451         case 'l':
1452                 if (gamemode == GAME_ROGUE)
1453                 {
1454                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_lava_nails);
1455                         if (val)
1456                         {
1457                                 val->_float = v;
1458                                 if (sv_player->v->weapon > IT_LIGHTNING)
1459                                         sv_player->v->ammo_nails = v;
1460                         }
1461                 }
1462                 break;
1463         case 'r':
1464                 if (gamemode == GAME_ROGUE)
1465                 {
1466                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_rockets1);
1467                         if (val)
1468                         {
1469                                 val->_float = v;
1470                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1471                                         sv_player->v->ammo_rockets = v;
1472                         }
1473                 }
1474                 else
1475                 {
1476                         sv_player->v->ammo_rockets = v;
1477                 }
1478                 break;
1479         case 'm':
1480                 if (gamemode == GAME_ROGUE)
1481                 {
1482                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_multi_rockets);
1483                         if (val)
1484                         {
1485                                 val->_float = v;
1486                                 if (sv_player->v->weapon > IT_LIGHTNING)
1487                                         sv_player->v->ammo_rockets = v;
1488                         }
1489                 }
1490                 break;
1491         case 'h':
1492                 sv_player->v->health = v;
1493                 break;
1494         case 'c':
1495                 if (gamemode == GAME_ROGUE)
1496                 {
1497                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_cells1);
1498                         if (val)
1499                         {
1500                                 val->_float = v;
1501                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1502                                         sv_player->v->ammo_cells = v;
1503                         }
1504                 }
1505                 else
1506                 {
1507                         sv_player->v->ammo_cells = v;
1508                 }
1509                 break;
1510         case 'p':
1511                 if (gamemode == GAME_ROGUE)
1512                 {
1513                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_plasma);
1514                         if (val)
1515                         {
1516                                 val->_float = v;
1517                                 if (sv_player->v->weapon > IT_LIGHTNING)
1518                                         sv_player->v->ammo_cells = v;
1519                         }
1520                 }
1521                 break;
1522         }
1523 }
1524
1525 edict_t *FindViewthing (void)
1526 {
1527         int             i;
1528         edict_t *e;
1529
1530         for (i=0 ; i<sv.num_edicts ; i++)
1531         {
1532                 e = EDICT_NUM(i);
1533                 if (!strcmp (PR_GetString(e->v->classname), "viewthing"))
1534                         return e;
1535         }
1536         Con_Print("No viewthing on map\n");
1537         return NULL;
1538 }
1539
1540 /*
1541 ==================
1542 Host_Viewmodel_f
1543 ==================
1544 */
1545 void Host_Viewmodel_f (void)
1546 {
1547         edict_t *e;
1548         model_t *m;
1549
1550         e = FindViewthing ();
1551         if (!e)
1552                 return;
1553
1554         m = Mod_ForName (Cmd_Argv(1), false, true, false);
1555         if (!m)
1556         {
1557                 Con_Printf("Can't load %s\n", Cmd_Argv(1));
1558                 return;
1559         }
1560
1561         e->v->frame = 0;
1562         cl.model_precache[(int)e->v->modelindex] = m;
1563 }
1564
1565 /*
1566 ==================
1567 Host_Viewframe_f
1568 ==================
1569 */
1570 void Host_Viewframe_f (void)
1571 {
1572         edict_t *e;
1573         int             f;
1574         model_t *m;
1575
1576         e = FindViewthing ();
1577         if (!e)
1578                 return;
1579         m = cl.model_precache[(int)e->v->modelindex];
1580
1581         f = atoi(Cmd_Argv(1));
1582         if (f >= m->numframes)
1583                 f = m->numframes-1;
1584
1585         e->v->frame = f;
1586 }
1587
1588
1589 void PrintFrameName (model_t *m, int frame)
1590 {
1591         if (m->animscenes)
1592                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
1593         else
1594                 Con_Printf("frame %i\n", frame);
1595 }
1596
1597 /*
1598 ==================
1599 Host_Viewnext_f
1600 ==================
1601 */
1602 void Host_Viewnext_f (void)
1603 {
1604         edict_t *e;
1605         model_t *m;
1606
1607         e = FindViewthing ();
1608         if (!e)
1609                 return;
1610         m = cl.model_precache[(int)e->v->modelindex];
1611
1612         e->v->frame = e->v->frame + 1;
1613         if (e->v->frame >= m->numframes)
1614                 e->v->frame = m->numframes - 1;
1615
1616         PrintFrameName (m, e->v->frame);
1617 }
1618
1619 /*
1620 ==================
1621 Host_Viewprev_f
1622 ==================
1623 */
1624 void Host_Viewprev_f (void)
1625 {
1626         edict_t *e;
1627         model_t *m;
1628
1629         e = FindViewthing ();
1630         if (!e)
1631                 return;
1632
1633         m = cl.model_precache[(int)e->v->modelindex];
1634
1635         e->v->frame = e->v->frame - 1;
1636         if (e->v->frame < 0)
1637                 e->v->frame = 0;
1638
1639         PrintFrameName (m, e->v->frame);
1640 }
1641
1642 /*
1643 ===============================================================================
1644
1645 DEMO LOOP CONTROL
1646
1647 ===============================================================================
1648 */
1649
1650
1651 /*
1652 ==================
1653 Host_Startdemos_f
1654 ==================
1655 */
1656 void Host_Startdemos_f (void)
1657 {
1658         int             i, c;
1659
1660         if (cls.state == ca_dedicated || COM_CheckParm("-listen"))
1661         {
1662                 if (!sv.active)
1663                 {
1664                         if (gamemode == GAME_TRANSFUSION)
1665                                 Cbuf_AddText ("map bb1\n");
1666                         else
1667                                 Cbuf_AddText ("map start\n");
1668                 }
1669                 return;
1670         }
1671
1672         c = Cmd_Argc() - 1;
1673         if (c > MAX_DEMOS)
1674         {
1675                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
1676                 c = MAX_DEMOS;
1677         }
1678         Con_DPrintf("%i demo(s) in loop\n", c);
1679
1680         for (i=1 ; i<c+1 ; i++)
1681                 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
1682
1683         // LordHavoc: clear the remaining slots
1684         for (;i <= MAX_DEMOS;i++)
1685                 cls.demos[i-1][0] = 0;
1686
1687         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
1688         {
1689                 cls.demonum = 0;
1690                 CL_NextDemo ();
1691         }
1692         else
1693                 cls.demonum = -1;
1694 }
1695
1696
1697 /*
1698 ==================
1699 Host_Demos_f
1700
1701 Return to looping demos
1702 ==================
1703 */
1704 void Host_Demos_f (void)
1705 {
1706         if (cls.state == ca_dedicated)
1707                 return;
1708         if (cls.demonum == -1)
1709                 cls.demonum = 1;
1710         CL_Disconnect_f ();
1711         CL_NextDemo ();
1712 }
1713
1714 /*
1715 ==================
1716 Host_Stopdemo_f
1717
1718 Return to looping demos
1719 ==================
1720 */
1721 void Host_Stopdemo_f (void)
1722 {
1723         if (!cls.demoplayback)
1724                 return;
1725         CL_Disconnect ();
1726         Host_ShutdownServer (false);
1727 }
1728
1729 static void MaxPlayers_f(void)
1730 {
1731         int n;
1732
1733         if (Cmd_Argc() != 2)
1734         {
1735                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
1736                 return;
1737         }
1738
1739         if (sv.active)
1740         {
1741                 Con_Print("maxplayers can not be changed while a server is running.\n");
1742                 return;
1743         }
1744
1745         n = atoi(Cmd_Argv(1));
1746         n = bound(1, n, MAX_SCOREBOARD);
1747         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1748
1749         if (svs.clients)
1750                 Mem_Free(svs.clients);
1751         svs.maxclients = n;
1752         svs.clients = Mem_Alloc(sv_clients_mempool, sizeof(client_t) * svs.maxclients);
1753         if (n == 1)
1754                 Cvar_Set ("deathmatch", "0");
1755         else
1756                 Cvar_Set ("deathmatch", "1");
1757 }
1758
1759 //=============================================================================
1760
1761 /*
1762 ==================
1763 Host_InitCommands
1764 ==================
1765 */
1766 void Host_InitCommands (void)
1767 {
1768         Cmd_AddCommand ("status", Host_Status_f);
1769         Cmd_AddCommand ("quit", Host_Quit_f);
1770         if (gamemode == GAME_NEHAHRA)
1771         {
1772                 Cmd_AddCommand ("max", Host_God_f);
1773                 Cmd_AddCommand ("monster", Host_Notarget_f);
1774                 Cmd_AddCommand ("scrag", Host_Fly_f);
1775                 Cmd_AddCommand ("wraith", Host_Noclip_f);
1776                 Cmd_AddCommand ("gimme", Host_Give_f);
1777         }
1778         else
1779         {
1780                 Cmd_AddCommand ("god", Host_God_f);
1781                 Cmd_AddCommand ("notarget", Host_Notarget_f);
1782                 Cmd_AddCommand ("fly", Host_Fly_f);
1783                 Cmd_AddCommand ("noclip", Host_Noclip_f);
1784                 Cmd_AddCommand ("give", Host_Give_f);
1785         }
1786         Cmd_AddCommand ("map", Host_Map_f);
1787         Cmd_AddCommand ("restart", Host_Restart_f);
1788         Cmd_AddCommand ("changelevel", Host_Changelevel_f);
1789         Cmd_AddCommand ("connect", Host_Connect_f);
1790         Cmd_AddCommand ("reconnect", Host_Reconnect_f);
1791         Cmd_AddCommand ("version", Host_Version_f);
1792         Cmd_AddCommand ("say", Host_Say_f);
1793         Cmd_AddCommand ("say_team", Host_Say_Team_f);
1794         Cmd_AddCommand ("tell", Host_Tell_f);
1795         Cmd_AddCommand ("kill", Host_Kill_f);
1796         Cmd_AddCommand ("pause", Host_Pause_f);
1797         Cmd_AddCommand ("kick", Host_Kick_f);
1798         Cmd_AddCommand ("ping", Host_Ping_f);
1799         Cmd_AddCommand ("load", Host_Loadgame_f);
1800         Cmd_AddCommand ("save", Host_Savegame_f);
1801
1802         Cmd_AddCommand ("startdemos", Host_Startdemos_f);
1803         Cmd_AddCommand ("demos", Host_Demos_f);
1804         Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
1805
1806         Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
1807         Cmd_AddCommand ("viewframe", Host_Viewframe_f);
1808         Cmd_AddCommand ("viewnext", Host_Viewnext_f);
1809         Cmd_AddCommand ("viewprev", Host_Viewprev_f);
1810
1811         Cvar_RegisterVariable (&cl_name);
1812         Cmd_AddCommand ("name", Host_Name_f);
1813         Cvar_RegisterVariable (&cl_color);
1814         Cmd_AddCommand ("color", Host_Color_f);
1815         Cvar_RegisterVariable (&cl_rate);
1816         Cmd_AddCommand ("rate", Host_Rate_f);
1817         if (gamemode == GAME_NEHAHRA)
1818         {
1819                 Cvar_RegisterVariable (&cl_pmodel);
1820                 Cmd_AddCommand ("pmodel", Host_PModel_f);
1821         }
1822         Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
1823         Cmd_AddCommand ("spawn", Host_Spawn_f);
1824         Cmd_AddCommand ("begin", Host_Begin_f);
1825         Cmd_AddCommand ("maxplayers", MaxPlayers_f);
1826
1827         Cvar_RegisterVariable(&sv_cheats);
1828 }
1829