~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/game/g_main.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
7
Quake III Arena source code is free software; you can redistribute it
 
8
and/or modify it under the terms of the GNU General Public License as
 
9
published by the Free Software Foundation; either version 2 of the License,
 
10
or (at your option) any later version.
 
11
 
 
12
Quake III Arena source code is distributed in the hope that it will be
 
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with Quake III Arena source code; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
===========================================================================
 
21
*/
 
22
//
 
23
 
 
24
#include "g_local.h"
 
25
 
 
26
level_locals_t  level;
 
27
 
 
28
typedef struct {
 
29
        vmCvar_t        *vmCvar;
 
30
        char            *cvarName;
 
31
        char            *defaultString;
 
32
        int                     cvarFlags;
 
33
        int                     modificationCount;  // for tracking changes
 
34
        qboolean        trackChange;        // track this variable, and announce if changed
 
35
  qboolean teamShader;        // track and if changed, update shader state
 
36
} cvarTable_t;
 
37
 
 
38
gentity_t               g_entities[MAX_GENTITIES];
 
39
gclient_t               g_clients[MAX_CLIENTS];
 
40
 
 
41
vmCvar_t        g_gametype;
 
42
vmCvar_t        g_dmflags;
 
43
vmCvar_t        g_fraglimit;
 
44
vmCvar_t        g_timelimit;
 
45
vmCvar_t        g_capturelimit;
 
46
vmCvar_t        g_friendlyFire;
 
47
vmCvar_t        g_password;
 
48
vmCvar_t        g_needpass;
 
49
vmCvar_t        g_maxclients;
 
50
vmCvar_t        g_maxGameClients;
 
51
vmCvar_t        g_dedicated;
 
52
vmCvar_t        g_speed;
 
53
vmCvar_t        g_gravity;
 
54
vmCvar_t        g_cheats;
 
55
vmCvar_t        g_knockback;
 
56
vmCvar_t        g_quadfactor;
 
57
vmCvar_t        g_forcerespawn;
 
58
vmCvar_t        g_inactivity;
 
59
vmCvar_t        g_debugMove;
 
60
vmCvar_t        g_debugDamage;
 
61
vmCvar_t        g_debugAlloc;
 
62
vmCvar_t        g_weaponRespawn;
 
63
vmCvar_t        g_weaponTeamRespawn;
 
64
vmCvar_t        g_motd;
 
65
vmCvar_t        g_synchronousClients;
 
66
vmCvar_t        g_warmup;
 
67
vmCvar_t        g_doWarmup;
 
68
vmCvar_t        g_restarted;
 
69
vmCvar_t        g_logfile;
 
70
vmCvar_t        g_logfileSync;
 
71
vmCvar_t        g_blood;
 
72
vmCvar_t        g_podiumDist;
 
73
vmCvar_t        g_podiumDrop;
 
74
vmCvar_t        g_allowVote;
 
75
vmCvar_t        g_teamAutoJoin;
 
76
vmCvar_t        g_teamForceBalance;
 
77
vmCvar_t        g_banIPs;
 
78
vmCvar_t        g_filterBan;
 
79
vmCvar_t        g_smoothClients;
 
80
vmCvar_t        pmove_fixed;
 
81
vmCvar_t        pmove_msec;
 
82
vmCvar_t        g_rankings;
 
83
vmCvar_t        g_listEntity;
 
84
#ifdef MISSIONPACK
 
85
vmCvar_t        g_obeliskHealth;
 
86
vmCvar_t        g_obeliskRegenPeriod;
 
87
vmCvar_t        g_obeliskRegenAmount;
 
88
vmCvar_t        g_obeliskRespawnDelay;
 
89
vmCvar_t        g_cubeTimeout;
 
90
vmCvar_t        g_redteam;
 
91
vmCvar_t        g_blueteam;
 
92
vmCvar_t        g_singlePlayer;
 
93
vmCvar_t        g_enableDust;
 
94
vmCvar_t        g_enableBreath;
 
95
vmCvar_t        g_proxMineTimeout;
 
96
#endif
 
97
 
 
98
// bk001129 - made static to avoid aliasing
 
