1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2010 COR Entertainment, LLC.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #if defined HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #if defined HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32
33 #if !defined HAVE__STRDUP
34 #define _strdup strdup
35 #endif
36
37 #include "g_local.h"
38
39 game_locals_t game;
40 level_locals_t level;
41 game_import_t gi;
42 game_export_t globals;
43 spawn_temp_t st;
44 g_vote_t playervote;
45 tactical_t tacticalScore;
46
47 int sm_meat_index;
48 int meansOfDeath;
49 int bot_won;
50
51 char *winningmap;
52
53 edict_t *g_edicts;
54
55 cvar_t *deathmatch;
56 cvar_t *ctf;
57 cvar_t *tca;
58 cvar_t *cp;
59 //mutators
60 cvar_t *instagib;
61 cvar_t *rocket_arena;
62 cvar_t *insta_rockets;
63 cvar_t *low_grav;
64 cvar_t *regeneration;
65 cvar_t *vampire;
66 cvar_t *excessive;
67 cvar_t *grapple;
68 cvar_t *classbased;
69
70 //duel mode
71 cvar_t *g_duel;
72
73 //tactical mode
74 cvar_t *g_tactical;
75
76 cvar_t *g_losehealth;
77 cvar_t *g_losehealth_num;
78
79 //weapons
80 cvar_t *wep_selfdmgmulti;
81
82 //health/max health/max ammo
83 cvar_t *g_spawnhealth;
84 cvar_t *g_maxhealth;
85 cvar_t *g_maxbullets;
86 cvar_t *g_maxshells;
87 cvar_t *g_maxrockets;
88 cvar_t *g_maxgrenades;
89 cvar_t *g_maxcells;
90 cvar_t *g_maxslugs;
91 cvar_t *g_maxseekers;
92 cvar_t *g_maxbombs;
93
94 //quick weapon change
95 cvar_t *quickweap;
96
97 //anti-camp
98 cvar_t *anticamp;
99 cvar_t *camptime;
100
101 //show player lights for visibility even in non-team games
102 cvar_t *g_dmlights;
103
104 /** @brief Anti-camp frames
105 *
106 * This CVar controls the amount of frames velocity is accumulated for when
107 * trying to determine if a player is camping. Its valid range is 1-100,
108 * anything outside this range will default it to the value of the
109 * G_ANTICAMP_FRAMES constant.
110 *
111 * @note If the value is 1, the anti-camp will work exactly as it used to.
112 *
113 * @note This CVar's value should be high enough for the anti-camp to work
114 * correctly (the higher it is, the more the system "remembers" about
115 * players' previous velocities), but low enough and for it not to
116 * punish players for camping long after they've resumed moving.
117 */
118 cvar_t *ac_frames;
119
120 /** @brief Anti-camp threshold
121 *
122 * This CVar contains the speed below which the timeout to "suicide" is no
123 * longer updated. It will default to G_ANTICAMP_THRESHOLD if its value is
124 * not in the ]0;500] range.
125 *
126 * @note In excessive mode, this value is multiplied by 1.5
127 */
128 cvar_t *ac_threshold;
129
130 //random quad
131 cvar_t *g_randomquad;
132
133 //warmup
134 cvar_t *warmuptime;
135
136 //spawn protection
137 cvar_t *g_spawnprotect;
138
139 //joust mode
140 cvar_t *joustmode;
141
142 //map voting
143 cvar_t *g_mapvote;
144 cvar_t *g_voterand;
145 cvar_t *g_votemode;
146 cvar_t *g_votesame;
147
148 //call voting
149 cvar_t *g_callvote;
150
151 //reward point threshold
152 cvar_t *g_reward;
153
154 //force autobalanced teams
155 cvar_t *g_autobalance;
156
157 cvar_t *dmflags;
158 cvar_t *skill;
159 cvar_t *fraglimit;
160 cvar_t *timelimit;
161 cvar_t *password;
162 cvar_t *spectator_password;
163 cvar_t *needpass;
164 cvar_t *g_maxclients;
165 cvar_t *maxspectators;
166 cvar_t *maxentities;
167 cvar_t *g_select_empty;
168 cvar_t *g_dedicated;
169 cvar_t *motdfile;
170
171 /** @brief CVar that forces MOTD display
172 *
173 * This CVar should contain an integer, which will indicate how client frames
174 * the MOTD should be forced on for. If it is 0, then the MOTD will not be
175 * forced on.
176 */
177 cvar_t *motdforce;
178
179 cvar_t *filterban;
180
181 cvar_t *sv_maxvelocity;
182 cvar_t *sv_gravity;
183
184 cvar_t *sv_rollspeed;
185 cvar_t *sv_rollangle;
186 cvar_t *gun_x;
187 cvar_t *gun_y;
188 cvar_t *gun_z;
189
190 cvar_t *run_pitch;
191 cvar_t *run_roll;
192 cvar_t *bob_up;
193 cvar_t *bob_pitch;
194 cvar_t *bob_roll;
195
196 cvar_t *sv_cheats;
197
198 cvar_t *flood_msgs;
199 cvar_t *flood_persecond;
200 cvar_t *flood_waitdelay;
201
202 cvar_t *sv_maplist;
203
204 cvar_t *g_background_music;
205
206 cvar_t *sv_botkickthreshold;
207 cvar_t *sv_custombots;
208
209 //unlagged
210 cvar_t *g_antilag;
211 cvar_t *g_antilagdebug;
212 cvar_t *g_antilagprojectiles;
213
214 void SpawnEntities (char *mapname, char *entities, char *spawnpoint);
215 void ClientThink (edict_t *ent, usercmd_t *cmd);
216 qboolean ClientConnect (edict_t *ent, char *userinfo);
217 void ClientUserinfoChanged (edict_t *ent, char *userinfo, int whereFrom);
218 void ClientDisconnect (edict_t *ent);
219 void ClientBegin (edict_t *ent);
220 void ClientCommand (edict_t *ent);
221 void RunEntity (edict_t *ent);
222 void InitGame (void);
223 void G_RunFrame (void);
224 int ACESP_FindBotNum(void);
225 extern long filelength(int);
226 extern void ED_CallSpawn (edict_t *ent);
227
228 static size_t szr;
229
g_filelength(FILE * f)230 static int g_filelength( FILE *f )
231 {
232 int length = -1;
233
234 #if defined HAVE_FILELENGTH
235
236 length = filelength( fileno( f ) );
237
238 #elif defined HAVE_FSTAT
239
240 struct stat statbfr;
241 int result;
242
243 result = fstat( fileno(f), &statbfr );
244 if ( result != -1 )
245 {
246 length = (int)statbfr.st_size;
247 }
248
249 #else
250
251 long int pos;
252
253 pos = ftell( f );
254 fseek( f, 0L, SEEK_END );
255 length = ftell( f );
256 fseek( f, pos, SEEK_SET );
257
258 #endif
259
260 return length;
261 }
262
263
264 //===================================================================
265
266
ShutdownGame(void)267 void ShutdownGame (void)
268 {
269 gi.dprintf ("==== ShutdownGame ====\n");
270
271 gi.FreeTags (TAG_LEVEL);
272 gi.FreeTags (TAG_GAME);
273 }
274
275 /*
276 =============
277 G_ForceExitIntermission
278
279 To prevent those annoying occurrences when you join a server which has been
280 idling at intermission for a long time, then it instantly switches maps as
281 soon as the old one is done loading.
282
283 Now new player joins will force intermission to end after twenty seconds, even
284 if there are still players in the server.
285 =============
286 */
287 void ExitLevel (void);
G_ForceExitIntermission(void)288 void G_ForceExitIntermission (void)
289 {
290 if (level.time < level.intermissiontime + 20.0 || !level.intermissiontime)
291 return;
292 ExitLevel();
293 }
294
295 /*
296 =================
297 GetGameAPI
298
299 Returns a pointer to the structure with all entry points
300 and global variables
301 =================
302 */
GetGameAPI(game_import_t * import)303 game_export_t *GetGameAPI (game_import_t *import)
304 {
305 gi = *import;
306
307 globals.apiversion = GAME_API_VERSION;
308 globals.Init = InitGame;
309 globals.Shutdown = ShutdownGame;
310 globals.SpawnEntities = SpawnEntities;
311
312 globals.ClientThink = ClientThink;
313 globals.ClientConnect = ClientConnect;
314 globals.ClientUserinfoChanged = ClientUserinfoChanged;
315 globals.ClientDisconnect = ClientDisconnect;
316 globals.ClientBegin = ClientBegin;
317 globals.ClientCommand = ClientCommand;
318 globals.ACESP_FindBotNum = ACESP_FindBotNum;
319
320 globals.RunFrame = G_RunFrame;
321 globals.ForceExitIntermission = G_ForceExitIntermission;
322
323 globals.ServerCommand = ServerCommand;
324
325 globals.edict_size = sizeof(edict_t);
326
327 return &globals;
328 }
329
330
331 #define Z_MAGIC 0x1d1d
332
333 typedef struct zhead_s
334 {
335 struct zhead_s *prev, *next;
336 short magic;
337 short tag;
338 int size;
339 } zhead_t;
340
341 zhead_t z_chain;
342 int z_count, z_bytes;
343
344
345 /*
346 =================
347 ClientEndServerFrames
348 =================
349 */
ClientEndServerFrames(void)350 void ClientEndServerFrames (void)
351 {
352 int i;
353 edict_t *ent;
354
355 /* make sure bot info in first client is up to date */
356 ACESP_UpdateBots();
357
358 // calc the player views now that all pushing
359 // and damage has been added
360 for (i=0 ; i<g_maxclients->value ; i++)
361 {
362 ent = g_edicts + 1 + i;
363 if (!ent->inuse || !ent->client)
364 continue;
365 ClientEndServerFrame (ent);
366 }
367
368 }
369
370 /*
371 =================
372 CreateTargetChangeLevel
373
374 Returns the created target changelevel
375 =================
376 */
CreateTargetChangeLevel(char * map)377 edict_t *CreateTargetChangeLevel(char *map)
378 {
379 edict_t *ent;
380
381 ent = G_Spawn ();
382 ent->classname = "target_changelevel";
383 Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map);
384 ent->map = level.nextmap;
385 return ent;
386 }
387
388
389 /**
390 * \brief Log scores and stats
391 *
392 * \detail This is a somewhat crude formatted dump of game information to
393 * the server console and log file. This version is mostly intended
394 * to collect some data about bot targeting accuracy.
395 *
396 * The odds that the array indexes for the weapon hit data and weapon
397 * accuracy data are correctly cross-referenced are slim.
398 *
399 * --- from hud stat report (p_hud.c) ---
400 * --- currently def'd out ---
401 * 0: "blaster"
402 * 1: "disruptor"
403 * 2: "smartgun"
404 * 3: "chaingun"
405 * 4: "flame"
406 * 5: "rocket"
407 * 6: "beamgun"
408 * 7: "vaporizer"
409 * 8: "violator"
410 *
411 *--- weapon_hit[] incremented ---
412 * 0 : blasterball_touch : Weapon_Beamgun, Weapon_Blaster, Weapon_Hover
413 * 0 : fire_blaster_beam : Weapon_Beamgun, Weapon_Blaster, Weapon_Alienblaster, Weapon_Strafer
414 * 1 : fire_disruptor : Weapon_Disruptor
415 * 2 : smartgrenade_think : Weapon_Smartgun
416 * 3 : fire_lead : Weapon_Chain
417 * 4 : fireball_touch : Weapon_Flame
418 * 4 : flame_touch : Weapon_Flame
419 * 5 : rocket_touch : Weapon_RocketLauncher, Weapon_Bomber, Weapon_Strafer
420 * 6 : fire_blaster : Weapon_Beamgun, Weapon_Blaster
421 * 7 : bomb_touch : Weapon_Bomber, Weapon_Vaporizer
422 * 7 : fire_vaporizer : Weapon_Vaporizer
423 * 8 : fire_violator : Weapon_Violator
424
425 * --- from sample.cfg accuracy
426 * weapon_hit[] :to: bot weapacc[]
427 * 0: 1: blaster
428 * 1: 2: alien disruptor
429 * 3: 3: pulse rifle (chaingun)
430 * 4: 4: flame thrower
431 * x: 5: homing rocket launcher (not implemented)
432 * 5: 6: rocket launcher
433 * 2: 7: alien smartgun
434 * 6: 8: alien beamgun
435 * 7: 9: alien vaporizer
436 * 8: x: violator
437 */
game_report(void)438 static void game_report( void )
439 {
440 static const char* weapname[] =
441 {
442 " blaster", // 0 blaster, hover
443 " disruptor", // 1
444 " smartgun", // 2
445 " chaingun", // 3
446 " flame", // 4
447 " rocket", // 5 rocketlauncher, bomber, strafer
448 " beamgun", // 6 beamgun
449 " vaporizer", // 7 +bomber's bomb
450 " violator" // 8
451 };
452 /* cross-reference from weapon_hit to weapacc[] */
453 static const int accxref[] =
454 {
455 1, /* 0 blaster */
456 2, /* 1 disruptor */
457 7, /* 2 smartgun */
458 3, /* 3 chaingun */
459 4, /* 4 flame */
460 6, /* 5 rocket */
461 8, /* 6 beamgun */
462 9, /* 7 vaporizer */
463 0 /* 8 violator */ /* no weapacc[] for this */
464 };
465
466 edict_t *pclient;
467 int i;
468 char *client_name;
469
470 gi.dprintf( "<GAME-REPORT-BEGIN>\n" );
471
472 for ( i = 0, pclient = &g_edicts[1] ; i < game.maxclients ; ++pclient, i++ )
473 { // for each possible client, human or bot
474 if ( pclient->inuse && game.clients[i].resp.spectator == 0 )
475 {
476 int weap;
477
478 /* scoring - UNSORTED */
479 client_name = Info_ValueForKey( pclient->client->pers.userinfo, "name" );
480 if ( pclient->is_bot )
481 {
482 gi.dprintf( "%s (%i,%0.2f): %i\n",
483 client_name,
484 pclient->skill, pclient->awareness,
485 game.clients[i].resp.score );
486 }
487 else
488 {
489 gi.dprintf( "%s: %i\n",
490 client_name,
491 game.clients[i].resp.score );
492 }
493
494 /* weapon skill */
495 for ( weap = 0; weap < 9; weap++ )
496 {
497 if ( pclient->client->resp.weapon_shots[weap] != 0 )
498 {
499 int hits = pclient->client->resp.weapon_hits[weap];
500 int shots = pclient->client->resp.weapon_shots[weap];
501 int percent = (100 * hits) / shots;
502
503 if ( pclient->is_bot )
504 {
505 if ( weap == 8 )
506 { /* violator - no weapacc[], accuracy always 1.0 */
507 gi.dprintf( " %s (1.0): %i/%i = %i%%\n",
508 weapname[weap], hits, shots, percent );
509 }
510 else
511 {
512 gi.dprintf( " %s (%0.2f): %i/%i = %i%%\n",
513 weapname[weap],
514 pclient->weapacc[accxref[weap]],
515 hits, shots, percent );
516 }
517 }
518 else
519 {
520 gi.dprintf( " %s : %i/%i = %i%%\n",
521 weapname[weap], hits, shots, percent );
522 }
523 }
524 }
525 }
526 }
527 gi.dprintf("<GAME-REPORT-END>\n");
528
529 }
530
531
532 /*
533 =================
534 EndDMLevel
535
536 The timelimit or fraglimit has been exceeded
537 =================
538 */
EndDMLevel(void)539 void EndDMLevel (void)
540 {
541 edict_t *ent;
542 char *s, *t, *f;
543 static const char *seps = " ,\n\r";
544 char *buffer;
545 char mapsname[MAX_OSPATH];
546 int length;
547 static char **mapnames;
548 static int nummaps;
549 int i;
550 FILE *fp;
551
552 // log game scoring, statistics. currently crude for bot testing mostly.
553 if ( g_dedicated && g_dedicated->integer != 0 )
554 {
555 game_report();
556 }
557
558 /* Search for dead players in order to remove DeathCam and free mem */
559 for (i=0 ; i<g_maxclients->value ; i++)
560 {
561 ent = g_edicts + i + 1;
562 if (!ent->inuse || ent->client->resp.spectator)
563 continue;
564 if(!ent->is_bot && ent->deadflag)
565 DeathcamRemove (ent, "off");
566 }
567
568 //call voting
569 if(g_callvote->value)
570 {
571 playervote.called = false;
572 playervote.yay = 0;
573 playervote.nay = 0;
574 playervote.command[0] = 0;
575 }
576
577 //map voting
578 if(g_mapvote->value)
579 {
580 level.changemap = level.mapname;
581
582 // initialise map list using the current map's name
583 // this is done in all modes just in case
584 for ( i = 0 ; i < 4 ; i ++ )
585 {
586 strcpy(votedmap[i].mapname, level.mapname);
587 votedmap[i].tally = 0;
588 }
589
590 // if there is a map list, time to choose
591 if ( sv_maplist && sv_maplist->string && *(sv_maplist->string) )
592 {
593 char * names[200]; // 200 map names should be fine
594 int n_maps = 0;
595 int same_index = -1;
596 int mode = ( g_votemode ? g_votemode->value : 0 );
597 qboolean same = ( g_votesame ? (g_votesame->value == 1) : 1 );
598
599 memset( names, 0 , sizeof(names) );
600 s = _strdup( sv_maplist->string );
601 t = strtok( s, seps );
602 do {
603 // if using the same map is disallowed, skip it
604 if ( !Q_strcasecmp(t, level.mapname) )
605 {
606 if ( same_index == -1 )
607 same_index = n_maps;
608 if ( !same )
609 continue;
610 }
611
612 // avoid duplicates
613 for ( i = 0 ; i < n_maps ; i ++ )
614 {
615 if ( ! Q_strcasecmp( t , names[i] ) )
616 break;
617 }
618 if ( i < n_maps )
619 continue;
620
621 names[n_maps ++] = t;
622 } while ( (t = strtok( NULL, seps)) != NULL );
623
624 if ( n_maps > 0 )
625 {
626 // if the map list has been changed and the
627 // current map is no longer in it, prevent
628 // screw-ups
629 if ( same_index == -1 )
630 same_index = 0;
631
632 if ( mode == 0 )
633 {
634 // standard vote mode, take the next
635 // 4 maps from the list
636 for ( i = 0 ; i < 4 ; i ++ )
637 strcpy( votedmap[i].mapname, names[(same_index + i) % n_maps] );
638 }
639 else
640 {
641 // random selection
642 for ( i = 0 ; i < 4 && n_maps > 0 ; i ++ )
643 {
644 int map = random() * (n_maps - 1);
645 strcpy( votedmap[i].mapname, names[map] );
646 names[map] = names[n_maps - 1];
647 n_maps --;
648 }
649 }
650 }
651
652 free(s);
653 }
654 }
655
656 // stay on same level flag
657 if (dmflags->integer & DF_SAME_LEVEL)
658 {
659 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
660 return;
661 }
662
663 if (bot_won && !(dmflags->integer & DF_BOT_LEVELAD) && !ctf->value)
664 {
665 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
666 return;
667 }
668
669 if( ( ctf->integer || cp->integer ) && ! g_dedicated->integer )
670 { //ctf will just stay on same level unless specified by dedicated list
671 BeginIntermission (CreateTargetChangeLevel (level.mapname));
672 return;
673 }
674 // see if it's in the map list
675 if (*sv_maplist->string) {
676 s = _strdup(sv_maplist->string);
677 f = NULL;
678 t = strtok(s, seps);
679 while (t != NULL) {
680 if (Q_strcasecmp(t, level.mapname) == 0) {
681 // it's in the list, go to the next one
682 t = strtok(NULL, seps);
683 if (t == NULL) { // end of list, go to first one
684 if (f == NULL) // there isn't a first one, same level
685 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
686 else
687 BeginIntermission (CreateTargetChangeLevel (f) );
688 } else
689 BeginIntermission (CreateTargetChangeLevel (t) );
690 free(s);
691 return;
692 }
693 if (!f)
694 f = t;
695 t = strtok(NULL, seps);
696 }
697 free(s);
698 }
699
700 if(ctf->integer) { //wasn't in the dedicated list
701 BeginIntermission (CreateTargetChangeLevel (level.mapname));
702 return;
703 }
704
705 //check the maps.lst file and read in those, which will overide anything in the level
706 //write this code here
707
708 /*
709 ** load the list of map names
710 */
711 if ( !gi.FullPath( mapsname, sizeof(mapsname), "maps.lst" ) )
712 { // no maps.lst.
713 // note: originally this was looked for in "./data1/" only
714 // hope it is ok to use FullPath() search path.
715 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
716 return;
717 }
718 if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
719 {
720 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
721 return;
722 }
723
724 length = g_filelength( fp );
725 buffer = malloc( length + 1 );
726 szr = fread( buffer, length, 1, fp );
727 buffer[length] = 0;
728
729 s = buffer;
730
731 i = 0;
732 while ( i < length )
733 {
734 if ( s[i] == '\r' )
735 nummaps++;
736 i++;
737 }
738
739 mapnames = malloc( sizeof( char * ) * ( nummaps + 1 ) );
740 memset( mapnames, 0, sizeof( char * ) * ( nummaps + 1 ) );
741
742 s = buffer;
743
744 for ( i = 0; i < nummaps; i++ )
745 {
746 char shortname[MAX_TOKEN_CHARS];
747 char longname[MAX_TOKEN_CHARS];
748 char scratch[200];
749 #if defined WIN32_VARIANT
750 int j;
751 #endif
752 int l;
753
754 strcpy( shortname, COM_Parse( &s ) );
755 l = strlen(shortname);
756 #if defined WIN32_VARIANT
757 for (j=0 ; j<l ; j++)
758 shortname[j] = toupper(shortname[j]);
759 #endif
760 strcpy( longname, COM_Parse( &s ) );
761 Com_sprintf( scratch, sizeof( scratch ), "%s", shortname );
762
763 mapnames[i] = malloc( strlen( scratch ) + 1 );
764 strcpy( mapnames[i], scratch );
765 }
766 mapnames[nummaps] = 0;
767
768 fclose(fp);
769 free( buffer );
770
771 //find map, goto next map - if one doesn't exist, repeat list
772 //stick something in here to filter out CTF, and just make it loop back
773 for (i = 0; i < nummaps; i++) {
774 if (Q_strcasecmp(mapnames[i], level.mapname) == 0) {
775 if(mapnames[i+1])
776 {
777 if(mapnames[i+1][0])
778 BeginIntermission (CreateTargetChangeLevel (mapnames[i+1]) );
779 else if(mapnames[0][0]) //no more maps, repeat list
780 BeginIntermission (CreateTargetChangeLevel (mapnames[0]) );
781 }
782 else
783 BeginIntermission (CreateTargetChangeLevel (mapnames[0]) );
784 }
785 }
786
787 if (level.nextmap[0]) // go to a specific map
788 BeginIntermission (CreateTargetChangeLevel (level.nextmap) );
789 else { // search for a changelevel
790 ent = G_Find (NULL, FOFS(classname), "target_changelevel");
791 if (!ent)
792 { // the map designer didn't include a changelevel,
793 // so create a fake ent that goes back to the same level
794 BeginIntermission (CreateTargetChangeLevel (level.mapname) );
795 return;
796 }
797 BeginIntermission (ent);
798 }
799 }
800
801
802 /*
803 =================
804 CheckNeedPass
805 =================
806 */
CheckNeedPass(void)807 void CheckNeedPass (void)
808 {
809 int need;
810
811 // if password or spectator_password has changed, update needpass
812 // as needed
813 if (password->modified || spectator_password->modified)
814 {
815 password->modified = spectator_password->modified = false;
816
817 need = 0;
818
819 if (*password->string && Q_strcasecmp(password->string, "none"))
820 need |= 1;
821 if (*spectator_password->string && Q_strcasecmp(spectator_password->string, "none"))
822 need |= 2;
823
824 gi.cvar_set("needpass", va("%d", need));
825 }
826 }
827 /*
828 =================
829 ResetLevel
830 =================
831 */
ResetLevel(qboolean keepscores)832 void ResetLevel (qboolean keepscores) //for resetting players and items after warmup
833 {
834 int i, backup_score = 0; //initialize to shut gcc up
835 edict_t *ent;
836 gitem_t *item;
837
838 for (i=0 ; i<g_maxclients->value ; i++)
839 {
840 ent = g_edicts + i + 1;
841 if (!ent->inuse || ent->client->resp.spectator)
842 continue;
843 if (keepscores)
844 backup_score = ent->client->resp.score;
845 // locate ent at a spawn point and reset everything
846 InitClientResp (ent->client);
847 if(ent->is_bot)
848 {
849 // respawn bots after warmup
850 ACESP_PutClientInServer( ent, true );
851 }
852 else {
853 if(ent->deadflag)
854 DeathcamRemove (ent, "off");
855 PutClientInServer (ent);
856 }
857 ent->client->homing_shots = 0;
858 if (keepscores)
859 ent->client->resp.score = backup_score;
860 }
861
862 ACESP_SaveBots(); // update bots.tmp and client bot information
863
864 blue_team_score = 0;
865 red_team_score = 0;
866
867 if(g_tactical->integer)
868 {
869 tacticalScore.alienAmmoDepot =
870 tacticalScore.alienComputer =
871 tacticalScore.alienPowerSource =
872 tacticalScore.alienBackupGen =
873 tacticalScore.humanAmmoDepot =
874 tacticalScore.humanComputer =
875 tacticalScore.humanPowerSource =
876 tacticalScore.humanBackupGen =
877 true;
878 }
879
880 mindEraserTime = level.time;
881
882 //reset level items
883 for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++) {
884
885 if (!ent->inuse)
886 continue;
887 if(ent->client) //not players
888 continue;
889
890 //only items, not triggers, trains, etc
891 for (i=0,item=itemlist ; i<game.num_items ; i++,item++)
892 {
893 if (!item->classname)
894 continue;
895
896 if (!strcmp(item->classname, ent->classname))
897 { // found it
898 DoRespawn(ent);
899 break;
900 }
901 }
902
903 if (!strcmp(ent->classname, "misc_bluenode") || !strcmp(ent->classname, "misc_rednode"))
904 ent->powered = true;
905 if (!strcmp(ent->classname, "misc_redspidernode"))
906 {
907 gi.unlinkentity (ent);
908 SP_misc_redspidernode (ent);
909 }
910 if (!strcmp(ent->classname, "misc_bluespidernode"))
911 {
912 gi.unlinkentity (ent);
913 SP_misc_bluespidernode (ent);
914 }
915 }
916
917 if(g_callvote->integer)
918 safe_bprintf(PRINT_HIGH, "Call voting is ^2ENABLED\n");
919 else
920 safe_bprintf(PRINT_HIGH, "Call voting is ^1DISABLED\n");
921
922 if(g_antilag->integer)
923 safe_bprintf(PRINT_HIGH, "Antilag is ^2ENABLED\n");
924 else
925 safe_bprintf(PRINT_HIGH, "Antilag is ^1DISABLED\n");
926
927 return;
928 }
929
930 /*
931 =================
932 CheckDMRules
933 =================
934 */
CheckDMRules(void)935 void CheckDMRules (void)
936 {
937 int i, top_score;
938 gclient_t *cl;
939 edict_t *cl_ent;
940 float countdown_time;
941 static int warmup_state = 0;
942
943 gi.cvar_set ("g_teamgame", va("%d", TEAM_GAME));
944
945 if ( !g_tactical->integer && !tca->integer && !ctf->integer && !cp->integer && !(dmflags->integer & DF_SKINTEAMS) )
946 {
947 /*--- non-team game warmup ---*/
948 /*
949 * note: broadcast sounds require first client to have sound info
950 * whether or not first client is inuse (bot or not, also not important)
951 */
952 if ( (warmup_state == 0) && (level.time <= warmuptime->value) )
953 { /* start warmup countdown */
954 countdown_time = warmuptime->value - level.time;
955 warmup_state = (int)(ceil( countdown_time ));
956 }
957
958 if ( warmup_state > 0 )
959 { /* warmup in progress */
960 countdown_time = warmuptime->value - level.time;
961 if ( (float)warmup_state > ceil( countdown_time ) )
962 { /* next state of countdown */
963 --warmup_state;
964 switch ( warmup_state )
965 {
966 case 3:
967 gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/three.wav" ), 1, ATTN_NONE, 0 );
968 break;
969 case 2:
970 gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/two.wav" ), 1, ATTN_NONE, 0 );
971 break;
972 case 1:
973 gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/one.wav" ), 1, ATTN_NONE, 0 );
974 break;
975 case 0:
976 gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/fight.wav" ), 1, ATTN_NONE, 0 );
977 break;
978 default:
979 break;
980 }
981 if ( warmup_state > 0 )
982 {
983 for ( i = 1; i <= g_maxclients->integer; i++ )
984 {
985 safe_centerprintf( &g_edicts[i], "%i...\n", warmup_state );
986 }
987 }
988 else
989 { /* end of warmup */
990 for ( i = 1; i <= g_maxclients->integer; i++ )
991 {
992 safe_centerprintf( &g_edicts[i], "FIGHT!\n" );
993 }
994 ResetLevel(false);
995 }
996 }
997 }
998 }
999
1000 if (level.intermissiontime)
1001 return;
1002
1003 if (!deathmatch->integer)
1004 return;
1005
1006 if (timelimit->value)
1007 {
1008 if (level.time >= timelimit->value*60.0 && ((tca->integer || ctf->integer || cp->integer || (dmflags->integer & DF_SKINTEAMS)) || level.time > warmuptime->value))
1009 {
1010 safe_bprintf (PRINT_HIGH, "Timelimit hit.\n");
1011 EndDMLevel ();
1012 return;
1013 }
1014 }
1015
1016 if (fraglimit->integer && ((tca->integer || ctf->integer || cp->integer || (dmflags->integer & DF_SKINTEAMS)) || level.time > warmuptime->value))
1017 {
1018 //team scores
1019 if ((dmflags->integer & DF_SKINTEAMS) || ctf->integer || cp->integer) //it's all about the team!
1020 {
1021 if(blue_team_score >= fraglimit->integer)
1022 {
1023 safe_bprintf(PRINT_HIGH, "Blue Team wins!\n");
1024 bot_won = 0; //we don't care if it's a bot that wins
1025 EndDMLevel();
1026 return;
1027 }
1028 if(red_team_score >= fraglimit->integer)
1029 {
1030 safe_bprintf(PRINT_HIGH, "Red Team wins!\n");
1031 bot_won = 0; //we don't care if it's a bot that wins
1032 EndDMLevel();
1033 return;
1034 }
1035 }
1036 else
1037 {
1038 top_score = 0;
1039 for (i=0 ; i<g_maxclients->integer ; i++)
1040 {
1041 cl = game.clients + i;
1042 if (!g_edicts[i+1].inuse)
1043 continue;
1044
1045 if(cl->resp.score > top_score)
1046 top_score = cl->resp.score; //grab the top score
1047
1048 if (cl->resp.score >= fraglimit->integer)
1049 {
1050 if(cl->is_bot)
1051 {
1052 bot_won = 1; //a bot has won the match
1053 safe_bprintf (PRINT_HIGH, "Fraglimit hit by bot.\n");
1054 }
1055 else
1056 {
1057 bot_won = 0;
1058 safe_bprintf (PRINT_HIGH, "Fraglimit hit.\n");
1059 }
1060
1061 EndDMLevel ();
1062 return;
1063 }
1064 }
1065 if(!tca->integer && !ctf->integer && !cp->integer)
1066 {
1067 i = fraglimit->integer - top_score;
1068 switch(i)
1069 {
1070 case 3:
1071 if(!print3)
1072 {
1073 for (i=0 ; i<g_maxclients->integer ; i++)
1074 {
1075 cl_ent = g_edicts + 1 + i;
1076 if (!cl_ent->inuse || cl_ent->is_bot)
1077 continue;
1078 if(i == 0)
1079 gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/3frags.wav"), 1, ATTN_NONE, 0);
1080 safe_centerprintf(cl_ent, "3 frags remain!\n");
1081 }
1082 print3 = true;
1083 }
1084 break;
1085 case 2:
1086 if(!print2)
1087 {
1088 for (i=0 ; i<g_maxclients->integer ; i++)
1089 {
1090 cl_ent = g_edicts + 1 + i;
1091 if (!cl_ent->inuse || cl_ent->is_bot)
1092 continue;
1093 if(i == 0)
1094 gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/2frags.wav"), 1, ATTN_NONE, 0);
1095 safe_centerprintf(cl_ent, "2 frags remain!\n");
1096 }
1097 print2 = true;
1098 }
1099 break;
1100 case 1:
1101 if(!print1)
1102 {
1103 for (i=0 ; i<g_maxclients->integer ; i++)
1104 {
1105 cl_ent = g_edicts + 1 + i;
1106 if (!cl_ent->inuse || cl_ent->is_bot)
1107 continue;
1108 if(i == 0)
1109 gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/1frags.wav"), 1, ATTN_NONE, 0);
1110 safe_centerprintf(cl_ent, "1 frag remains!\n");
1111 }
1112 print1 = true;
1113 }
1114 break;
1115 default:
1116 break;
1117 }
1118 }
1119 }
1120 }
1121
1122 if(tca->integer)
1123 {
1124 if(red_team_matches == 2)
1125 {
1126 safe_bprintf(PRINT_HIGH, "Red Team wins!\n");
1127 bot_won = 0; //we don't care if it's a bot that wins
1128 EndDMLevel();
1129 return;
1130 }
1131 if(blue_team_matches == 2)
1132 {
1133 safe_bprintf(PRINT_HIGH, "Blue Team wins!\n");
1134 bot_won = 0; //we don't care if it's a bot that wins
1135 EndDMLevel();
1136 return;
1137 }
1138 if (blue_team_score == 0)
1139 {
1140 safe_bprintf(PRINT_HIGH, "Red Team wins match %d out of 3!\n", red_team_matches);
1141 bot_won = 0;
1142 ResetLevel(true);
1143 blue_team_score = red_team_score = 4;
1144 return;
1145 }
1146 if (red_team_score == 0)
1147 {
1148 safe_bprintf(PRINT_HIGH, "Blue Team wins match %d out of 3!\n", blue_team_matches);
1149 bot_won = 0;
1150 ResetLevel(true);
1151 blue_team_score = red_team_score = 4;
1152 return;
1153 }
1154 }
1155
1156 if(g_tactical->integer)
1157 {
1158 if(!tacticalScore.alienAmmoDepot && !tacticalScore.alienComputer && !tacticalScore.alienPowerSource)
1159 {
1160 safe_bprintf(PRINT_HIGH, "The Humans have defeated the Aliens!\n");
1161 bot_won = 0; //we don't care if it's a bot that wins
1162 EndDMLevel();
1163 return;
1164 }
1165 if(!tacticalScore.humanAmmoDepot && !tacticalScore.humanComputer && !tacticalScore.humanPowerSource)
1166 {
1167 safe_bprintf(PRINT_HIGH, "The Aliens have defeated the Humans!\n");
1168 bot_won = 0; //we don't care if it's a bot that wins
1169 EndDMLevel();
1170 return;
1171 }
1172 }
1173 }
1174
1175 /*
1176 =============
1177 ExitLevel
1178 =============
1179 */
ExitLevel(void)1180 void ExitLevel (void)
1181 {
1182 int i;
1183 edict_t *ent;
1184 char command [256];
1185
1186 if(strcmp(level.mapname, level.changemap) || timelimit->value || g_tactical->value)
1187 {
1188 Com_sprintf( command, sizeof(command), "map \"%s\"\n", level.changemap );
1189 gi.AddCommandString( command );
1190 }
1191
1192 //Note-- whenever the map command fails (for instance, misspelled bsp name
1193 //in the server cfg,) it will just play another game on the same map. As
1194 //of right here in the code, there is no way to detect if that is going to
1195 //happen, so we initialize another game on this map just in case. This
1196 //fixes many longstanding bugs where misspelled map names in the cfg would
1197 //cause servers to glitch out.
1198 // -Max
1199
1200 level.changemap = NULL;
1201 level.exitintermission = 0;
1202 level.intermissiontime = 0;
1203 ClientEndServerFrames ();
1204 EndIntermission();
1205
1206 // clear some things before going to next level
1207 for (i=0 ; i<g_maxclients->integer ; i++)
1208 {
1209 ent = g_edicts + 1 + i;
1210 if (!ent->inuse)
1211 continue;
1212 if (ent->health > ent->client->pers.max_health)
1213 ent->health = ent->client->pers.max_health;
1214
1215 ent->client->resp.score = 0;
1216 ent->client->resp.deaths = 0;
1217 ent->client->resp.reward_pts = 0;
1218 ent->client->homing_shots = 0;
1219 ent->takedamage = DAMAGE_AIM;
1220 ent->solid = SOLID_BBOX;
1221 ent->deadflag = DEAD_NO;
1222 if(ent->is_bot)
1223 {
1224 ACESP_PutClientInServer( ent, true );
1225 }
1226 else
1227 {
1228 PutClientInServer (ent);
1229 }
1230 if(g_duel->integer)
1231 {
1232 ClientPlaceInQueue(ent);
1233 ClientCheckQueue(ent);
1234 }
1235 }
1236 for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++)
1237 {
1238
1239 if (!ent->inuse || ent->client)
1240 continue;
1241 //remove podiums
1242 if(!strcmp(ent->classname, "pad"))
1243 G_FreeEdict(ent);
1244 if(tca->integer)
1245 {
1246 if(strstr(ent->classname, "spidernode"))
1247 ED_CallSpawn (ent);
1248 ent->powered = true;
1249 }
1250 }
1251 if(tca->integer)
1252 {
1253 blue_team_score = red_team_score = 4;
1254 red_team_matches = blue_team_matches = 0;
1255 }
1256 else
1257 {
1258 red_team_score = 0;
1259 blue_team_score = 0;
1260 }
1261
1262 if(g_tactical->integer)
1263 {
1264 tacticalScore.alienAmmoDepot =
1265 tacticalScore.alienComputer =
1266 tacticalScore.alienPowerSource =
1267 tacticalScore.alienBackupGen =
1268 tacticalScore.humanAmmoDepot =
1269 tacticalScore.humanComputer =
1270 tacticalScore.humanPowerSource =
1271 tacticalScore.humanBackupGen =
1272 true;
1273
1274 tacticalScore.alienAmmoDepotHealth =
1275 tacticalScore.alienComputerHealth =
1276 tacticalScore.alienPowerSourceHealth =
1277 tacticalScore.humanAmmoDepotHealth =
1278 tacticalScore.humanComputerHealth =
1279 tacticalScore.humanPowerSourceHealth =
1280 100;
1281 }
1282
1283 print1 = print2 = print3 = false;
1284
1285 }
1286
1287 /*
1288 ================
1289 G_Ban
1290
1291 Ban player
1292 ================
1293 */
1294 /*
1295 =================
1296 SV_WriteIP_f
1297 =================
1298 */
G_Ban(char * ip)1299 void G_Ban (char *ip)
1300 {
1301 FILE *f;
1302 char name[MAX_OSPATH];
1303 cvar_t *game;
1304 int i;
1305
1306 //add to banlist file
1307 game = gi.cvar("game", "", 0);
1308
1309 if (!*game->string)
1310 sprintf (name, "%s/listip.cfg", GAMEVERSION);
1311 else
1312 sprintf (name, "%s/listip.cfg", game->string);
1313
1314 safe_cprintf (NULL, PRINT_HIGH, "Writing %s.\n", name);
1315
1316 f = fopen (name, "ab");
1317 if (!f)
1318 {
1319 safe_cprintf (NULL, PRINT_HIGH, "Couldn't open %s\n", name);
1320 return;
1321 }
1322
1323 fprintf (f, "sv addip %s\n", ip);
1324
1325 fclose (f);
1326
1327 //add to current ban list
1328 for (i=0 ; i<numipfilters ; i++)
1329 if (ipfilters[i].compare == 0xffffffff)
1330 break; // free spot
1331 if (i == numipfilters)
1332 {
1333 if (numipfilters == MAX_IPFILTERS)
1334 {
1335 safe_cprintf (NULL, PRINT_HIGH, "IP filter list is full\n");
1336 return;
1337 }
1338 numipfilters++;
1339 }
1340
1341 if (!StringToFilter (ip, &ipfilters[i]))
1342 ipfilters[i].compare = 0xffffffff;
1343 }
1344
1345 /*
1346 ================
1347 G_NameMatch
1348
1349 A name entered in the console may (unlikely) or may not have color codes.
1350 This is a case-sensitive match, because a case-insensitive match would
1351 complicate matters.
1352
1353 Used by sv removebot <name> and callvote kicks
1354
1355 ================
1356 */
G_NameMatch(const char * netname,const char * kickname)1357 qboolean G_NameMatch( const char* netname, const char *kickname )
1358 {
1359 const char* p_netname = netname;
1360 const char* p_kickname = kickname;
1361 int char_count = 16;
1362 int len_count = 49; // maximum length of 16 char color name (16 * 3 + nul)
1363 qboolean matched = false;
1364
1365 while ( len_count > 0 && char_count > 0)
1366 {
1367 if ( *p_netname != *p_kickname )
1368 {
1369 if ( Q_IsColorString( p_netname ) )
1370 { // netname has color code, kickname does not, match still possible
1371 p_netname += 2;
1372 len_count -= 2;
1373 }
1374 else if ( Q_IsColorString( p_kickname ))
1375 { // kickname has color code, netname does not, match still possible
1376 p_kickname += 2;
1377 len_count -= 2;
1378 }
1379 else
1380 { // no match. mismatched chars, or end of one or the other
1381 break;
1382 }
1383 }
1384 else if ( !*p_netname && !*p_kickname )
1385 { // end of both strings,
1386 matched = true;
1387 break;
1388 }
1389 else
1390 { // chars matched
1391 if ( Q_IsColorString( p_netname ) && Q_IsColorString( p_kickname ) )
1392 { // color does not have to match
1393 p_netname += 2;
1394 p_kickname += 2;
1395 len_count -= 2;
1396 }
1397 else
1398 {
1399 ++p_netname;
1400 ++p_kickname;
1401 --len_count;
1402 --char_count;
1403 }
1404 }
1405 }
1406
1407 return matched;
1408 }
1409
1410 /*
1411 ================
1412 G_ParseVoteCommand
1413
1414 Parse and execute command
1415 ================
1416 */
1417 // helper function
find_kick_target(const char * name)1418 static edict_t* find_kick_target( const char *name )
1419 {
1420 edict_t* kick_target = NULL;
1421 edict_t* ent;
1422 int i;
1423
1424 for ( i = 1 ; i <= g_maxclients->integer ; i++ )
1425 {
1426 ent = &g_edicts[i];
1427 if ( !ent->inuse )
1428 continue;
1429 if ( ent->client )
1430 {
1431 if ( G_NameMatch( ent->client->pers.netname, name ) )
1432 {
1433 kick_target = ent;
1434 break;
1435 }
1436 }
1437 }
1438
1439 return kick_target;
1440 }
1441
G_ParseVoteCommand(void)1442 void G_ParseVoteCommand (void)
1443 {
1444 int i, j;
1445 char command[128];
1446 char args[128];
1447 edict_t *ent;
1448 qboolean donearg = false;
1449 char *value;
1450
1451 //separate command from args
1452 i = j = 0;
1453 while(i < 128) {
1454
1455 if(playervote.command[i] == ' ')
1456 donearg = true;
1457
1458 if(!donearg)
1459 command[i] = playervote.command[i];
1460 else
1461 command[i] = 0;
1462
1463 if(donearg && i < 127) { //skip the space between command and arg
1464 args[j] = playervote.command[i+1];
1465 j++;
1466 }
1467 i++;
1468 }
1469
1470 if ( !strcmp(command, "kick") )
1471 { // kick player or bot
1472 ent = find_kick_target( args );
1473 if ( ent )
1474 {
1475 if ( ent->is_bot )
1476 {
1477 if ( sv_botkickthreshold && sv_botkickthreshold->integer )
1478 {
1479 safe_bprintf(PRINT_HIGH,
1480 "Auto bot kick enabled, callvote kick <bot> disabled.\n%s not kicked.\n",
1481 ent->client->pers.netname);
1482 }
1483 else
1484 {
1485 ACESP_KickBot( ent );
1486 }
1487 }
1488 else
1489 {
1490 safe_bprintf(PRINT_HIGH, "%s was kicked\n", args);
1491 ClientDisconnect (ent);
1492 }
1493 }
1494 else
1495 {
1496 safe_bprintf( PRINT_HIGH, "Did not find %s here.\n", args );
1497 }
1498 }
1499 else if ( !strcmp( command, "kickban") )
1500 { // kickban player
1501 ent = find_kick_target( args );
1502 if ( ent )
1503 {
1504 if ( ent->is_bot )
1505 { //
1506 safe_bprintf(PRINT_HIGH,
1507 "%s is a bot, use \"kick\", not \"kickban.\"\n", ent->client->pers.netname);
1508 }
1509 else
1510 {
1511 safe_bprintf(PRINT_HIGH, "%s was kickbanned\n", args);
1512 ClientDisconnect (ent);
1513 value = Info_ValueForKey (ent->client->pers.userinfo, "ip");
1514 G_Ban(value);
1515 }
1516 }
1517 else
1518 {
1519 safe_bprintf( PRINT_HIGH, "Did not find %s here.\n", args );
1520 }
1521 }
1522 else if(!strcmp(command, "fraglimit")) { //change fraglimit
1523 gi.cvar_set("fraglimit", args);
1524 safe_bprintf(PRINT_HIGH, "Fraglimit changed to %s\n", args);
1525 }
1526 else if(!strcmp(command, "timelimit")) { //change timelimit
1527 gi.cvar_set("timelimit", args);
1528 safe_bprintf(PRINT_HIGH, "Timelimit changed to %s\n", args);
1529 }
1530 else if(!strcmp(command, "map")) { //change map
1531 Com_sprintf (command, sizeof(command), "map \"%s\"\n", args);
1532 gi.AddCommandString (command);
1533 }
1534 else
1535 safe_bprintf(PRINT_HIGH, "Invalid command!");
1536
1537 }
1538
1539
1540 /*
1541 ================
1542 G_RunFrame
1543
1544 Advances the world by 0.1 seconds
1545 ================
1546 */
1547 extern void ACEND_DrawPath(void);
G_RunFrame(void)1548 void G_RunFrame (void)
1549 {
1550 int i, numActiveClients = 0;
1551 edict_t *ent;
1552
1553 level.previousTime = gi.Sys_Milliseconds() - 100;
1554
1555 level.framenum++;
1556 level.time = level.framenum*FRAMETIME;
1557
1558 /*
1559 * update bot info in first client always, and in other active clients
1560 */
1561 ACESP_UpdateBots();
1562
1563 // choose a client for monsters to target this frame
1564 AI_SetSightClient ();
1565
1566 // exit intermissions
1567
1568 if (level.exitintermission)
1569 {
1570 ExitLevel ();
1571 return;
1572 }
1573
1574 //
1575 // treat each object in turn
1576 // even the world gets a chance to think
1577 //
1578
1579 ent = &g_edicts[0];
1580 for (i=0 ; i<globals.num_edicts ; i++, ent++)
1581 {
1582 if (!ent->inuse)
1583 continue;
1584
1585 level.current_entity = ent;
1586
1587 VectorCopy (ent->s.origin, ent->s.old_origin);
1588
1589 // if the ground entity moved, make sure we are still on it
1590 if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
1591 {
1592 ent->groundentity = NULL;
1593 if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) )
1594 {
1595 M_CheckGround (ent);
1596 }
1597 }
1598
1599 if (i > 0 && i <= g_maxclients->integer)
1600 {
1601 ClientBeginServerFrame (ent);
1602 }
1603
1604 if(ent->inuse && ent->client && !ent->is_bot)
1605 {
1606 if ( ent->s.number <= g_maxclients->integer )
1607 { // count actual players, not deathcam entities
1608 numActiveClients++;
1609 }
1610 }
1611
1612 //this next block of code may not be practical for a server running at 10fps
1613 /* if(ent->movetype & MOVETYPE_FLYMISSILE) {
1614 //unlagged
1615 if ( g_antilag->integer)
1616 G_TimeShiftAllClients( level.previousTime, NULL );
1617
1618 G_RunEntity (ent);
1619
1620 //unlagged
1621 if ( g_antilag->integer)
1622 G_UnTimeShiftAllClients( NULL );
1623 }
1624 else*/
1625 G_RunEntity (ent, FRAMETIME);
1626 }
1627
1628 // see if it is time to end a deathmatch
1629 CheckDMRules ();
1630
1631 // see if needpass needs updated
1632 CheckNeedPass ();
1633
1634 // build the playerstate_t structures for all players
1635 ClientEndServerFrames ();
1636
1637 // For bot debugging
1638 ACEND_DrawPath();
1639
1640 //unlagged
1641 if ( g_antilag->integer)
1642 level.frameStartTime = level.time;
1643
1644 //call voting
1645 if(g_callvote->integer && playervote.called) {
1646
1647 playervote.time = level.time;
1648 if(playervote.time-playervote.starttime > 15 ){ //15 seconds
1649 //execute command if votes are sufficient
1650 if(numActiveClients <= 2 && playervote.yay > playervote.nay+1) { //minimum of 2 votes to pass
1651 safe_bprintf(PRINT_HIGH, "Vote ^2Passed\n");
1652
1653 //parse command(we will allow kick, map, fraglimit, timelimit).
1654 G_ParseVoteCommand();
1655
1656 }
1657 else if(playervote.yay > 2 && playervote.yay > playervote.nay+1) { //3-1 minimum to pass
1658 safe_bprintf(PRINT_HIGH, "Vote ^2Passed\n");
1659
1660 //parse command(we will allow kick, map, fraglimit, timelimit).
1661 G_ParseVoteCommand();
1662
1663 }
1664 else
1665 safe_bprintf(PRINT_HIGH, "Vote ^1Failed\n");
1666
1667 //clear
1668 playervote.called = false;
1669 playervote.yay = playervote.nay = 0;
1670 playervote.command[0] = 0;
1671
1672 //do each ent
1673 for (i=0 ; i<g_maxclients->integer ; i++)
1674 {
1675 ent = g_edicts + 1 + i;
1676 if (!ent->inuse || ent->is_bot)
1677 continue;
1678 ent->client->resp.voted = false;
1679 }
1680 }
1681 }
1682 }
1683
1684