99
static cvarTable_t              gameCvarTable[] = {
 
100
        // don't override the cheat state set by the system
 
101
        { &g_cheats, "sv_cheats", "", 0, 0, qfalse },
 
102
 
 
103
        // noset vars
 
104
        { NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
 
105
        { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse  },
 
106
        { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse  },
 
107
        { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
 
108
 
 
109
        // latched vars
 
110
        { &g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse  },
 
111
 
 
112
        { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
 
113
        { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
 
114
 
 
115
        // change anytime vars
 
116
        { &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
 
117
        { &g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
 
118
        { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
 
119
        { &g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
 
120
 
 
121
        { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse  },
 
122
 
 
123
        { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue  },
 
124
 
 
125
        { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE  },
 
126
        { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE  },
 
127
 
 
128
        { &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue  },
 
129
        { &g_doWarmup, "g_doWarmup", "0", 0, 0, qtrue  },
 
130
        { &g_logfile, "g_logfile", "games.log", CVAR_ARCHIVE, 0, qfalse  },
 
131
        { &g_logfileSync, "g_logfileSync", "0", CVAR_ARCHIVE, 0, qfalse  },
 
132
 
 
133
        { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse  },
 
134
 
 
135
        { &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse  },
 
136
        { &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse  },
 
137
 
 
138
        { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
 
139
 
 
140
        { &g_dedicated, "dedicated", "0", 0, 0, qfalse  },
 
141
 
 
142
        { &g_speed, "g_speed", "320", 0, 0, qtrue  },
 
143
        { &g_gravity, "g_gravity", "800", 0, 0, qtrue  },
 
144
        { &g_knockback, "g_knockback", "1000", 0, 0, qtrue  },
 
145
        { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue  },
 
146
        { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue  },
 
147
        { &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue },
 
148
        { &g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue },
 
149
        { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
 
150
        { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
 
151
        { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
 
152
        { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse },
 
153
        { &g_motd, "g_motd", "", 0, 0, qfalse },
 
154
        { &g_blood, "com_blood", "1", 0, 0, qfalse },
 
155
 
 
156
        { &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse },
 
157
        { &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse },
 
158
 
 
159
        { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },
 
160
        { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse },
 
161
 
 
162
#ifdef MISSIONPACK
 
163
        { &g_obeliskHealth, "g_obeliskHealth", "2500", 0, 0, qfalse },
 
164
        { &g_obeliskRegenPeriod, "g_obeliskRegenPeriod", "1", 0, 0, qfalse },
 
165
        { &g_obeliskRegenAmount, "g_obeliskRegenAmount", "15", 0, 0, qfalse },
 
166
        { &g_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO, 0, qfalse },
 
167
 
 
168
        { &g_cubeTimeout, "g_cubeTimeout", "30", 0, 0, qfalse },
 
169
        { &g_redteam, "g_redteam", "Stroggs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue },
 
170
        { &g_blueteam, "g_blueteam", "Pagans", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue  },
 
171
        { &g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse  },
 
172
 
 
173
        { &g_enableDust, "g_enableDust", "0", CVAR_SERVERINFO, 0, qtrue, qfalse },
 
174
        { &g_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO, 0, qtrue, qfalse },
 
175
        { &g_proxMineTimeout, "g_proxMineTimeout", "20000", 0, 0, qfalse },
 
176
#endif
 
177
        { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},
 
178
        { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse},
 
179
        { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse},
 
180
 
 
181
        { &g_rankings, "g_rankings", "0", 0, 0, qfalse}
 
182
 
 
183
};
 
184
 
 
185
// bk001129 - made static to avoid aliasing
 
186
static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[0] );
 
187
 
 
188
 
 
189
void G_InitGame( int levelTime, int randomSeed, int restart );
 
190
void G_RunFrame( int levelTime );
 
191
void G_ShutdownGame( int restart );
 
192
void CheckExitRules( void );
 
193
 
 
194
 
 
195
/*
 
196
================
 
197
vmMain
 
198
 
 
199
This is the only way control passes into the module.
 
200
This must be the very first function compiled into the .q3vm file
 
201
================
 
202
*/
 
203
intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11  ) {
 
204
        switch ( command ) {
 
205
        case GAME_INIT:
 
206
                G_InitGame( arg0, arg1, arg2 );
 
207
                return 0;
 
208
        case GAME_SHUTDOWN:
 
209
                G_ShutdownGame( arg0 );
 
210
                return 0;
 
211
        case GAME_CLIENT_CONNECT:
 
212
                return (intptr_t)ClientConnect( arg0, arg1, arg2 );
 
213
        case GAME_CLIENT_THINK:
 
214
                ClientThink( arg0 );
 
215
                return 0;
 
216
        case GAME_CLIENT_USERINFO_CHANGED:
 
217
                ClientUserinfoChanged( arg0 );
 
218
                return 0;
 
219
        case GAME_CLIENT_DISCONNECT:
 
220
                ClientDisconnect( arg0 );
 
221
                return 0;
 
222
        case GAME_CLIENT_BEGIN:
 
223
                ClientBegin( arg0 );
 
224
                return 0;
 
225
        case GAME_CLIENT_COMMAND:
 
226
                ClientCommand( arg0 );
 
227
                return 0;
 
228
        case GAME_RUN_FRAME:
 
229
                G_RunFrame( arg0 );
 
230
                return 0;
 
231
        case GAME_CONSOLE_COMMAND:
 
232
                return ConsoleCommand();
 
233
        case BOTAI_START_FRAME:
 
234
                return BotAIStartFrame( arg0 );
 
235
        }
 
236
 
 
237
        return -1;
 
238
}
 
239
 
 
240
 
 
241
void QDECL G_Printf( const char *fmt, ... ) {
 
242
        va_list         argptr;
 
243
        char            text[1024];
 
244
 
 
245
        va_start (argptr, fmt);
 
246
        vsprintf (text, fmt, argptr);
 
247
        va_end (argptr);
 
248
 
 
249
        trap_Printf( text );
 
250
}
 
251
 
 
252
void QDECL G_Error( const char *fmt, ... ) {
 
253
        va_list         argptr;
 
254
        char            text[1024];
 
255
 
 
256
        va_start (argptr, fmt);
 
257
        vsprintf (text, fmt, argptr);
 
258
        va_end (argptr);
 
259
 
 
260
        trap_Error( text );
 
261
}
 
262
 
 
263
/*
 
264
================
 
265
G_FindTeams
 
266
 
 
267
Chain together all entities with a matching team field.
 
268
Entity teams are used for item groups and multi-entity mover groups.
 
269
 
 
270
All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
 
271
All but the last will have the teamchain field set to the next one
 
272
================
 
273
*/
 
274
void G_FindTeams( void ) {
 
275
        gentity_t       *e, *e2;
 
276
        int             i, j;
 
277
        int             c, c2;
 
278
 
 
279
        c = 0;
 
280
        c2 = 0;
 
281
        for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){
 
282
                if (!e->inuse)
 
283
                        continue;
 
284
                if (!e->team)
 
285
                        continue;
 
286
                if (e->flags & FL_TEAMSLAVE)
 
287
                        continue;
 
288
                e->teammaster = e;
 
289
                c++;
 
290
                c2++;
 
291
                for (j=i+1, e2=e+1 ; j < level.num_entities ; j++,e2++)
 
292
                {
 
293
                        if (!e2->inuse)
 
294
                                continue;
 
295
                        if (!e2->team)
 
296
                                continue;
 
297
                        if (e2->flags & FL_TEAMSLAVE)
 
298
                                continue;
 
299
                        if (!strcmp(e->team, e2->team))
 
300
                        {
 
301
                                c2++;
 
302
                                e2->teamchain = e->teamchain;
 
303
                                e->teamchain = e2;
 
304
                                e2->teammaster = e;
 
305
                                e2->flags |= FL_TEAMSLAVE;
 
306
 
 
307
                                // make sure that targets only point at the master
 
308
                                if ( e2->targetname ) {
 
309
                                        e->targetname = e2->targetname;
 
310
                                        e2->targetname = NULL;
 
311
                                }
 
312
                        }
 
313
                }
 
314
        }
 
315
 
 
316
        G_Printf ("%i teams with %i entities\n", c, c2);
 
317
}
 
318
 
 
319
void G_RemapTeamShaders( void ) {
 
320
#ifdef MISSIONPACK
 
321
        char string[1024];
 
322
        float f = level.time * 0.001;
 
323
        Com_sprintf( string, sizeof(string), "team_icon/%s_red", g_redteam.string );
 
324
        AddRemap("textures/ctf2/redteam01", string, f); 
 
325
        AddRemap("textures/ctf2/redteam02", string, f); 
 
326
        Com_sprintf( string, sizeof(string), "team_icon/%s_blue", g_blueteam.string );
 
327
        AddRemap("textures/ctf2/blueteam01", string, f); 
 
328
        AddRemap("textures/ctf2/blueteam02", string, f); 
 
329
        trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
 
330
#endif
 
331
}
 
332
 
 
333
 
 
334
/*
 
335
=================
 
336
G_RegisterCvars
 
337
=================
 
338
*/
 
339
void G_RegisterCvars( void ) {
 
340
        int                     i;
 
341
        cvarTable_t     *cv;
 
342
        qboolean remapped = qfalse;
 
343
 
 
344
        for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
 
345
                trap_Cvar_Register( cv->vmCvar, cv->cvarName,
 
346
                        cv->defaultString, cv->cvarFlags );
 
347
                if ( cv->vmCvar )
 
348
                        cv->modificationCount = cv->vmCvar->modificationCount;
 
349
 
 
350
                if (cv->teamShader) {
 
351
                        remapped = qtrue;
 
352
                }
 
353
        }
 
354
 
 
355
        if (remapped) {
 
356
                G_RemapTeamShaders();
 
357
        }
 
358
 
 
359
        // check some things
 
360
        if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
 
361
                G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer );
 
362
                trap_Cvar_Set( "g_gametype", "0" );
 
363
        }
 
364
 
 
365
        level.warmupModificationCount = g_warmup.modificationCount;
 
366
}
 
367
 
 
368
/*
 
369
=================
 
370
G_UpdateCvars
 
371
=================
 
372
*/
 
373
void G_UpdateCvars( void ) {
 
374
        int                     i;
 
375
        cvarTable_t     *cv;
 
376
        qboolean remapped = qfalse;
 
377
 
 
378
        for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
 
379
                if ( cv->vmCvar ) {
 
380
                        trap_Cvar_Update( cv->vmCvar );
 
381
 
 
382
                        if ( cv->modificationCount != cv->vmCvar->modificationCount ) {
 
383
                                cv->modificationCount = cv->vmCvar->modificationCount;
 
384
 
 
385
                                if ( cv->trackChange ) {
 
386
                                        trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"", 
 
387
                                                cv->cvarName, cv->vmCvar->string ) );
 
388
                                }
 
389
 
 
390
                                if (cv->teamShader) {
 
391
                                        remapped = qtrue;
 
392
                                }
 
393
                        }
 
394
                }
 
395
        }
 
396
 
 
397
        if (remapped) {
 
398
                G_RemapTeamShaders();
 
399
        }
 
400
}
 
401
 
 
402
/*
 
403
============
 
404
G_InitGame
 
405
 
 
406
============
 
407
*/
 
408
void G_InitGame( int levelTime, int randomSeed, int restart ) {
 
409
        int                                     i;
 
410
 
 
411
        G_Printf ("------- Game Initialization -------\n");
 
412
        G_Printf ("gamename: %s\n", GAMEVERSION);
 
413
        G_Printf ("gamedate: %s\n", __DATE__);
 
414
 
 
415
        srand( randomSeed );
 
416
 
 
417
        G_RegisterCvars();
 
418
 
 
419
        G_ProcessIPBans();
 
420
 
 
421
        G_InitMemory();
 
422
 
 
423
        // set some level globals
 
424
        memset( &level, 0, sizeof( level ) );
 
425
        level.time = levelTime;
 
426
        level.startTime = levelTime;
 
427
 
 
428
        level.snd_fry = G_SoundIndex("sound/player/fry.wav");   // FIXME standing in lava / slime
 
429
 
 
430
        if ( g_gametype.integer != GT_SINGLE_PLAYER && g_logfile.string[0] ) {
 
431
                if ( g_logfileSync.integer ) {
 
432
                        trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND_SYNC );
 
433
                } else {
 
434
                        trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND );
 
435
                }
 
436
                if ( !level.logFile ) {
 
437
                        G_Printf( "WARNING: Couldn't open logfile: %s\n", g_logfile.string );
 
438
                } else {
 
439
                        char    serverinfo[MAX_INFO_STRING];
 
440
 
 
441
                        trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
 
442
 
 
443
                        G_LogPrintf("------------------------------------------------------------\n" );
 
444
                        G_LogPrintf("InitGame: %s\n", serverinfo );
 
445
                }
 
446
        } else {
 
447
                G_Printf( "Not logging to disk.\n" );
 
448
        }
 
449
 
 
450
        G_InitWorldSession();
 
451
 
 
452
        // initialize all entities for this game
 
453
        memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
 
454
        level.gentities = g_entities;
 
455
 
 
456
        // initialize all clients for this game
 
457
        level.maxclients = g_maxclients.integer;
 
458
        memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) );
 
459
        level.clients = g_clients;
 
460
 
 
461
        // set client fields on player ents
 
462
        for ( i=0 ; i<level.maxclients ; i++ ) {
 
463
                g_entities[i].client = level.clients + i;
 
464
        }
 
465
 
 
466
        // always leave room for the max number of clients,
 
467
        // even if they aren't all used, so numbers inside that
 
468
        // range are NEVER anything but clients
 
469
        level.num_entities = MAX_CLIENTS;
 
470
 
 
471
        // let the server system know where the entites are
 
472
        trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), 
 
473
                &level.clients[0].ps, sizeof( level.clients[0] ) );
 
474
 
 
475
        // reserve some spots for dead player bodies
 
476
        InitBodyQue();
 
477
 
 
478
        ClearRegisteredItems();
 
479
 
 
480
        // parse the key/value pairs and spawn gentities
 
481
        G_SpawnEntitiesFromString();
 
482
 
 
483
        // general initialization
 
484
        G_FindTeams();
 
485
 
 
486
        // make sure we have flags for CTF, etc
 
487
        if( g_gametype.integer >= GT_TEAM ) {
 
488
                G_CheckTeamItems();
 
489
        }
 
490
 
 
491
        SaveRegisteredItems();
 
492
 
 
493
        G_Printf ("-----------------------------------\n");
 
494
 
 
495
        if( g_gametype.integer == GT_SINGLE_PLAYER || trap_Cvar_VariableIntegerValue( "com_buildScript" ) ) {
 
496
                G_ModelIndex( SP_PODIUM_MODEL );
 
497
                G_SoundIndex( "sound/player/gurp1.wav" );
 
498
                G_SoundIndex( "sound/player/gurp2.wav" );
 
499
        }
 
500
 
 
501
        if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
 
502
                BotAISetup( restart );
 
503
                BotAILoadMap( restart );
 
504
                G_InitBots( restart );
 
505
        }
 
506
 
 
507
        G_RemapTeamShaders();
 
508
 
 
509
}
 
510
 
 
511
 
 
512
 
 
513
/*
 
514
=================
 
515
G_ShutdownGame
 
516
=================
 
517
*/
 
518
void G_ShutdownGame( int restart ) {
 
519
        G_Printf ("==== ShutdownGame ====\n");
 
520
 
 
521
        if ( level.logFile ) {
 
522
                G_LogPrintf("ShutdownGame:\n" );
 
523
                G_LogPrintf("------------------------------------------------------------\n" );
 
524
                trap_FS_FCloseFile( level.logFile );
 
525
        }
 
526
 
 
527
        // write all the client session data so we can get it back
 
528
        G_WriteSessionData();
 
529
 
 
530
        if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
 
531
                BotAIShutdown( restart );
 
532
        }
 
533
}
 
534
 
 
535
 
 
536
 
 
537
//===================================================================
 
538
 
 
539
void QDECL Com_Error ( int level, const char *error, ... ) {
 
540
        va_list         argptr;
 
541
        char            text[1024];
 
542
 
 
543
        va_start (argptr, error);
 
544
        vsprintf (text, error, argptr);
 
545
        va_end (argptr);
 
546
 
 
547
        G_Error( "%s", text);
 
548
}
 
549
 
 
550
void QDECL Com_Printf( const char *msg, ... ) {
 
551
        va_list         argptr;
 
552
        char            text[1024];
 
553
 
 
554
        va_start (argptr, msg);
 
555
        vsprintf (text, msg, argptr);
 
556
        va_end (argptr);
 
557
 
 
558
        G_Printf ("%s", text);
 
559
}
 
560
 
 
561
/*
 
562
========================================================================
 
563
 
 
564
PLAYER COUNTING / SCORE SORTING
 
565
 
 
566
========================================================================
 
567
*/
 
568
 
 
569
/*
 
570
=============
 
571
AddTournamentPlayer
 
572
 
 
573
If there are less than two tournament players, put a
 
574
spectator in the game and restart
 
575
=============
 
576
*/
 
577
void AddTournamentPlayer( void ) {
 
578
        int                     i;
 
579
        gclient_t       *client;
 
580
        gclient_t       *nextInLine;
 
581
 
 
582
        if ( level.numPlayingClients >= 2 ) {
 
583
                return;
 
584
        }
 
585
 
 
586
        // never change during intermission
 
587
        if ( level.intermissiontime ) {
 
588
                return;
 
589
        }
 
590
 
 
591
        nextInLine = NULL;
 
592
 
 
593
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
594
                client = &level.clients[i];
 
595
                if ( client->pers.connected != CON_CONNECTED ) {
 
596
                        continue;
 
597
                }
 
598
                if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
 
599
                        continue;
 
600
                }
 
601
                // never select the dedicated follow or scoreboard clients
 
602
                if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD || 
 
603
                        client->sess.spectatorClient < 0  ) {
 
604
                        continue;
 
605
                }
 
606
 
 
607
                if ( !nextInLine || client->sess.spectatorTime < nextInLine->sess.spectatorTime ) {
 
608
                        nextInLine = client;
 
609
                }
 
610
        }
 
611
 
 
612
        if ( !nextInLine ) {
 
613
                return;
 
614
        }
 
615
 
 
616
        level.warmupTime = -1;
 
617
 
 
618
        // set them to free-for-all team
 
619
        SetTeam( &g_entities[ nextInLine - level.clients ], "f" );
 
620
}
 
621
 
 
622
/*
 
623
=======================
 
624
RemoveTournamentLoser
 
625
 
 
626
Make the loser a spectator at the back of the line
 
627
=======================
 
628
*/
 
629
void RemoveTournamentLoser( void ) {
 
630
        int                     clientNum;
 
631
 
 
632
        if ( level.numPlayingClients != 2 ) {
 
633
                return;
 
634
        }
 
635
 
 
636
        clientNum = level.sortedClients[1];
 
637
 
 
638
        if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) {
 
639
                return;
 
640
        }
 
641
 
 
642
        // make them a spectator
 
643
        SetTeam( &g_entities[ clientNum ], "s" );
 
644
}
 
645
 
 
646
/*
 
647
=======================
 
648
RemoveTournamentWinner
 
649
=======================
 
650
*/
 
651
void RemoveTournamentWinner( void ) {
 
652
        int                     clientNum;
 
653
 
 
654
        if ( level.numPlayingClients != 2 ) {
 
655
                return;
 
656
        }
 
657
 
 
658
        clientNum = level.sortedClients[0];
 
659
 
 
660
        if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) {
 
661
                return;
 
662
        }
 
663
 
 
664
        // make them a spectator
 
665
        SetTeam( &g_entities[ clientNum ], "s" );
 
666
}
 
667
 
 
668
/*
 
669
=======================
 
670
AdjustTournamentScores
 
671
=======================
 
672
*/
 
673
void AdjustTournamentScores( void ) {
 
674
        int                     clientNum;
 
675
 
 
676
        clientNum = level.sortedClients[0];
 
677
        if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
 
678
                level.clients[ clientNum ].sess.wins++;
 
679
                ClientUserinfoChanged( clientNum );
 
680
        }
 
681
 
 
682
        clientNum = level.sortedClients[1];
 
683
        if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
 
684
                level.clients[ clientNum ].sess.losses++;
 
685
                ClientUserinfoChanged( clientNum );
 
686
        }
 
687
 
 
688
}
 
689
 
 
690
/*
 
691
=============
 
692
SortRanks
 
693
 
 
694
=============
 
695
*/
 
696
int QDECL SortRanks( const void *a, const void *b ) {
 
697
        gclient_t       *ca, *cb;
 
698
 
 
699
        ca = &level.clients[*(int *)a];
 
700
        cb = &level.clients[*(int *)b];
 
701
 
 
702
        // sort special clients last
 
703
        if ( ca->sess.spectatorState == SPECTATOR_SCOREBOARD || ca->sess.spectatorClient < 0 ) {
 
704
                return 1;
 
705
        }
 
706
        if ( cb->sess.spectatorState == SPECTATOR_SCOREBOARD || cb->sess.spectatorClient < 0  ) {
 
707
                return -1;
 
708
        }
 
709
 
 
710
        // then connecting clients
 
711
        if ( ca->pers.connected == CON_CONNECTING ) {
 
712
                return 1;
 
713
        }
 
714
        if ( cb->pers.connected == CON_CONNECTING ) {
 
715
                return -1;
 
716
        }
 
717
 
 
718
 
 
719
        // then spectators
 
720
        if ( ca->sess.sessionTeam == TEAM_SPECTATOR && cb->sess.sessionTeam == TEAM_SPECTATOR ) {
 
721
                if ( ca->sess.spectatorTime < cb->sess.spectatorTime ) {
 
722
                        return -1;
 
723
                }
 
724
                if ( ca->sess.spectatorTime > cb->sess.spectatorTime ) {
 
725
                        return 1;
 
726
                }
 
727
                return 0;
 
728
        }
 
729
        if ( ca->sess.sessionTeam == TEAM_SPECTATOR ) {
 
730
                return 1;
 
731
        }
 
732
        if ( cb->sess.sessionTeam == TEAM_SPECTATOR ) {
 
733
                return -1;
 
734
        }
 
735
 
 
736
        // then sort by score
 
737
        if ( ca->ps.persistant[PERS_SCORE]
 
738
                > cb->ps.persistant[PERS_SCORE] ) {
 
739
                return -1;
 
740
        }
 
741
        if ( ca->ps.persistant[PERS_SCORE]
 
742
                < cb->ps.persistant[PERS_SCORE] ) {
 
743
                return 1;
 
744
        }
 
745
        return 0;
 
746
}
 
747
 
 
748
/*
 
749
============
 
750
CalculateRanks
 
751
 
 
752
Recalculates the score ranks of all players
 
753
This will be called on every client connect, begin, disconnect, death,
 
754
and team change.
 
755
============
 
756
*/
 
757
void CalculateRanks( void ) {
 
758
        int             i;
 
759
        int             rank;
 
760
        int             score;
 
761
        int             newScore;
 
762
        gclient_t       *cl;
 
763
 
 
764
        level.follow1 = -1;
 
765
        level.follow2 = -1;
 
766
        level.numConnectedClients = 0;
 
767
        level.numNonSpectatorClients = 0;
 
768
        level.numPlayingClients = 0;
 
769
        level.numVotingClients = 0;             // don't count bots
 
770
        for ( i = 0; i < TEAM_NUM_TEAMS; i++ ) {
 
771
                level.numteamVotingClients[i] = 0;
 
772
        }
 
773
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
774
                if ( level.clients[i].pers.connected != CON_DISCONNECTED ) {
 
775
                        level.sortedClients[level.numConnectedClients] = i;
 
776
                        level.numConnectedClients++;
 
777
 
 
778
                        if ( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR ) {
 
779
                                level.numNonSpectatorClients++;
 
780
                        
 
781
                                // decide if this should be auto-followed
 
782
                                if ( level.clients[i].pers.connected == CON_CONNECTED ) {
 
783
                                        level.numPlayingClients++;
 
784
                                        if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
 
785
                                                level.numVotingClients++;
 
786
                                                if ( level.clients[i].sess.sessionTeam == TEAM_RED )
 
787
                                                        level.numteamVotingClients[0]++;
 
788
                                                else if ( level.clients[i].sess.sessionTeam == TEAM_BLUE )
 
789
                                                        level.numteamVotingClients[1]++;
 
790
                                        }
 
791
                                        if ( level.follow1 == -1 ) {
 
792
                                                level.follow1 = i;
 
793
                                        } else if ( level.follow2 == -1 ) {
 
794
                                                level.follow2 = i;
 
795
                                        }
 
796
                                }
 
797
                        }
 
798
                }
 
799
        }
 
800
 
 
801
        qsort( level.sortedClients, level.numConnectedClients, 
 
802
                sizeof(level.sortedClients[0]), SortRanks );
 
803
 
 
804
        // set the rank value for all clients that are connected and not spectators
 
805
        if ( g_gametype.integer >= GT_TEAM ) {
 
806
                // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied
 
807
                for ( i = 0;  i < level.numConnectedClients; i++ ) {
 
808
                        cl = &level.clients[ level.sortedClients[i] ];
 
809
                        if ( level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] ) {
 
810
                                cl->ps.persistant[PERS_RANK] = 2;
 
811
                        } else if ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) {
 
812
                                cl->ps.persistant[PERS_RANK] = 0;
 
813
                        } else {
 
814
                                cl->ps.persistant[PERS_RANK] = 1;
 
815
                        }
 
816
                }
 
817
        } else {        
 
818
                rank = -1;
 
819
                score = 0;
 
820
                for ( i = 0;  i < level.numPlayingClients; i++ ) {
 
821
                        cl = &level.clients[ level.sortedClients[i] ];
 
822
                        newScore = cl->ps.persistant[PERS_SCORE];
 
823
                        if ( i == 0 || newScore != score ) {
 
824
                                rank = i;
 
825
                                // assume we aren't tied until the next client is checked
 
826
                                level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank;
 
827
                        } else {
 
828
                                // we are tied with the previous client
 
829
                                level.clients[ level.sortedClients[i-1] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
 
830
                                level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
 
831
                        }
 
832
                        score = newScore;
 
833
                        if ( g_gametype.integer == GT_SINGLE_PLAYER && level.numPlayingClients == 1 ) {
 
834
                                level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
 
835
                        }
 
836
                }
 
837
        }
 
838
 
 
839
        // set the CS_SCORES1/2 configstrings, which will be visible to everyone
 
840
        if ( g_gametype.integer >= GT_TEAM ) {
 
841
                trap_SetConfigstring( CS_SCORES1, va("%i", level.teamScores[TEAM_RED] ) );
 
842
                trap_SetConfigstring( CS_SCORES2, va("%i", level.teamScores[TEAM_BLUE] ) );
 
843
        } else {
 
844
                if ( level.numConnectedClients == 0 ) {
 
845
                        trap_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) );
 
846
                        trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
 
847
                } else if ( level.numConnectedClients == 1 ) {
 
848
                        trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
 
849
                        trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
 
850
                } else {
 
851
                        trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
 
852
                        trap_SetConfigstring( CS_SCORES2, va("%i", level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] ) );
 
853
                }
 
854
        }
 
855
 
 
856
        // see if it is time to end the level
 
857
        CheckExitRules();
 
858
 
 
859
        // if we are at the intermission, send the new info to everyone
 
860
        if ( level.intermissiontime ) {
 
861
                SendScoreboardMessageToAllClients();
 
862
        }
 
863
}
 
864
 
 
865
 
 
866
/*
 
867
========================================================================
 
868
 
 
869
MAP CHANGING
 
870
 
 
871
========================================================================
 
872
*/
 
873
 
 
874
/*
 
875
========================
 
876
SendScoreboardMessageToAllClients
 
877
 
 
878
Do this at BeginIntermission time and whenever ranks are recalculated
 
879
due to enters/exits/forced team changes
 
880
========================
 
881
*/
 
882
void SendScoreboardMessageToAllClients( void ) {
 
883
        int             i;
 
884
 
 
885
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
886
                if ( level.clients[ i ].pers.connected == CON_CONNECTED ) {
 
887
                        DeathmatchScoreboardMessage( g_entities + i );
 
888
                }
 
889
        }
 
890
}
 
891
 
 
892
/*
 
893
========================
 
894
MoveClientToIntermission
 
895
 
 
896
When the intermission starts, this will be called for all players.
 
897
If a new client connects, this will be called after the spawn function.
 
898
========================
 
899
*/
 
900
void MoveClientToIntermission( gentity_t *ent ) {
 
901
        // take out of follow mode if needed
 
902
        if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
 
903
                StopFollowing( ent );
 
904
        }
 
905
 
 
906
 
 
907
        // move to the spot
 
908
        VectorCopy( level.intermission_origin, ent->s.origin );
 
909
        VectorCopy( level.intermission_origin, ent->client->ps.origin );
 
910
        VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
 
911
        ent->client->ps.pm_type = PM_INTERMISSION;
 
912
 
 
913
        // clean up powerup info
 
914
        memset( ent->client->ps.powerups, 0, sizeof(ent->client->ps.powerups) );
 
915
 
 
916
        ent->client->ps.eFlags = 0;
 
917
        ent->s.eFlags = 0;
 
918
        ent->s.eType = ET_GENERAL;
 
919
        ent->s.modelindex = 0;
 
920
        ent->s.loopSound = 0;
 
921
        ent->s.event = 0;
 
922
        ent->r.contents = 0;
 
923
}
 
924
 
 
925
/*
 
926
==================
 
927
FindIntermissionPoint
 
928
 
 
929
This is also used for spectator spawns
 
930
==================
 
931
*/
 
932
void FindIntermissionPoint( void ) {
 
933
        gentity_t       *ent, *target;
 
934
        vec3_t          dir;
 
935
 
 
936
        // find the intermission spot
 
937
        ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
 
938
        if ( !ent ) {   // the map creator forgot to put in an intermission point...
 
939
                SelectSpawnPoint ( vec3_origin, level.intermission_origin, level.intermission_angle );
 
940
        } else {
 
941
                VectorCopy (ent->s.origin, level.intermission_origin);
 
942
                VectorCopy (ent->s.angles, level.intermission_angle);
 
943
                // if it has a target, look towards it
 
944
                if ( ent->target ) {
 
945
                        target = G_PickTarget( ent->target );
 
946
                        if ( target ) {
 
947
                                VectorSubtract( target->s.origin, level.intermission_origin, dir );
 
948
                                vectoangles( dir, level.intermission_angle );
 
949
                        }
 
950
                }
 
951
        }
 
952
 
 
953
}
 
954
 
 
955
/*
 
956
==================
 
957
BeginIntermission
 
958
==================
 
959
*/
 
960
void BeginIntermission( void ) {
 
961
        int                     i;
 
962
        gentity_t       *client;
 
963
 
 
964
        if ( level.intermissiontime ) {
 
965
                return;         // already active
 
966
        }
 
967
 
 
968
        // if in tournement mode, change the wins / losses
 
969
        if ( g_gametype.integer == GT_TOURNAMENT ) {
 
970
                AdjustTournamentScores();
 
971
        }
 
972
 
 
973
        level.intermissiontime = level.time;
 
974
        FindIntermissionPoint();
 
975
 
 
976
#ifdef MISSIONPACK
 
977
        if (g_singlePlayer.integer) {
 
978
                trap_Cvar_Set("ui_singlePlayerActive", "0");
 
979
                UpdateTournamentInfo();
 
980
        }
 
981
#else
 
982
        // if single player game
 
983
        if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
 
984
                UpdateTournamentInfo();
 
985
                SpawnModelsOnVictoryPads();
 
986
        }
 
987
#endif
 
988
 
 
989
        // move all clients to the intermission point
 
990
        for (i=0 ; i< level.maxclients ; i++) {
 
991
                client = g_entities + i;
 
992
                if (!client->inuse)
 
993
                        continue;
 
994
                // respawn if dead
 
995
                if (client->health <= 0) {
 
996
                        respawn(client);
 
997
                }
 
998
                MoveClientToIntermission( client );
 
999
        }
 
1000
 
 
1001
        // send the current scoring to all clients
 
1002
        SendScoreboardMessageToAllClients();
 
1003
 
 
1004
}
 
1005
 
 
1006
 
 
1007
/*
 
1008
=============
 
1009
ExitLevel
 
1010
 
 
1011
When the intermission has been exited, the server is either killed
 
1012
or moved to a new level based on the "nextmap" cvar 
 
1013
 
 
1014
=============
 
1015
*/
 
1016
void ExitLevel (void) {
 
1017
        int             i;
 
1018
        gclient_t *cl;
 
1019
        char nextmap[MAX_STRING_CHARS];
 
1020
        char d1[MAX_STRING_CHARS];
 
1021
 
 
1022
        //bot interbreeding
 
1023
        BotInterbreedEndMatch();
 
1024
 
 
1025
        // if we are running a tournement map, kick the loser to spectator status,
 
1026
        // which will automatically grab the next spectator and restart
 
1027
        if ( g_gametype.integer == GT_TOURNAMENT  ) {
 
1028
                if ( !level.restarted ) {
 
1029
                        RemoveTournamentLoser();
 
1030
                        trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
 
1031
                        level.restarted = qtrue;
 
1032
                        level.changemap = NULL;
 
1033
                        level.intermissiontime = 0;
 
1034
                }
 
1035
                return; 
 
1036
        }
 
1037
 
 
1038
        trap_Cvar_VariableStringBuffer( "nextmap", nextmap, sizeof(nextmap) );
 
1039
        trap_Cvar_VariableStringBuffer( "d1", d1, sizeof(d1) );
 
1040
 
 
1041
        if( !Q_stricmp( nextmap, "map_restart 0" ) && Q_stricmp( d1, "" ) ) {
 
1042
                trap_Cvar_Set( "nextmap", "vstr d2" );
 
1043
                trap_SendConsoleCommand( EXEC_APPEND, "vstr d1\n" );
 
1044
        } else {
 
1045
                trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
 
1046
        }
 
1047
 
 
1048
        level.changemap = NULL;
 
1049
        level.intermissiontime = 0;
 
1050
 
 
1051
        // reset all the scores so we don't enter the intermission again
 
1052
        level.teamScores[TEAM_RED] = 0;
 
1053
        level.teamScores[TEAM_BLUE] = 0;
 
1054
        for ( i=0 ; i< g_maxclients.integer ; i++ ) {
 
1055
                cl = level.clients + i;
 
1056
                if ( cl->pers.connected != CON_CONNECTED ) {
 
1057
                        continue;
 
1058
                }
 
1059
                cl->ps.persistant[PERS_SCORE] = 0;
 
1060
        }
 
1061
 
 
1062
        // we need to do this here before chaning to CON_CONNECTING
 
1063
        G_WriteSessionData();
 
1064
 
 
1065
        // change all client states to connecting, so the early players into the
 
1066
        // next level will know the others aren't done reconnecting
 
1067
        for (i=0 ; i< g_maxclients.integer ; i++) {
 
1068
                if ( level.clients[i].pers.connected == CON_CONNECTED ) {
 
1069
                        level.clients[i].pers.connected = CON_CONNECTING;
 
1070
                }
 
1071
        }
 
1072
 
 
1073
}
 
1074
 
 
1075
/*
 
1076
=================
 
1077
G_LogPrintf
 
1078
 
 
1079
Print to the logfile with a time stamp if it is open
 
1080
=================
 
1081
*/
 
1082
void QDECL G_LogPrintf( const char *fmt, ... ) {
 
1083
        va_list         argptr;
 
1084
        char            string[1024];
 
1085
        int                     min, tens, sec;
 
1086
 
 
1087
        sec = level.time / 1000;
 
1088
 
 
1089
        min = sec / 60;
 
1090
        sec -= min * 60;
 
1091
        tens = sec / 10;
 
1092
        sec -= tens * 10;
 
1093
 
 
1094
        Com_sprintf( string, sizeof(string), "%3i:%i%i ", min, tens, sec );
 
1095
 
 
1096
        va_start( argptr, fmt );
 
1097
        vsprintf( string +7 , fmt,argptr );
 
1098
        va_end( argptr );
 
1099
 
 
1100
        if ( g_dedicated.integer ) {
 
1101
                G_Printf( "%s", string + 7 );
 
1102
        }
 
1103
 
 
1104
        if ( !level.logFile ) {
 
1105
                return;
 
1106
        }
 
1107
 
 
1108
        trap_FS_Write( string, strlen( string ), level.logFile );
 
1109
}
 
1110
 
 
1111
/*
 
1112
================
 
1113
LogExit
 
1114
 
 
1115
Append information about this game to the log file
 
1116
================
 
1117
*/
 
1118
void LogExit( const char *string ) {
 
1119
        int                             i, numSorted;
 
1120
        gclient_t               *cl;
 
1121
#ifdef MISSIONPACK // bk001205
 
1122
        qboolean won = qtrue;
 
1123
#endif
 
1124
        G_LogPrintf( "Exit: %s\n", string );
 
1125
 
 
1126
        level.intermissionQueued = level.time;
 
1127
 
 
1128
        // this will keep the clients from playing any voice sounds
 
1129
        // that will get cut off when the queued intermission starts
 
1130
        trap_SetConfigstring( CS_INTERMISSION, "1" );
 
1131
 
 
1132
        // don't send more than 32 scores (FIXME?)
 
1133
        numSorted = level.numConnectedClients;
 
1134
        if ( numSorted > 32 ) {
 
1135
                numSorted = 32;
 
1136
        }
 
1137
 
 
1138
        if ( g_gametype.integer >= GT_TEAM ) {
 
1139
                G_LogPrintf( "red:%i  blue:%i\n",
 
1140
                        level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE] );
 
1141
        }
 
1142
 
 
1143
        for (i=0 ; i < numSorted ; i++) {
 
1144
                int             ping;
 
1145
 
 
1146
                cl = &level.clients[level.sortedClients[i]];
 
1147
 
 
1148
                if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) {
 
1149
                        continue;
 
1150
                }
 
1151
                if ( cl->pers.connected == CON_CONNECTING ) {
 
1152
                        continue;
 
1153
                }
 
1154
 
 
1155
                ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
 
1156
 
 
1157
                G_LogPrintf( "score: %i  ping: %i  client: %i %s\n", cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i],       cl->pers.netname );
 
1158
#ifdef MISSIONPACK
 
1159
                if (g_singlePlayer.integer && g_gametype.integer == GT_TOURNAMENT) {
 
1160
                        if (g_entities[cl - level.clients].r.svFlags & SVF_BOT && cl->ps.persistant[PERS_RANK] == 0) {
 
1161
                                won = qfalse;
 
1162
                        }
 
1163
                }
 
1164
#endif
 
1165
 
 
1166
        }
 
1167
 
 
1168
#ifdef MISSIONPACK
 
1169
        if (g_singlePlayer.integer) {
 
1170
                if (g_gametype.integer >= GT_CTF) {
 
1171
                        won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE];
 
1172
                }
 
1173
                trap_SendConsoleCommand( EXEC_APPEND, (won) ? "spWin\n" : "spLose\n" );
 
1174
        }
 
1175
#endif
 
1176
 
 
1177
 
 
1178
}
 
1179
 
 
1180
 
 
1181
/*
 
1182
=================
 
1183
CheckIntermissionExit
 
1184
 
 
1185
The level will stay at the intermission for a minimum of 5 seconds
 
1186
If all players wish to continue, the level will then exit.
 
1187
If one or more players have not acknowledged the continue, the game will
 
1188
wait 10 seconds before going on.
 
1189
=================
 
1190
*/
 
1191
void CheckIntermissionExit( void ) {
 
1192
        int                     ready, notReady;
 
1193
        int                     i;
 
1194
        gclient_t       *cl;
 
1195
        int                     readyMask;
 
1196
 
 
1197
        if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
 
1198
                return;
 
1199
        }
 
1200
 
 
1201
        // see which players are ready
 
1202
        ready = 0;
 
1203
        notReady = 0;
 
1204
        readyMask = 0;
 
1205
        for (i=0 ; i< g_maxclients.integer ; i++) {
 
1206
                cl = level.clients + i;
 
1207
                if ( cl->pers.connected != CON_CONNECTED ) {
 
1208
                        continue;
 
1209
                }
 
1210
                if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
 
1211
                        continue;
 
1212
                }
 
1213
 
 
1214
                if ( cl->readyToExit ) {
 
1215
                        ready++;
 
1216
                        if ( i < 16 ) {
 
1217
                                readyMask |= 1 << i;
 
1218
                        }
 
1219
                } else {
 
1220
                        notReady++;
 
1221
                }
 
1222
        }
 
1223
 
 
1224
        // copy the readyMask to each player's stats so
 
1225
        // it can be displayed on the scoreboard
 
1226
        for (i=0 ; i< g_maxclients.integer ; i++) {
 
1227
                cl = level.clients + i;
 
1228
                if ( cl->pers.connected != CON_CONNECTED ) {
 
1229
                        continue;
 
1230
                }
 
1231
                cl->ps.stats[STAT_CLIENTS_READY] = readyMask;
 
1232
        }
 
1233
 
 
1234
        // never exit in less than five seconds
 
1235
        if ( level.time < level.intermissiontime + 5000 ) {
 
1236
                return;
 
1237
        }
 
1238
 
 
1239
        // if nobody wants to go, clear timer
 
1240
        if ( !ready ) {
 
1241
                level.readyToExit = qfalse;
 
1242
                return;
 
1243
        }
 
1244
 
 
1245
        // if everyone wants to go, go now
 
1246
        if ( !notReady ) {
 
1247
                ExitLevel();
 
1248
                return;
 
1249
        }
 
1250
 
 
1251
        // the first person to ready starts the ten second timeout
 
1252
        if ( !level.readyToExit ) {
 
1253
                level.readyToExit = qtrue;
 
1254
                level.exitTime = level.time;
 
1255
        }
 
1256
 
 
1257
        // if we have waited ten seconds since at least one player
 
1258
        // wanted to exit, go ahead
 
1259
        if ( level.time < level.exitTime + 10000 ) {
 
1260
                return;
 
1261
        }
 
1262
 
 
1263
        ExitLevel();
 
1264
}
 
1265
 
 
1266
/*
 
1267
=============
 
1268
ScoreIsTied
 
1269
=============
 
1270
*/
 
1271
qboolean ScoreIsTied( void ) {
 
1272
        int             a, b;
 
1273
 
 
1274
        if ( level.numPlayingClients < 2 ) {
 
1275
                return qfalse;
 
1276
        }
 
1277
        
 
1278
        if ( g_gametype.integer >= GT_TEAM ) {
 
1279
                return level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE];
 
1280
        }
 
1281
 
 
1282
        a = level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE];
 
1283
        b = level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE];
 
1284
 
 
1285
        return a == b;
 
1286
}
 
1287
 
 
1288
/*
 
1289
=================
 
1290
CheckExitRules
 
1291
 
 
1292
There will be a delay between the time the exit is qualified for
 
1293
and the time everyone is moved to the intermission spot, so you
 
1294
can see the last frag.
 
1295
=================
 
1296
*/
 
1297
void CheckExitRules( void ) {
 
1298
        int                     i;
 
1299
        gclient_t       *cl;
 
1300
        // if at the intermission, wait for all non-bots to
 
1301
        // signal ready, then go to next level
 
1302
        if ( level.intermissiontime ) {
 
1303
                CheckIntermissionExit ();
 
1304
                return;
 
1305
        }
 
1306
 
 
1307
        if ( level.intermissionQueued ) {
 
1308
#ifdef MISSIONPACK
 
1309
                int time = (g_singlePlayer.integer) ? SP_INTERMISSION_DELAY_TIME : INTERMISSION_DELAY_TIME;
 
1310
                if ( level.time - level.intermissionQueued >= time ) {
 
1311
                        level.intermissionQueued = 0;
 
1312
                        BeginIntermission();
 
1313
                }
 
1314
#else
 
1315
                if ( level.time - level.intermissionQueued >= INTERMISSION_DELAY_TIME ) {
 
1316
                        level.intermissionQueued = 0;
 
1317
                        BeginIntermission();
 
1318
                }
 
1319
#endif
 
1320
                return;
 
1321
        }
 
1322
 
 
1323
        // check for sudden death
 
1324
        if ( ScoreIsTied() ) {
 
1325
                // always wait for sudden death
 
1326
                return;
 
1327
        }
 
1328
 
 
1329
        if ( g_timelimit.integer && !level.warmupTime ) {
 
1330
                if ( level.time - level.startTime >= g_timelimit.integer*60000 ) {
 
1331
                        trap_SendServerCommand( -1, "print \"Timelimit hit.\n\"");
 
1332
                        LogExit( "Timelimit hit." );
 
1333
                        return;
 
1334
                }
 
1335
        }
 
1336
 
 
1337
        if ( level.numPlayingClients < 2 ) {
 
1338
                return;
 
1339
        }
 
1340
 
 
1341
        if ( g_gametype.integer < GT_CTF && g_fraglimit.integer ) {
 
1342
                if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) {
 
1343
                        trap_SendServerCommand( -1, "print \"Red hit the fraglimit.\n\"" );
 
1344
                        LogExit( "Fraglimit hit." );
 
1345
                        return;
 
1346
                }
 
1347
 
 
1348
                if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer ) {
 
1349
                        trap_SendServerCommand( -1, "print \"Blue hit the fraglimit.\n\"" );
 
1350
                        LogExit( "Fraglimit hit." );
 
1351
                        return;
 
1352
                }
 
1353
 
 
1354
                for ( i=0 ; i< g_maxclients.integer ; i++ ) {
 
1355
                        cl = level.clients + i;
 
1356
                        if ( cl->pers.connected != CON_CONNECTED ) {
 
1357
                                continue;
 
1358
                        }
 
1359
                        if ( cl->sess.sessionTeam != TEAM_FREE ) {
 
1360
                                continue;
 
1361
                        }
 
1362
 
 
1363
                        if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) {
 
1364
                                LogExit( "Fraglimit hit." );
 
1365
                                trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " hit the fraglimit.\n\"",
 
1366
                                        cl->pers.netname ) );
 
1367
                                return;
 
1368
                        }
 
1369
                }
 
1370
        }
 
1371
 
 
1372
        if ( g_gametype.integer >= GT_CTF && g_capturelimit.integer ) {
 
1373
 
 
1374
                if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) {
 
1375
                        trap_SendServerCommand( -1, "print \"Red hit the capturelimit.\n\"" );
 
1376
                        LogExit( "Capturelimit hit." );
 
1377
                        return;
 
1378
                }
 
1379
 
 
1380
                if ( level.teamScores[TEAM_BLUE] >= g_capturelimit.integer ) {
 
1381
                        trap_SendServerCommand( -1, "print \"Blue hit the capturelimit.\n\"" );
 
1382
                        LogExit( "Capturelimit hit." );
 
1383
                        return;
 
1384
                }
 
1385
        }
 
1386
}
 
1387
 
 
1388
 
 
1389
 
 
1390
/*
 
1391
========================================================================
 
1392
 
 
1393
FUNCTIONS CALLED EVERY FRAME
 
1394
 
 
1395
========================================================================
 
1396
*/
 
1397
 
 
1398
 
 
1399
/*
 
1400
=============
 
1401
CheckTournament
 
1402
 
 
1403
Once a frame, check for changes in tournement player state
 
1404
=============
 
1405
*/
 
1406
void CheckTournament( void ) {
 
1407
        // check because we run 3 game frames before calling Connect and/or ClientBegin
 
1408
        // for clients on a map_restart
 
1409
        if ( level.numPlayingClients == 0 ) {
 
1410
                return;
 
1411
        }
 
1412
 
 
1413
        if ( g_gametype.integer == GT_TOURNAMENT ) {
 
1414
 
 
1415
                // pull in a spectator if needed
 
1416
                if ( level.numPlayingClients < 2 ) {
 
1417
                        AddTournamentPlayer();
 
1418
                }
 
1419
 
 
1420
                // if we don't have two players, go back to "waiting for players"
 
1421
                if ( level.numPlayingClients != 2 ) {
 
1422
                        if ( level.warmupTime != -1 ) {
 
1423
                                level.warmupTime = -1;
 
1424
                                trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
 
1425
                                G_LogPrintf( "Warmup:\n" );
 
1426
                        }
 
1427
                        return;
 
1428
                }
 
1429
 
 
1430
                if ( level.warmupTime == 0 ) {
 
1431
                        return;
 
1432
                }
 
1433
 
 
1434
                // if the warmup is changed at the console, restart it
 
1435
                if ( g_warmup.modificationCount != level.warmupModificationCount ) {
 
1436
                        level.warmupModificationCount = g_warmup.modificationCount;
 
1437
                        level.warmupTime = -1;
 
1438
                }
 
1439
 
 
1440
                // if all players have arrived, start the countdown
 
1441
                if ( level.warmupTime < 0 ) {
 
1442
                        if ( level.numPlayingClients == 2 ) {
 
1443
                                // fudge by -1 to account for extra delays
 
1444
                                if ( g_warmup.integer > 1 ) {
 
1445
                                        level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
 
1446
                                } else {
 
1447
                                        level.warmupTime = 0;
 
1448
                                }
 
1449
 
 
1450
                                trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
 
1451
                        }
 
1452
                        return;
 
1453
                }
 
1454
 
 
1455
                // if the warmup time has counted down, restart
 
1456
                if ( level.time > level.warmupTime ) {
 
1457
                        level.warmupTime += 10000;
 
1458
                        trap_Cvar_Set( "g_restarted", "1" );
 
1459
                        trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
 
1460
                        level.restarted = qtrue;
 
1461
                        return;
 
1462
                }
 
1463
        } else if ( g_gametype.integer != GT_SINGLE_PLAYER && level.warmupTime != 0 ) {
 
1464
                int             counts[TEAM_NUM_TEAMS];
 
1465
                qboolean        notEnough = qfalse;
 
1466
 
 
1467
                if ( g_gametype.integer > GT_TEAM ) {
 
1468
                        counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE );
 
1469
                        counts[TEAM_RED] = TeamCount( -1, TEAM_RED );
 
1470
 
 
1471
                        if (counts[TEAM_RED] < 1 || counts[TEAM_BLUE] < 1) {
 
1472
                                notEnough = qtrue;
 
1473
                        }
 
1474
                } else if ( level.numPlayingClients < 2 ) {
 
1475
                        notEnough = qtrue;
 
1476
                }
 
1477
 
 
1478
                if ( notEnough ) {
 
1479
                        if ( level.warmupTime != -1 ) {
 
1480
                                level.warmupTime = -1;
 
1481
                                trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
 
1482
                                G_LogPrintf( "Warmup:\n" );
 
1483
                        }
 
1484
                        return; // still waiting for team members
 
1485
                }
 
1486
 
 
1487
                if ( level.warmupTime == 0 ) {
 
1488
                        return;
 
1489
                }
 
1490
 
 
1491
                // if the warmup is changed at the console, restart it
 
1492
                if ( g_warmup.modificationCount != level.warmupModificationCount ) {
 
1493
                        level.warmupModificationCount = g_warmup.modificationCount;
 
1494
                        level.warmupTime = -1;
 
1495
                }
 
1496
 
 
1497
                // if all players have arrived, start the countdown
 
1498
                if ( level.warmupTime < 0 ) {
 
1499
                        // fudge by -1 to account for extra delays
 
1500
                        level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
 
1501
                        trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
 
1502
                        return;
 
1503
                }
 
1504
 
 
1505
                // if the warmup time has counted down, restart
 
1506
                if ( level.time > level.warmupTime ) {
 
1507
                        level.warmupTime += 10000;
 
1508
                        trap_Cvar_Set( "g_restarted", "1" );
 
1509
                        trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
 
1510
                        level.restarted = qtrue;
 
1511
                        return;
 
1512
                }
 
1513
        }
 
1514
}
 
1515
 
 
1516
 
 
1517
/*
 
1518
==================
 
1519
CheckVote
 
1520
==================
 
1521
*/
 
1522
void CheckVote( void ) {
 
1523
        if ( level.voteExecuteTime && level.voteExecuteTime < level.time ) {
 
1524
                level.voteExecuteTime = 0;
 
1525
                trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
 
1526
        }
 
1527
        if ( !level.voteTime ) {
 
1528
                return;
 
1529
        }
 
1530
        if ( level.time - level.voteTime >= VOTE_TIME ) {
 
1531
                trap_SendServerCommand( -1, "print \"Vote failed.\n\"" );
 
1532
        } else {
 
1533
                // ATVI Q3 1.32 Patch #9, WNF
 
1534
                if ( level.voteYes > level.numVotingClients/2 ) {
 
1535
                        // execute the command, then remove the vote
 
1536
                        trap_SendServerCommand( -1, "print \"Vote passed.\n\"" );
 
1537
                        level.voteExecuteTime = level.time + 3000;
 
1538
                } else if ( level.voteNo >= level.numVotingClients/2 ) {
 
1539
                        // same behavior as a timeout
 
1540
                        trap_SendServerCommand( -1, "print \"Vote failed.\n\"" );
 
1541
                } else {
 
1542
                        // still waiting for a majority
 
1543
                        return;
 
1544
                }
 
1545
        }
 
1546
        level.voteTime = 0;
 
1547
        trap_SetConfigstring( CS_VOTE_TIME, "" );
 
1548
 
 
1549
}
 
1550
 
 
1551
/*
 
1552
==================
 
1553
PrintTeam
 
1554
==================
 
1555
*/
 
1556
void PrintTeam(int team, char *message) {
 
1557
        int i;
 
1558
 
 
1559
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
1560
                if (level.clients[i].sess.sessionTeam != team)
 
1561
                        continue;
 
1562
                trap_SendServerCommand( i, message );
 
1563
        }
 
1564
}
 
1565
 
 
1566
/*
 
1567
==================
 
1568
SetLeader
 
1569
==================
 
1570
*/
 
1571
void SetLeader(int team, int client) {
 
1572
        int i;
 
1573
 
 
1574
        if ( level.clients[client].pers.connected == CON_DISCONNECTED ) {
 
1575
                PrintTeam(team, va("print \"%s is not connected\n\"", level.clients[client].pers.netname) );
 
1576
                return;
 
1577
        }
 
1578
        if (level.clients[client].sess.sessionTeam != team) {
 
1579
                PrintTeam(team, va("print \"%s is not on the team anymore\n\"", level.clients[client].pers.netname) );
 
1580
                return;
 
1581
        }
 
1582
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
1583
                if (level.clients[i].sess.sessionTeam != team)
 
1584
                        continue;
 
1585
                if (level.clients[i].sess.teamLeader) {
 
1586
                        level.clients[i].sess.teamLeader = qfalse;
 
1587
                        ClientUserinfoChanged(i);
 
1588
                }
 
1589
        }
 
1590
        level.clients[client].sess.teamLeader = qtrue;
 
1591
        ClientUserinfoChanged( client );
 
1592
        PrintTeam(team, va("print \"%s is the new team leader\n\"", level.clients[client].pers.netname) );
 
1593
}
 
1594
 
 
1595
/*
 
1596
==================
 
1597
CheckTeamLeader
 
1598
==================
 
1599
*/
 
1600
void CheckTeamLeader( int team ) {
 
1601
        int i;
 
1602
 
 
1603
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
1604
                if (level.clients[i].sess.sessionTeam != team)
 
1605
                        continue;
 
1606
                if (level.clients[i].sess.teamLeader)
 
1607
                        break;
 
1608
        }
 
1609
        if (i >= level.maxclients) {
 
1610
                for ( i = 0 ; i < level.maxclients ; i++ ) {
 
1611
                        if (level.clients[i].sess.sessionTeam != team)
 
1612
                                continue;
 
1613
                        if (!(g_entities[i].r.svFlags & SVF_BOT)) {
 
1614
                                level.clients[i].sess.teamLeader = qtrue;
 
1615
                                break;
 
1616
                        }
 
1617
                }
 
1618
                for ( i = 0 ; i < level.maxclients ; i++ ) {
 
1619
                        if (level.clients[i].sess.sessionTeam != team)
 
1620
                                continue;
 
1621
                        level.clients[i].sess.teamLeader = qtrue;
 
1622
                        break;
 
1623
                }
 
1624
        }
 
1625
}
 
1626
 
 
1627
/*
 
1628
==================
 
1629
CheckTeamVote
 
1630
==================
 
1631
*/
 
1632
void CheckTeamVote( int team ) {
 
1633
        int cs_offset;
 
1634
 
 
1635
        if ( team == TEAM_RED )
 
1636
                cs_offset = 0;
 
1637
        else if ( team == TEAM_BLUE )
 
1638
                cs_offset = 1;
 
1639
        else
 
1640
                return;
 
1641
 
 
1642
        if ( !level.teamVoteTime[cs_offset] ) {
 
1643
                return;
 
1644
        }
 
1645
        if ( level.time - level.teamVoteTime[cs_offset] >= VOTE_TIME ) {
 
1646
                trap_SendServerCommand( -1, "print \"Team vote failed.\n\"" );
 
1647
        } else {
 
1648
                if ( level.teamVoteYes[cs_offset] > level.numteamVotingClients[cs_offset]/2 ) {
 
1649
                        // execute the command, then remove the vote
 
1650
                        trap_SendServerCommand( -1, "print \"Team vote passed.\n\"" );
 
1651
                        //
 
1652
                        if ( !Q_strncmp( "leader", level.teamVoteString[cs_offset], 6) ) {
 
1653
                                //set the team leader
 
1654
                                SetLeader(team, atoi(level.teamVoteString[cs_offset] + 7));
 
1655
                        }
 
1656
                        else {
 
1657
                                trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.teamVoteString[cs_offset] ) );
 
1658
                        }
 
1659
                } else if ( level.teamVoteNo[cs_offset] >= level.numteamVotingClients[cs_offset]/2 ) {
 
1660
                        // same behavior as a timeout
 
1661
                        trap_SendServerCommand( -1, "print \"Team vote failed.\n\"" );
 
1662
                } else {
 
1663
                        // still waiting for a majority
 
1664
                        return;
 
1665
                }
 
1666
        }
 
1667
        level.teamVoteTime[cs_offset] = 0;
 
1668
        trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" );
 
1669
 
 
1670
}
 
1671
 
 
1672
 
 
1673
/*
 
1674
==================
 
1675
CheckCvars
 
1676
==================
 
1677
*/
 
1678
void CheckCvars( void ) {
 
1679
        static int lastMod = -1;
 
1680
 
 
1681
        if ( g_password.modificationCount != lastMod ) {
 
1682
                lastMod = g_password.modificationCount;
 
1683
                if ( *g_password.string && Q_stricmp( g_password.string, "none" ) ) {
 
1684
                        trap_Cvar_Set( "g_needpass", "1" );
 
1685
                } else {
 
1686
                        trap_Cvar_Set( "g_needpass", "0" );
 
1687
                }
 
1688
        }
 
1689
}
 
1690
 
 
1691
/*
 
1692
=============
 
1693
G_RunThink
 
1694
 
 
1695
Runs thinking code for this frame if necessary
 
1696
=============
 
1697
*/
 
1698
void G_RunThink (gentity_t *ent) {
 
1699
        float   thinktime;
 
1700
 
 
1701
        thinktime = ent->nextthink;
 
1702
        if (thinktime <= 0) {
 
1703
                return;
 
1704
        }
 
1705
        if (thinktime > level.time) {
 
1706
                return;
 
1707
        }
 
1708
        
 
1709
        ent->nextthink = 0;
 
1710
        if (!ent->think) {
 
1711
                G_Error ( "NULL ent->think");
 
1712
        }
 
1713
        ent->think (ent);
 
1714
}
 
1715
 
 
1716
/*
 
1717
================
 
1718
G_RunFrame
 
1719
 
 
1720
Advances the non-player objects in the world
 
1721
================
 
1722
*/
 
1723
void G_RunFrame( int levelTime ) {
 
1724
        int                     i;
 
1725
        gentity_t       *ent;
 
1726
        int                     msec;
 
1727
int start, end;
 
1728
 
 
1729
        // if we are waiting for the level to restart, do nothing
 
1730
        if ( level.restarted ) {
 
1731
                return;
 
1732
        }
 
1733
 
 
1734
        level.framenum++;
 
1735
        level.previousTime = level.time;
 
1736
        level.time = levelTime;
 
1737
        msec = level.time - level.previousTime;
 
1738
 
 
1739
        // get any cvar changes
 
1740
        G_UpdateCvars();
 
1741
 
 
1742
        //
 
1743
        // go through all allocated objects
 
1744
        //
 
1745
        start = trap_Milliseconds();
 
1746
        ent = &g_entities[0];
 
1747
        for (i=0 ; i<level.num_entities ; i++, ent++) {
 
1748
                if ( !ent->inuse ) {
 
1749
                        continue;
 
1750
                }
 
1751
 
 
1752
                // clear events that are too old
 
1753
                if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
 
1754
                        if ( ent->s.event ) {
 
1755
                                ent->s.event = 0;       // &= EV_EVENT_BITS;
 
1756
                                if ( ent->client ) {
 
1757
                                        ent->client->ps.externalEvent = 0;
 
1758
                                        // predicted events should never be set to zero
 
1759
                                        //ent->client->ps.events[0] = 0;
 
1760
                                        //ent->client->ps.events[1] = 0;
 
1761
                                }
 
1762
                        }
 
1763
                        if ( ent->freeAfterEvent ) {
 
1764
                                // tempEntities or dropped items completely go away after their event
 
1765
                                G_FreeEntity( ent );
 
1766
                                continue;
 
1767
                        } else if ( ent->unlinkAfterEvent ) {
 
1768
                                // items that will respawn will hide themselves after their pickup event
 
1769
                                ent->unlinkAfterEvent = qfalse;
 
1770
                                trap_UnlinkEntity( ent );
 
1771
                        }
 
1772
                }
 
1773
 
 
1774
                // temporary entities don't think
 
1775
                if ( ent->freeAfterEvent ) {
 
1776
                        continue;
 
1777
                }
 
1778
 
 
1779
                if ( !ent->r.linked && ent->neverFree ) {
 
1780
                        continue;
 
1781
                }
 
1782
 
 
1783
                if ( ent->s.eType == ET_MISSILE ) {
 
1784
                        G_RunMissile( ent );
 
1785
                        continue;
 
1786
                }
 
1787
 
 
1788
                if ( ent->s.eType == ET_ITEM || ent->physicsObject ) {
 
1789
                        G_RunItem( ent );
 
1790
                        continue;
 
1791
                }
 
1792
 
 
1793
                if ( ent->s.eType == ET_MOVER ) {
 
1794
                        G_RunMover( ent );
 
1795
                        continue;
 
1796
                }
 
1797
 
 
1798
                if ( i < MAX_CLIENTS ) {
 
1799
                        G_RunClient( ent );
 
1800
                        continue;
 
1801
                }
 
1802
 
 
1803
                G_RunThink( ent );
 
1804
        }
 
1805
end = trap_Milliseconds();
 
1806
 
 
1807
start = trap_Milliseconds();
 
1808
        // perform final fixups on the players
 
1809
        ent = &g_entities[0];
 
1810
        for (i=0 ; i < level.maxclients ; i++, ent++ ) {
 
1811
                if ( ent->inuse ) {
 
1812
                        ClientEndFrame( ent );
 
1813
                }
 
1814
        }
 
1815
end = trap_Milliseconds();
 
1816
 
 
1817
        // see if it is time to do a tournement restart
 
1818
        CheckTournament();
 
1819
 
 
1820
        // see if it is time to end the level
 
1821
        CheckExitRules();
 
1822
 
 
1823
        // update to team status?
 
1824
        CheckTeamStatus();
 
1825
 
 
1826
        // cancel vote if timed out
 
1827
        CheckVote();
 
1828
 
 
1829
        // check team votes
 
1830
        CheckTeamVote( TEAM_RED );
 
1831
        CheckTeamVote( TEAM_BLUE );
 
1832
 
 
1833
        // for tracking changes
 
1834
        CheckCvars();
 
1835
 
 
1836
        if (g_listEntity.integer) {
 
1837
                for (i = 0; i < MAX_GENTITIES; i++) {
 
1838
                        G_Printf("%4i: %s\n", i, g_entities[i].classname);
 
1839
                }
 
1840
                trap_Cvar_Set("g_listEntity", "0");
 
1841
        }
 
1842
}