codemp/game/g_main.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 
00004 #include "g_local.h"
00005 #include "g_ICARUScb.h"
00006 #include "g_nav.h"
00007 #include "bg_saga.h"
00008 
00009 level_locals_t  level;
00010 
00011 int             eventClearTime = 0;
00012 static int navCalcPathTime = 0;
00013 extern int fatalErrors;
00014 
00015 int killPlayerTimer = 0;
00016 
00017 typedef struct {
00018         vmCvar_t        *vmCvar;
00019         char            *cvarName;
00020         char            *defaultString;
00021         int                     cvarFlags;
00022         int                     modificationCount;  // for tracking changes
00023         qboolean        trackChange;        // track this variable, and announce if changed
00024   qboolean teamShader;        // track and if changed, update shader state
00025 } cvarTable_t;
00026 
00027 gentity_t               g_entities[MAX_GENTITIES];
00028 gclient_t               g_clients[MAX_CLIENTS];
00029 
00030 qboolean gDuelExit = qfalse;
00031 
00032 vmCvar_t        g_trueJedi;
00033 
00034 vmCvar_t        g_gametype;
00035 vmCvar_t        g_MaxHolocronCarry;
00036 vmCvar_t        g_ff_objectives;
00037 vmCvar_t        g_autoMapCycle;
00038 vmCvar_t        g_dmflags;
00039 vmCvar_t        g_maxForceRank;
00040 vmCvar_t        g_forceBasedTeams;
00041 vmCvar_t        g_privateDuel;
00042 
00043 vmCvar_t        g_allowNPC;
00044 
00045 vmCvar_t        g_armBreakage;
00046 
00047 vmCvar_t        g_saberLocking;
00048 vmCvar_t        g_saberLockFactor;
00049 vmCvar_t        g_saberTraceSaberFirst;
00050 
00051 vmCvar_t        d_saberKickTweak;
00052 
00053 vmCvar_t        d_powerDuelPrint;
00054 
00055 vmCvar_t        d_saberGhoul2Collision;
00056 vmCvar_t        g_saberBladeFaces;
00057 vmCvar_t        d_saberAlwaysBoxTrace;
00058 vmCvar_t        d_saberBoxTraceSize;
00059 
00060 vmCvar_t        d_siegeSeekerNPC;
00061 
00062 vmCvar_t        g_debugMelee;
00063 vmCvar_t        g_stepSlideFix;
00064 
00065 vmCvar_t        g_noSpecMove;
00066 
00067 #ifdef _DEBUG
00068 vmCvar_t        g_disableServerG2;
00069 #endif
00070 
00071 vmCvar_t        d_perPlayerGhoul2;
00072 
00073 vmCvar_t        d_projectileGhoul2Collision;
00074 
00075 vmCvar_t        g_g2TraceLod;
00076 
00077 vmCvar_t        g_optvehtrace;
00078 
00079 vmCvar_t        g_locationBasedDamage;
00080 
00081 vmCvar_t        g_allowHighPingDuelist;
00082 
00083 vmCvar_t        g_logClientInfo;
00084 
00085 vmCvar_t        g_slowmoDuelEnd;
00086 
00087 vmCvar_t        g_saberDamageScale;
00088 
00089 vmCvar_t        g_useWhileThrowing;
00090 
00091 vmCvar_t        g_RMG;
00092 
00093 vmCvar_t        g_svfps;
00094 
00095 vmCvar_t        g_forceRegenTime;
00096 vmCvar_t        g_spawnInvulnerability;
00097 vmCvar_t        g_forcePowerDisable;
00098 vmCvar_t        g_weaponDisable;
00099 vmCvar_t        g_duelWeaponDisable;
00100 vmCvar_t        g_allowDuelSuicide;
00101 vmCvar_t        g_fraglimitVoteCorrection;
00102 vmCvar_t        g_fraglimit;
00103 vmCvar_t        g_duel_fraglimit;
00104 vmCvar_t        g_timelimit;
00105 vmCvar_t        g_capturelimit;
00106 vmCvar_t        d_saberInterpolate;
00107 vmCvar_t        g_friendlyFire;
00108 vmCvar_t        g_friendlySaber;
00109 vmCvar_t        g_password;
00110 vmCvar_t        g_needpass;
00111 vmCvar_t        g_maxclients;
00112 vmCvar_t        g_maxGameClients;
00113 vmCvar_t        g_dedicated;
00114 vmCvar_t        g_developer;
00115 vmCvar_t        g_speed;
00116 vmCvar_t        g_gravity;
00117 vmCvar_t        g_cheats;
00118 vmCvar_t        g_knockback;
00119 vmCvar_t        g_quadfactor;
00120 vmCvar_t        g_forcerespawn;
00121 vmCvar_t        g_siegeRespawn;
00122 vmCvar_t        g_inactivity;
00123 vmCvar_t        g_debugMove;
00124 #ifndef FINAL_BUILD
00125 vmCvar_t        g_debugDamage;
00126 #endif
00127 vmCvar_t        g_debugAlloc;
00128 vmCvar_t        g_debugServerSkel;
00129 vmCvar_t        g_weaponRespawn;
00130 vmCvar_t        g_weaponTeamRespawn;
00131 vmCvar_t        g_adaptRespawn;
00132 vmCvar_t        g_motd;
00133 vmCvar_t        g_synchronousClients;
00134 vmCvar_t        g_warmup;
00135 vmCvar_t        g_doWarmup;
00136 vmCvar_t        g_restarted;
00137 vmCvar_t        g_log;
00138 vmCvar_t        g_logSync;
00139 vmCvar_t        g_statLog;
00140 vmCvar_t        g_statLogFile;
00141 vmCvar_t        g_blood;
00142 vmCvar_t        g_podiumDist;
00143 vmCvar_t        g_podiumDrop;
00144 vmCvar_t        g_allowVote;
00145 vmCvar_t        g_teamAutoJoin;
00146 vmCvar_t        g_teamForceBalance;
00147 vmCvar_t        g_banIPs;
00148 vmCvar_t        g_filterBan;
00149 vmCvar_t        g_debugForward;
00150 vmCvar_t        g_debugRight;
00151 vmCvar_t        g_debugUp;
00152 vmCvar_t        g_smoothClients;
00153 
00154 #include "../namespace_begin.h"
00155 vmCvar_t        pmove_fixed;
00156 vmCvar_t        pmove_msec;
00157 #include "../namespace_end.h"
00158 
00159 vmCvar_t        g_listEntity;
00160 //vmCvar_t      g_redteam;
00161 //vmCvar_t      g_blueteam;
00162 vmCvar_t        g_singlePlayer;
00163 vmCvar_t        g_enableBreath;
00164 vmCvar_t        g_dismember;
00165 vmCvar_t        g_forceDodge;
00166 vmCvar_t        g_timeouttospec;
00167 
00168 vmCvar_t        g_saberDmgVelocityScale;
00169 vmCvar_t        g_saberDmgDelay_Idle;
00170 vmCvar_t        g_saberDmgDelay_Wound;
00171 
00172 vmCvar_t        g_saberDebugPrint;
00173 
00174 vmCvar_t        g_siegeTeamSwitch;
00175 
00176 vmCvar_t        bg_fighterAltControl;
00177 
00178 #ifdef DEBUG_SABER_BOX
00179 vmCvar_t        g_saberDebugBox;
00180 #endif
00181 
00182 //NPC nav debug
00183 vmCvar_t        d_altRoutes;
00184 vmCvar_t        d_patched;
00185 
00186 vmCvar_t                g_saberRealisticCombat;
00187 vmCvar_t                g_saberRestrictForce;
00188 vmCvar_t                d_saberSPStyleDamage;
00189 vmCvar_t                g_debugSaberLocks;
00190 vmCvar_t                g_saberLockRandomNess;
00191 // nmckenzie: SABER_DAMAGE_WALLS
00192 vmCvar_t                g_saberWallDamageScale;
00193 
00194 vmCvar_t                d_saberStanceDebug;
00195 // ai debug cvars
00196 vmCvar_t                debugNPCAI;                     // used to print out debug info about the bot AI
00197 vmCvar_t                debugNPCFreeze;         // set to disable bot ai and temporarily freeze them in place
00198 vmCvar_t                debugNPCAimingBeam;
00199 vmCvar_t                debugBreak;
00200 vmCvar_t                debugNoRoam;
00201 vmCvar_t                d_saberCombat;
00202 vmCvar_t                d_JediAI;
00203 vmCvar_t                d_noGroupAI;
00204 vmCvar_t                d_asynchronousGroupAI;
00205 vmCvar_t                d_slowmodeath;
00206 vmCvar_t                d_noIntermissionWait;
00207 
00208 vmCvar_t                g_spskill;
00209 
00210 
00211 vmCvar_t                g_siegeTeam1;
00212 vmCvar_t                g_siegeTeam2;
00213 
00214 vmCvar_t        g_austrian;
00215 
00216 vmCvar_t        g_powerDuelStartHealth;
00217 vmCvar_t        g_powerDuelEndHealth;
00218 
00219 // nmckenzie: temporary way to show player healths in duels - some iface gfx in game would be better, of course.
00220 // DUEL_HEALTH
00221 vmCvar_t                g_showDuelHealths;
00222 
00223 // bk001129 - made static to avoid aliasing
00224 static cvarTable_t              gameCvarTable[] = {
00225         // don't override the cheat state set by the system
00226         { &g_cheats, "sv_cheats", "", 0, 0, qfalse },
00227 
00228         { &g_debugMelee, "g_debugMelee", "0", CVAR_SERVERINFO, 0, qtrue  },
00229         { &g_stepSlideFix, "g_stepSlideFix", "1", CVAR_SERVERINFO, 0, qtrue  },
00230 
00231         { &g_noSpecMove, "g_noSpecMove", "0", CVAR_SERVERINFO, 0, qtrue },
00232 
00233         // noset vars
00234         { NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
00235         { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse  },
00236         { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse  },
00237         { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
00238 
00239         // latched vars
00240         { &g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse  },
00241         { &g_MaxHolocronCarry, "g_MaxHolocronCarry", "3", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse  },
00242 
00243         { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
00244         { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
00245 
00246         { &g_trueJedi, "g_jediVmerc", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qtrue },
00247 
00248         // change anytime vars
00249         { &g_ff_objectives, "g_ff_objectives", "0", /*CVAR_SERVERINFO |*/ CVAR_CHEAT | CVAR_NORESTART, 0, qtrue },
00250 
00251         { &g_autoMapCycle, "g_autoMapCycle", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
00252         { &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
00253         
00254         { &g_maxForceRank, "g_maxForceRank", "6", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH, 0, qfalse  },
00255         { &g_forceBasedTeams, "g_forceBasedTeams", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH, 0, qfalse  },
00256         { &g_privateDuel, "g_privateDuel", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
00257 
00258         { &g_allowNPC, "g_allowNPC", "1", CVAR_SERVERINFO | CVAR_CHEAT, 0, qtrue  },
00259 
00260         { &g_armBreakage, "g_armBreakage", "0", 0, 0, qtrue  },
00261 
00262         { &g_saberLocking, "g_saberLocking", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
00263         { &g_saberLockFactor, "g_saberLockFactor", "2", CVAR_ARCHIVE, 0, qtrue  },
00264         { &g_saberTraceSaberFirst, "g_saberTraceSaberFirst", "0", CVAR_ARCHIVE, 0, qtrue  },
00265 
00266         { &d_saberKickTweak, "d_saberKickTweak", "1", 0, 0, qtrue  },
00267 
00268         { &d_powerDuelPrint, "d_powerDuelPrint", "0", 0, qtrue },
00269 
00270         { &d_saberGhoul2Collision, "d_saberGhoul2Collision", "1", CVAR_CHEAT, 0, qtrue  },
00271         { &g_saberBladeFaces, "g_saberBladeFaces", "1", 0, 0, qtrue  },
00272 
00273         { &d_saberAlwaysBoxTrace, "d_saberAlwaysBoxTrace", "0", CVAR_CHEAT, 0, qtrue  },
00274         { &d_saberBoxTraceSize, "d_saberBoxTraceSize", "0", CVAR_CHEAT, 0, qtrue  },
00275 
00276         { &d_siegeSeekerNPC, "d_siegeSeekerNPC", "0", CVAR_CHEAT, 0, qtrue },
00277 
00278 #ifdef _DEBUG
00279         { &g_disableServerG2, "g_disableServerG2", "0", 0, 0, qtrue },
00280 #endif
00281 
00282         { &d_perPlayerGhoul2, "d_perPlayerGhoul2", "0", CVAR_CHEAT, 0, qtrue },
00283 
00284         { &d_projectileGhoul2Collision, "d_projectileGhoul2Collision", "1", CVAR_CHEAT, 0, qtrue  },
00285 
00286         { &g_g2TraceLod, "g_g2TraceLod", "3", 0, 0, qtrue  },
00287 
00288         { &g_optvehtrace, "com_optvehtrace", "0", 0, 0, qtrue  },
00289 
00290         { &g_locationBasedDamage, "g_locationBasedDamage", "1", 0, 0, qtrue },
00291 
00292         { &g_allowHighPingDuelist, "g_allowHighPingDuelist", "1", 0, 0, qtrue },
00293 
00294         { &g_logClientInfo, "g_logClientInfo", "0", CVAR_ARCHIVE, 0, qtrue  },
00295 
00296         { &g_slowmoDuelEnd, "g_slowmoDuelEnd", "0", CVAR_ARCHIVE, 0, qtrue  },
00297 
00298         { &g_saberDamageScale, "g_saberDamageScale", "1", CVAR_ARCHIVE, 0, qtrue  },
00299 
00300         { &g_useWhileThrowing, "g_useWhileThrowing", "1", 0, 0, qtrue  },
00301 
00302         { &g_RMG, "RMG", "0", 0, 0, qtrue  },
00303 
00304         { &g_svfps, "sv_fps", "20", 0, 0, qtrue },
00305 
00306         { &g_forceRegenTime, "g_forceRegenTime", "200", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
00307 
00308         { &g_spawnInvulnerability, "g_spawnInvulnerability", "3000", CVAR_ARCHIVE, 0, qtrue  },
00309 
00310         { &g_forcePowerDisable, "g_forcePowerDisable", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH, 0, qtrue  },
00311         { &g_weaponDisable, "g_weaponDisable", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH, 0, qtrue  },
00312         { &g_duelWeaponDisable, "g_duelWeaponDisable", "1", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH, 0, qtrue  },
00313 
00314         { &g_allowDuelSuicide, "g_allowDuelSuicide", "1", CVAR_ARCHIVE, 0, qtrue },
00315 
00316         { &g_fraglimitVoteCorrection, "g_fraglimitVoteCorrection", "1", CVAR_ARCHIVE, 0, qtrue },
00317 
00318         { &g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
00319         { &g_duel_fraglimit, "duel_fraglimit", "10", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
00320         { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
00321         { &g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
00322 
00323         { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse  },
00324 
00325         { &d_saberInterpolate, "d_saberInterpolate", "0", CVAR_CHEAT, 0, qtrue },
00326 
00327         { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue  },
00328         { &g_friendlySaber, "g_friendlySaber", "0", CVAR_ARCHIVE, 0, qtrue  },
00329 
00330         { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE  },
00331         { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE  },
00332 
00333         { &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue  },
00334         { &g_doWarmup, "g_doWarmup", "0", 0, 0, qtrue  },
00335         { &g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse  },
00336         { &g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse  },
00337 
00338         { &g_statLog, "g_statLog", "0", CVAR_ARCHIVE, 0, qfalse },
00339         { &g_statLogFile, "g_statLogFile", "statlog.log", CVAR_ARCHIVE, 0, qfalse },
00340 
00341         { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse  },
00342 
00343         { &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse  },
00344         { &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse  },
00345 
00346         { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
00347 
00348         { &g_dedicated, "dedicated", "0", 0, 0, qfalse  },
00349 
00350         { &g_developer, "developer", "0", 0, 0, qfalse },
00351 
00352         { &g_speed, "g_speed", "250", 0, 0, qtrue  },
00353         { &g_gravity, "g_gravity", "800", 0, 0, qtrue  },
00354         { &g_knockback, "g_knockback", "1000", 0, 0, qtrue  },
00355         { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue  },
00356         { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue  },
00357         { &g_weaponTeamRespawn, "g_weaponTeamRespawn", "5", 0, 0, qtrue },
00358         { &g_adaptRespawn, "g_adaptrespawn", "1", 0, 0, qtrue  },               // Make weapons respawn faster with a lot of players.
00359         { &g_forcerespawn, "g_forcerespawn", "60", 0, 0, qtrue },               // One minute force respawn.  Give a player enough time to reallocate force.
00360         { &g_siegeRespawn, "g_siegeRespawn", "20", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, //siege respawn wave time
00361         { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
00362         { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
00363 #ifndef FINAL_BUILD
00364         { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
00365 #endif
00366         { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse },
00367         { &g_debugServerSkel, "g_debugServerSkel", "0", CVAR_CHEAT, 0, qfalse },
00368         { &g_motd, "g_motd", "", 0, 0, qfalse },
00369         { &g_blood, "com_blood", "1", 0, 0, qfalse },
00370 
00371         { &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse },
00372         { &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse },
00373 
00374         { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },
00375         { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse },
00376 
00377 #if 0
00378         { &g_debugForward, "g_debugForward", "0", 0, 0, qfalse },
00379         { &g_debugRight, "g_debugRight", "0", 0, 0, qfalse },
00380         { &g_debugUp, "g_debugUp", "0", 0, 0, qfalse },
00381 #endif
00382 
00383 //      { &g_redteam, "g_redteam", "Empire", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue },
00384 //      { &g_blueteam, "g_blueteam", "Rebellion", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue  },
00385         { &g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse  },
00386 
00387         { &g_enableBreath, "g_enableBreath", "0", 0, 0, qtrue, qfalse },
00388         { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},
00389         { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse},
00390         { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse},
00391 
00392         { &g_dismember, "g_dismember", "0", CVAR_ARCHIVE, 0, qtrue  },
00393         { &g_forceDodge, "g_forceDodge", "1", 0, 0, qtrue  },
00394 
00395         { &g_timeouttospec, "g_timeouttospec", "70", CVAR_ARCHIVE, 0, qfalse },
00396 
00397         { &g_saberDmgVelocityScale, "g_saberDmgVelocityScale", "0", CVAR_ARCHIVE, 0, qtrue  },
00398         { &g_saberDmgDelay_Idle, "g_saberDmgDelay_Idle", "350", CVAR_ARCHIVE, 0, qtrue  },
00399         { &g_saberDmgDelay_Wound, "g_saberDmgDelay_Wound", "0", CVAR_ARCHIVE, 0, qtrue  },
00400 
00401 #ifndef FINAL_BUILD
00402         { &g_saberDebugPrint, "g_saberDebugPrint", "0", CVAR_CHEAT, 0, qfalse  },
00403 #endif
00404         { &g_debugSaberLocks, "g_debugSaberLocks", "0", CVAR_CHEAT, 0, qfalse },
00405         { &g_saberLockRandomNess, "g_saberLockRandomNess", "2", CVAR_CHEAT, 0, qfalse },
00406 // nmckenzie: SABER_DAMAGE_WALLS
00407         { &g_saberWallDamageScale, "g_saberWallDamageScale", "0.4", CVAR_SERVERINFO, 0, qfalse },
00408 
00409         { &d_saberStanceDebug, "d_saberStanceDebug", "0", 0, 0, qfalse },
00410 
00411         { &g_siegeTeamSwitch, "g_siegeTeamSwitch", "1", CVAR_SERVERINFO|CVAR_ARCHIVE, qfalse },
00412 
00413         { &bg_fighterAltControl, "bg_fighterAltControl", "0", CVAR_SERVERINFO, 0, qtrue },
00414 
00415 #ifdef DEBUG_SABER_BOX
00416         { &g_saberDebugBox, "g_saberDebugBox", "0", CVAR_CHEAT, 0, qfalse },
00417 #endif
00418 
00419         { &d_altRoutes, "d_altRoutes", "0", CVAR_CHEAT, 0, qfalse },
00420         { &d_patched, "d_patched", "0", CVAR_CHEAT, 0, qfalse },
00421 
00422         { &g_saberRealisticCombat, "g_saberRealisticCombat", "0", CVAR_CHEAT },
00423         { &g_saberRestrictForce, "g_saberRestrictForce", "0", CVAR_CHEAT },
00424         { &d_saberSPStyleDamage, "d_saberSPStyleDamage", "1", CVAR_CHEAT },
00425 
00426         { &debugNoRoam, "d_noroam", "0", CVAR_CHEAT },
00427         { &debugNPCAimingBeam, "d_npcaiming", "0", CVAR_CHEAT },
00428         { &debugBreak, "d_break", "0", CVAR_CHEAT },
00429         { &debugNPCAI, "d_npcai", "0", CVAR_CHEAT },
00430         { &debugNPCFreeze, "d_npcfreeze", "0", CVAR_CHEAT },
00431         { &d_JediAI, "d_JediAI", "0", CVAR_CHEAT },
00432         { &d_noGroupAI, "d_noGroupAI", "0", CVAR_CHEAT },
00433         { &d_asynchronousGroupAI, "d_asynchronousGroupAI", "0", CVAR_CHEAT },
00434         
00435         //0 = never (BORING)
00436         //1 = kyle only
00437         //2 = kyle and last enemy jedi
00438         //3 = kyle and any enemy jedi
00439         //4 = kyle and last enemy in a group
00440         //5 = kyle and any enemy
00441         //6 = also when kyle takes pain or enemy jedi dodges player saber swing or does an acrobatic evasion
00442 
00443         { &d_slowmodeath, "d_slowmodeath", "0", CVAR_CHEAT },
00444 
00445         { &d_saberCombat, "d_saberCombat", "0", CVAR_CHEAT },
00446 
00447         { &g_spskill, "g_npcspskill", "0", CVAR_ARCHIVE | CVAR_INTERNAL },
00448 
00449         //for overriding the level defaults
00450         { &g_siegeTeam1, "g_siegeTeam1", "none", CVAR_ARCHIVE|CVAR_SERVERINFO, 0, qfalse  },
00451         { &g_siegeTeam2, "g_siegeTeam2", "none", CVAR_ARCHIVE|CVAR_SERVERINFO, 0, qfalse  },
00452 
00453         //mainly for debugging with bots while I'm not around (want the server to
00454         //cycle through levels naturally)
00455         { &d_noIntermissionWait, "d_noIntermissionWait", "0", CVAR_CHEAT, 0, qfalse  },
00456 
00457         { &g_austrian, "g_austrian", "0", CVAR_ARCHIVE, 0, qfalse  },
00458 // nmckenzie:
00459 // DUEL_HEALTH
00460         { &g_showDuelHealths, "g_showDuelHealths", "0", CVAR_SERVERINFO },
00461         { &g_powerDuelStartHealth, "g_powerDuelStartHealth", "150", CVAR_ARCHIVE, 0, qtrue  },
00462         { &g_powerDuelEndHealth, "g_powerDuelEndHealth", "90", CVAR_ARCHIVE, 0, qtrue  },
00463 };
00464 
00465 // bk001129 - made static to avoid aliasing
00466 static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[0] );
00467 
00468 
00469 void G_InitGame                                 ( int levelTime, int randomSeed, int restart );
00470 void G_RunFrame                                 ( int levelTime );
00471 void G_ShutdownGame                             ( int restart );
00472 void CheckExitRules                             ( void );
00473 void G_ROFF_NotetrackCallback   ( gentity_t *cent, const char *notetrack);
00474 
00475 extern stringID_table_t setTable[];
00476 
00477 qboolean G_ParseSpawnVars( qboolean inSubBSP );
00478 void G_SpawnGEntityFromSpawnVars( qboolean inSubBSP );
00479 
00480 
00481 qboolean NAV_ClearPathToPoint( gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask, int okToHitEntNum );
00482 qboolean NPC_ClearLOS2( gentity_t *ent, const vec3_t end );
00483 int NAVNEW_ClearPathBetweenPoints(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask);
00484 qboolean NAV_CheckNodeFailedForEnt( gentity_t *ent, int nodeNum );
00485 qboolean G_EntIsUnlockedDoor( int entityNum );
00486 qboolean G_EntIsDoor( int entityNum );
00487 qboolean G_EntIsBreakable( int entityNum );
00488 qboolean G_EntIsRemovableUsable( int entNum );
00489 void CP_FindCombatPointWaypoints( void );
00490 
00491 /*
00492 ================
00493 vmMain
00494 
00495 This is the only way control passes into the module.
00496 This must be the very first function compiled into the .q3vm file
00497 ================
00498 */
00499 #include "../namespace_begin.h"
00500 #ifdef __linux__
00501 extern "C" {
00502 #endif
00503 int 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  ) {
00504         switch ( command ) {
00505         case GAME_INIT:
00506                 G_InitGame( arg0, arg1, arg2 );
00507                 return 0;
00508         case GAME_SHUTDOWN:
00509                 G_ShutdownGame( arg0 );
00510                 return 0;
00511         case GAME_CLIENT_CONNECT:
00512                 return (int)ClientConnect( arg0, arg1, arg2 );
00513         case GAME_CLIENT_THINK:
00514                 ClientThink( arg0, NULL );
00515                 return 0;
00516         case GAME_CLIENT_USERINFO_CHANGED:
00517                 ClientUserinfoChanged( arg0 );
00518                 return 0;
00519         case GAME_CLIENT_DISCONNECT:
00520                 ClientDisconnect( arg0 );
00521                 return 0;
00522         case GAME_CLIENT_BEGIN:
00523                 ClientBegin( arg0, qtrue );
00524                 return 0;
00525         case GAME_CLIENT_COMMAND:
00526                 ClientCommand( arg0 );
00527                 return 0;
00528         case GAME_RUN_FRAME:
00529                 G_RunFrame( arg0 );
00530                 return 0;
00531         case GAME_CONSOLE_COMMAND:
00532                 return ConsoleCommand();
00533         case BOTAI_START_FRAME:
00534                 return BotAIStartFrame( arg0 );
00535         case GAME_ROFF_NOTETRACK_CALLBACK:
00536                 G_ROFF_NotetrackCallback( &g_entities[arg0], (const char *)arg1 );
00537                 return 0;
00538         case GAME_SPAWN_RMG_ENTITY:
00539                 if (G_ParseSpawnVars(qfalse))
00540                 {
00541                         G_SpawnGEntityFromSpawnVars(qfalse);
00542                 }
00543                 return 0;
00544 
00545         //rww - begin icarus callbacks
00546         case GAME_ICARUS_PLAYSOUND:
00547                 {
00548                         T_G_ICARUS_PLAYSOUND *sharedMem = (T_G_ICARUS_PLAYSOUND *)gSharedBuffer;
00549                         return Q3_PlaySound(sharedMem->taskID, sharedMem->entID, sharedMem->name, sharedMem->channel);
00550                 }
00551         case GAME_ICARUS_SET:
00552                 {
00553                         T_G_ICARUS_SET *sharedMem = (T_G_ICARUS_SET *)gSharedBuffer;
00554                         return Q3_Set(sharedMem->taskID, sharedMem->entID, sharedMem->type_name, sharedMem->data);
00555                 }
00556         case GAME_ICARUS_LERP2POS:
00557                 {
00558                         T_G_ICARUS_LERP2POS *sharedMem = (T_G_ICARUS_LERP2POS *)gSharedBuffer;
00559                         if (sharedMem->nullAngles)
00560                         {
00561                                 Q3_Lerp2Pos(sharedMem->taskID, sharedMem->entID, sharedMem->origin, NULL, sharedMem->duration);
00562                         }
00563                         else
00564                         {
00565                                 Q3_Lerp2Pos(sharedMem->taskID, sharedMem->entID, sharedMem->origin, sharedMem->angles, sharedMem->duration);
00566                         }
00567                 }
00568                 return 0;
00569         case GAME_ICARUS_LERP2ORIGIN:
00570                 {
00571                         T_G_ICARUS_LERP2ORIGIN *sharedMem = (T_G_ICARUS_LERP2ORIGIN *)gSharedBuffer;
00572                         Q3_Lerp2Origin(sharedMem->taskID, sharedMem->entID, sharedMem->origin, sharedMem->duration);
00573                 }
00574                 return 0;
00575         case GAME_ICARUS_LERP2ANGLES:
00576                 {
00577                         T_G_ICARUS_LERP2ANGLES *sharedMem = (T_G_ICARUS_LERP2ANGLES *)gSharedBuffer;
00578                         Q3_Lerp2Angles(sharedMem->taskID, sharedMem->entID, sharedMem->angles, sharedMem->duration);
00579                 }
00580                 return 0;
00581         case GAME_ICARUS_GETTAG:
00582                 {
00583                         T_G_ICARUS_GETTAG *sharedMem = (T_G_ICARUS_GETTAG *)gSharedBuffer;
00584                         return Q3_GetTag(sharedMem->entID, sharedMem->name, sharedMem->lookup, sharedMem->info);
00585                 }
00586         case GAME_ICARUS_LERP2START:
00587                 {
00588                         T_G_ICARUS_LERP2START *sharedMem = (T_G_ICARUS_LERP2START *)gSharedBuffer;
00589                         Q3_Lerp2Start(sharedMem->entID, sharedMem->taskID, sharedMem->duration);
00590                 }
00591                 return 0;
00592         case GAME_ICARUS_LERP2END:
00593                 {
00594                         T_G_ICARUS_LERP2END *sharedMem = (T_G_ICARUS_LERP2END *)gSharedBuffer;
00595                         Q3_Lerp2End(sharedMem->entID, sharedMem->taskID, sharedMem->duration);
00596                 }
00597                 return 0;
00598         case GAME_ICARUS_USE:
00599                 {
00600                         T_G_ICARUS_USE *sharedMem = (T_G_ICARUS_USE *)gSharedBuffer;
00601                         Q3_Use(sharedMem->entID, sharedMem->target);
00602                 }
00603                 return 0;
00604         case GAME_ICARUS_KILL:
00605                 {
00606                         T_G_ICARUS_KILL *sharedMem = (T_G_ICARUS_KILL *)gSharedBuffer;
00607                         Q3_Kill(sharedMem->entID, sharedMem->name);
00608                 }
00609                 return 0;
00610         case GAME_ICARUS_REMOVE:
00611                 {
00612                         T_G_ICARUS_REMOVE *sharedMem = (T_G_ICARUS_REMOVE *)gSharedBuffer;
00613                         Q3_Remove(sharedMem->entID, sharedMem->name);
00614                 }
00615                 return 0;
00616         case GAME_ICARUS_PLAY:
00617                 {
00618                         T_G_ICARUS_PLAY *sharedMem = (T_G_ICARUS_PLAY *)gSharedBuffer;
00619                         Q3_Play(sharedMem->taskID, sharedMem->entID, sharedMem->type, sharedMem->name);
00620                 }
00621                 return 0;
00622         case GAME_ICARUS_GETFLOAT:
00623                 {
00624                         T_G_ICARUS_GETFLOAT *sharedMem = (T_G_ICARUS_GETFLOAT *)gSharedBuffer;
00625                         return Q3_GetFloat(sharedMem->entID, sharedMem->type, sharedMem->name, &sharedMem->value);
00626                 }
00627         case GAME_ICARUS_GETVECTOR:
00628                 {
00629                         T_G_ICARUS_GETVECTOR *sharedMem = (T_G_ICARUS_GETVECTOR *)gSharedBuffer;
00630                         return Q3_GetVector(sharedMem->entID, sharedMem->type, sharedMem->name, sharedMem->value);
00631                 }
00632         case GAME_ICARUS_GETSTRING:
00633                 {
00634                         T_G_ICARUS_GETSTRING *sharedMem = (T_G_ICARUS_GETSTRING *)gSharedBuffer;
00635                         int r;
00636                         char *crap = NULL; //I am sorry for this -rww
00637                         char **morecrap = &crap; //and this
00638                         r = Q3_GetString(sharedMem->entID, sharedMem->type, sharedMem->name, morecrap);
00639 
00640                         if (crap)
00641                         { //success!
00642                                 strcpy(sharedMem->value, crap);
00643                         }
00644 
00645                         return r;
00646                 }
00647         case GAME_ICARUS_SOUNDINDEX:
00648                 {
00649                         T_G_ICARUS_SOUNDINDEX *sharedMem = (T_G_ICARUS_SOUNDINDEX *)gSharedBuffer;
00650                         G_SoundIndex(sharedMem->filename);
00651                 }
00652                 return 0;
00653         case GAME_ICARUS_GETSETIDFORSTRING:
00654                 {
00655                         T_G_ICARUS_GETSETIDFORSTRING *sharedMem = (T_G_ICARUS_GETSETIDFORSTRING *)gSharedBuffer;
00656                         return GetIDForString(setTable, sharedMem->string);
00657                 }
00658         //rww - end icarus callbacks
00659 
00660         case GAME_NAV_CLEARPATHTOPOINT:
00661                 return NAV_ClearPathToPoint(&g_entities[arg0], (float *)arg1, (float *)arg2, (float *)arg3, arg4, arg5);
00662         case GAME_NAV_CLEARLOS:
00663                 return NPC_ClearLOS2(&g_entities[arg0], (const float *)arg1);
00664         case GAME_NAV_CLEARPATHBETWEENPOINTS:
00665                 return NAVNEW_ClearPathBetweenPoints((float *)arg0, (float *)arg1, (float *)arg2, (float *)arg3, arg4, arg5);
00666         case GAME_NAV_CHECKNODEFAILEDFORENT:
00667                 return NAV_CheckNodeFailedForEnt(&g_entities[arg0], arg1);
00668         case GAME_NAV_ENTISUNLOCKEDDOOR:
00669                 return G_EntIsUnlockedDoor(arg0);
00670         case GAME_NAV_ENTISDOOR:
00671                 return G_EntIsDoor(arg0);
00672         case GAME_NAV_ENTISBREAKABLE:
00673                 return G_EntIsBreakable(arg0);
00674         case GAME_NAV_ENTISREMOVABLEUSABLE:
00675                 return G_EntIsRemovableUsable(arg0);
00676         case GAME_NAV_FINDCOMBATPOINTWAYPOINTS:
00677                 CP_FindCombatPointWaypoints();
00678                 return 0;
00679         case GAME_GETITEMINDEXBYTAG:
00680                 return BG_GetItemIndexByTag(arg0, arg1);
00681         }
00682 
00683         return -1;
00684 }
00685 #ifdef __linux__
00686 }
00687 #endif
00688 #include "../namespace_end.h"
00689 
00690 
00691 void QDECL G_Printf( const char *fmt, ... ) {
00692         va_list         argptr;
00693         char            text[1024];
00694 
00695         va_start (argptr, fmt);
00696         vsprintf (text, fmt, argptr);
00697         va_end (argptr);
00698 
00699         trap_Printf( text );
00700 }
00701 
00702 void QDECL G_Error( const char *fmt, ... ) {
00703         va_list         argptr;
00704         char            text[1024];
00705 
00706         va_start (argptr, fmt);
00707         vsprintf (text, fmt, argptr);
00708         va_end (argptr);
00709 
00710         trap_Error( text );
00711 }
00712 
00713 /*
00714 ================
00715 G_FindTeams
00716 
00717 Chain together all entities with a matching team field.
00718 Entity teams are used for item groups and multi-entity mover groups.
00719 
00720 All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
00721 All but the last will have the teamchain field set to the next one
00722 ================
00723 */
00724 void G_FindTeams( void ) {
00725         gentity_t       *e, *e2;
00726         int             i, j;
00727         int             c, c2;
00728 
00729         c = 0;
00730         c2 = 0;
00731         for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){
00732                 if (!e->inuse)
00733                         continue;
00734                 if (!e->team)
00735                         continue;
00736                 if (e->flags & FL_TEAMSLAVE)
00737                         continue;
00738                 if (e->r.contents==CONTENTS_TRIGGER)
00739                         continue;//triggers NEVER link up in teams!
00740                 e->teammaster = e;
00741                 c++;
00742                 c2++;
00743                 for (j=i+1, e2=e+1 ; j < level.num_entities ; j++,e2++)
00744                 {
00745                         if (!e2->inuse)
00746                                 continue;
00747                         if (!e2->team)
00748                                 continue;
00749                         if (e2->flags & FL_TEAMSLAVE)
00750                                 continue;
00751                         if (!strcmp(e->team, e2->team))
00752                         {
00753                                 c2++;
00754                                 e2->teamchain = e->teamchain;
00755                                 e->teamchain = e2;
00756                                 e2->teammaster = e;
00757                                 e2->flags |= FL_TEAMSLAVE;
00758 
00759                                 // make sure that targets only point at the master
00760                                 if ( e2->targetname ) {
00761                                         e->targetname = e2->targetname;
00762                                         e2->targetname = NULL;
00763                                 }
00764                         }
00765                 }
00766         }
00767 
00768 //      G_Printf ("%i teams with %i entities\n", c, c2);
00769 }
00770 
00771 void G_RemapTeamShaders( void ) {
00772 #if 0
00773         char string[1024];
00774         float f = level.time * 0.001;
00775         Com_sprintf( string, sizeof(string), "team_icon/%s_red", g_redteam.string );
00776         AddRemap("textures/ctf2/redteam01", string, f); 
00777         AddRemap("textures/ctf2/redteam02", string, f); 
00778         Com_sprintf( string, sizeof(string), "team_icon/%s_blue", g_blueteam.string );
00779         AddRemap("textures/ctf2/blueteam01", string, f); 
00780         AddRemap("textures/ctf2/blueteam02", string, f); 
00781         trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
00782 #endif
00783 }
00784 
00785 
00786 /*
00787 =================
00788 G_RegisterCvars
00789 =================
00790 */
00791 void G_RegisterCvars( void ) {
00792         int                     i;
00793         cvarTable_t     *cv;
00794         qboolean remapped = qfalse;
00795 
00796         for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
00797                 trap_Cvar_Register( cv->vmCvar, cv->cvarName,
00798                         cv->defaultString, cv->cvarFlags );
00799                 if ( cv->vmCvar )
00800                         cv->modificationCount = cv->vmCvar->modificationCount;
00801 
00802                 if (cv->teamShader) {
00803                         remapped = qtrue;
00804                 }
00805         }
00806 
00807         if (remapped) {
00808                 G_RemapTeamShaders();
00809         }
00810 
00811         // check some things
00812         if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
00813                 G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer );
00814                 trap_Cvar_Set( "g_gametype", "0" );
00815         }
00816         else if (g_gametype.integer == GT_HOLOCRON)
00817         {
00818                 G_Printf( "This gametype is not supported.\n" );
00819                 trap_Cvar_Set( "g_gametype", "0" );
00820         }
00821         else if (g_gametype.integer == GT_JEDIMASTER)
00822         {
00823                 G_Printf( "This gametype is not supported.\n" );
00824                 trap_Cvar_Set( "g_gametype", "0" );
00825         }
00826         else if (g_gametype.integer == GT_CTY)
00827         {
00828                 G_Printf( "This gametype is not supported.\n" );
00829                 trap_Cvar_Set( "g_gametype", "0" );
00830         }
00831 
00832         level.warmupModificationCount = g_warmup.modificationCount;
00833 }
00834 
00835 /*
00836 =================
00837 G_UpdateCvars
00838 =================
00839 */
00840 void G_UpdateCvars( void ) {
00841         int                     i;
00842         cvarTable_t     *cv;
00843         qboolean remapped = qfalse;
00844 
00845         for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
00846                 if ( cv->vmCvar ) {
00847                         trap_Cvar_Update( cv->vmCvar );
00848 
00849                         if ( cv->modificationCount != cv->vmCvar->modificationCount ) {
00850                                 cv->modificationCount = cv->vmCvar->modificationCount;
00851 
00852                                 if ( cv->trackChange ) {
00853                                         trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"", 
00854                                                 cv->cvarName, cv->vmCvar->string ) );
00855                                 }
00856 
00857                                 if (cv->teamShader) {
00858                                         remapped = qtrue;
00859                                 }                               
00860                         }
00861                 }
00862         }
00863 
00864         if (remapped) {
00865                 G_RemapTeamShaders();
00866         }
00867 }
00868 
00869 char gSharedBuffer[MAX_G_SHARED_BUFFER_SIZE];
00870 
00871 #include "../namespace_begin.h"
00872 void WP_SaberLoadParms( void );
00873 void BG_VehicleLoadParms( void );
00874 #include "../namespace_end.h"
00875 
00876 /*
00877 ============
00878 G_InitGame
00879 
00880 ============
00881 */
00882 extern void RemoveAllWP(void);
00883 extern void BG_ClearVehicleParseParms(void);
00884 void G_InitGame( int levelTime, int randomSeed, int restart ) {
00885         int                                     i;
00886         vmCvar_t        mapname;
00887         vmCvar_t        ckSum;
00888 
00889 #ifdef _XBOX
00890         if(restart) {
00891                 BG_ClearVehicleParseParms();
00892                 RemoveAllWP();
00893         }
00894 #endif
00895 
00896         //Init RMG to 0, it will be autoset to 1 if there is terrain on the level.
00897         trap_Cvar_Set("RMG", "0");
00898         g_RMG.integer = 0;
00899 
00900         //Clean up any client-server ghoul2 instance attachments that may still exist exe-side
00901         trap_G2API_CleanEntAttachments();
00902 
00903         BG_InitAnimsets(); //clear it out
00904 
00905         B_InitAlloc(); //make sure everything is clean
00906 
00907         trap_SV_RegisterSharedMemory(gSharedBuffer);
00908 
00909         //Load external vehicle data
00910         BG_VehicleLoadParms();
00911 
00912         G_Printf ("------- Game Initialization -------\n");
00913         G_Printf ("gamename: %s\n", GAMEVERSION);
00914         G_Printf ("gamedate: %s\n", __DATE__);
00915 
00916         srand( randomSeed );
00917 
00918         G_RegisterCvars();
00919 
00920         G_ProcessIPBans();
00921 
00922         G_InitMemory();
00923 
00924         // set some level globals
00925         memset( &level, 0, sizeof( level ) );
00926         level.time = levelTime;
00927         level.startTime = levelTime;
00928 
00929         level.snd_fry = G_SoundIndex("sound/player/fry.wav");   // FIXME standing in lava / slime
00930 
00931         level.snd_hack = G_SoundIndex("sound/player/hacking.wav");
00932         level.snd_medHealed = G_SoundIndex("sound/player/supp_healed.wav");
00933         level.snd_medSupplied = G_SoundIndex("sound/player/supp_supplied.wav");
00934 
00935         //trap_SP_RegisterServer("mp_svgame");
00936 
00937 #ifndef _XBOX
00938         if ( g_log.string[0] ) {
00939                 if ( g_logSync.integer ) {
00940                         trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND_SYNC );
00941                 } else {
00942                         trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND );
00943                 }
00944                 if ( !level.logFile ) {
00945                         G_Printf( "WARNING: Couldn't open logfile: %s\n", g_log.string );
00946                 } else {
00947                         char    serverinfo[MAX_INFO_STRING];
00948 
00949                         trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
00950 
00951                         G_LogPrintf("------------------------------------------------------------\n" );
00952                         G_LogPrintf("InitGame: %s\n", serverinfo );
00953                 }
00954         } else {
00955                 G_Printf( "Not logging to disk.\n" );
00956         }
00957 #endif
00958 
00959         G_LogWeaponInit();
00960 
00961         G_InitWorldSession();
00962 
00963         // initialize all entities for this game
00964         memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
00965         level.gentities = g_entities;
00966 
00967         // initialize all clients for this game
00968         level.maxclients = g_maxclients.integer;
00969         memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) );
00970         level.clients = g_clients;
00971 
00972         // set client fields on player ents
00973         for ( i=0 ; i<level.maxclients ; i++ ) {
00974                 g_entities[i].client = level.clients + i;
00975         }
00976 
00977         // always leave room for the max number of clients,
00978         // even if they aren't all used, so numbers inside that
00979         // range are NEVER anything but clients
00980         level.num_entities = MAX_CLIENTS;
00981 
00982         // let the server system know where the entites are
00983         trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), 
00984                 &level.clients[0].ps, sizeof( level.clients[0] ) );
00985 
00986         //Load sabers.cfg data
00987         WP_SaberLoadParms();
00988 
00989         NPC_InitGame();
00990         
00991         TIMER_Clear();
00992         //
00993         //ICARUS INIT START
00994 
00995 //      Com_Printf("------ ICARUS Initialization ------\n");
00996 
00997         trap_ICARUS_Init();
00998 
00999 //      Com_Printf ("-----------------------------------\n");
01000 
01001         //ICARUS INIT END
01002         //
01003 
01004         // reserve some spots for dead player bodies
01005         InitBodyQue();
01006 
01007         ClearRegisteredItems();
01008 
01009         //make sure saber data is loaded before this! (so we can precache the appropriate hilts)
01010         InitSiegeMode();
01011 
01012         trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
01013         trap_Cvar_Register( &ckSum, "sv_mapChecksum", "", CVAR_ROM );
01014 
01015         navCalculatePaths       = ( trap_Nav_Load( mapname.string, ckSum.integer ) == qfalse );
01016 
01017         // parse the key/value pairs and spawn gentities
01018         G_SpawnEntitiesFromString(qfalse);
01019 
01020         // general initialization
01021         G_FindTeams();
01022 
01023         // make sure we have flags for CTF, etc
01024         if( g_gametype.integer >= GT_TEAM ) {
01025                 G_CheckTeamItems();
01026         }
01027         else if ( g_gametype.integer == GT_JEDIMASTER )
01028         {
01029                 trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, "-1" );
01030         }
01031 
01032         if (g_gametype.integer == GT_POWERDUEL)
01033         {
01034                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("-1|-1|-1") );
01035         }
01036         else
01037         {
01038                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("-1|-1") );
01039         }
01040 // nmckenzie: DUEL_HEALTH: Default.
01041         trap_SetConfigstring ( CS_CLIENT_DUELHEALTHS, va("-1|-1|!") );
01042         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("-1") );
01043 
01044         SaveRegisteredItems();
01045 
01046         //G_Printf ("-----------------------------------\n");
01047 
01048         if( g_gametype.integer == GT_SINGLE_PLAYER || trap_Cvar_VariableIntegerValue( "com_buildScript" ) ) {
01049                 G_ModelIndex( SP_PODIUM_MODEL );
01050                 G_SoundIndex( "sound/player/gurp1.wav" );
01051                 G_SoundIndex( "sound/player/gurp2.wav" );
01052         }
01053 
01054         if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
01055                 BotAISetup( restart );
01056                 BotAILoadMap( restart );
01057                 G_InitBots( restart );
01058         }
01059 
01060         G_RemapTeamShaders();
01061 
01062         if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL )
01063         {
01064                 G_LogPrintf("Duel Tournament Begun: kill limit %d, win limit: %d\n", g_fraglimit.integer, g_duel_fraglimit.integer );
01065         }
01066 
01067         if ( navCalculatePaths )
01068         {//not loaded - need to calc paths
01069                 navCalcPathTime = level.time + START_TIME_NAV_CALC;//make sure all ents are in and linked
01070         }
01071         else
01072         {//loaded
01073                 //FIXME: if this is from a loadgame, it needs to be sure to write this 
01074                 //out whenever you do a savegame since the edges and routes are dynamic...
01075                 //OR: always do a navigator.CheckBlockedEdges() on map startup after nav-load/calc-paths
01076                 //navigator.pathsCalculated = qtrue;//just to be safe?  Does this get saved out?  No... assumed
01077                 trap_Nav_SetPathsCalculated(qtrue);
01078                 //need to do this, because combatpoint waypoints aren't saved out...?
01079                 CP_FindCombatPointWaypoints();
01080                 navCalcPathTime = 0;
01081 
01082                 /*
01083                 if ( g_eSavedGameJustLoaded == eNO )
01084                 {//clear all the failed edges unless we just loaded the game (which would include failed edges)
01085                         trap_Nav_ClearAllFailedEdges();
01086                 }
01087                 */
01088                 //No loading games in MP.
01089         }
01090 
01091         if (g_gametype.integer == GT_SIEGE)
01092         { //just get these configstrings registered now...
01093                 int i = 0;
01094                 while (i < MAX_CUSTOM_SIEGE_SOUNDS)
01095                 {
01096                         if (!bg_customSiegeSoundNames[i])
01097                         {
01098                                 break;
01099                         }
01100                         G_SoundIndex((char *)bg_customSiegeSoundNames[i]);
01101                         i++;
01102                 }
01103         }
01104 }
01105 
01106 
01107 
01108 /*
01109 =================
01110 G_ShutdownGame
01111 =================
01112 */
01113 void G_ShutdownGame( int restart ) {
01114         int i = 0;
01115         gentity_t *ent;
01116 
01117 //      G_Printf ("==== ShutdownGame ====\n");
01118 
01119         G_CleanAllFakeClients(); //get rid of dynamically allocated fake client structs.
01120 
01121         BG_ClearAnimsets(); //free all dynamic allocations made through the engine
01122 
01123 //      Com_Printf("... Gameside GHOUL2 Cleanup\n");
01124         while (i < MAX_GENTITIES)
01125         { //clean up all the ghoul2 instances
01126                 ent = &g_entities[i];
01127 
01128                 if (ent->ghoul2 && trap_G2_HaveWeGhoul2Models(ent->ghoul2))
01129                 {
01130                         trap_G2API_CleanGhoul2Models(&ent->ghoul2);
01131                         ent->ghoul2 = NULL;
01132                 }
01133                 if (ent->client)
01134                 {
01135                         int j = 0;
01136 
01137                         while (j < MAX_SABERS)
01138                         {
01139                                 if (ent->client->weaponGhoul2[j] && trap_G2_HaveWeGhoul2Models(ent->client->weaponGhoul2[j]))
01140                                 {
01141                                         trap_G2API_CleanGhoul2Models(&ent->client->weaponGhoul2[j]);
01142                                 }
01143                                 j++;
01144                         }
01145                 }
01146                 i++;
01147         }
01148         if (g2SaberInstance && trap_G2_HaveWeGhoul2Models(g2SaberInstance))
01149         {
01150                 trap_G2API_CleanGhoul2Models(&g2SaberInstance);
01151                 g2SaberInstance = NULL;
01152         }
01153         if (precachedKyle && trap_G2_HaveWeGhoul2Models(precachedKyle))
01154         {
01155                 trap_G2API_CleanGhoul2Models(&precachedKyle);
01156                 precachedKyle = NULL;
01157         }
01158 
01159 //      Com_Printf ("... ICARUS_Shutdown\n");
01160         trap_ICARUS_Shutdown ();        //Shut ICARUS down
01161 
01162 //      Com_Printf ("... Reference Tags Cleared\n");
01163         TAG_Init();     //Clear the reference tags
01164 
01165         G_LogWeaponOutput();
01166 
01167         if ( level.logFile ) {
01168                 G_LogPrintf("ShutdownGame:\n" );
01169                 G_LogPrintf("------------------------------------------------------------\n" );
01170                 trap_FS_FCloseFile( level.logFile );
01171         }
01172 
01173         // write all the client session data so we can get it back
01174         G_WriteSessionData();
01175 
01176         trap_ROFF_Clean();
01177 
01178         if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
01179                 BotAIShutdown( restart );
01180         }
01181 
01182         B_CleanupAlloc(); //clean up all allocations made with B_Alloc
01183 }
01184 
01185 
01186 
01187 //===================================================================
01188 
01189 #ifndef GAME_HARD_LINKED
01190 // this is only here so the functions in q_shared.c and bg_*.c can link
01191 
01192 void QDECL Com_Error ( int level, const char *error, ... ) {
01193         va_list         argptr;
01194         char            text[1024];
01195 
01196         va_start (argptr, error);
01197         vsprintf (text, error, argptr);
01198         va_end (argptr);
01199 
01200         G_Error( "%s", text);
01201 }
01202 
01203 void QDECL Com_Printf( const char *msg, ... ) {
01204         va_list         argptr;
01205         char            text[1024];
01206 
01207         va_start (argptr, msg);
01208         vsprintf (text, msg, argptr);
01209         va_end (argptr);
01210 
01211         G_Printf ("%s", text);
01212 }
01213 
01214 #endif
01215 
01216 /*
01217 ========================================================================
01218 
01219 PLAYER COUNTING / SCORE SORTING
01220 
01221 ========================================================================
01222 */
01223 
01224 /*
01225 =============
01226 AddTournamentPlayer
01227 
01228 If there are less than two tournament players, put a
01229 spectator in the game and restart
01230 =============
01231 */
01232 void AddTournamentPlayer( void ) {
01233         int                     i;
01234         gclient_t       *client;
01235         gclient_t       *nextInLine;
01236 
01237         if ( level.numPlayingClients >= 2 ) {
01238                 return;
01239         }
01240 
01241         // never change during intermission
01242 //      if ( level.intermissiontime ) {
01243 //              return;
01244 //      }
01245 
01246         nextInLine = NULL;
01247 
01248         for ( i = 0 ; i < level.maxclients ; i++ ) {
01249                 client = &level.clients[i];
01250                 if ( client->pers.connected != CON_CONNECTED ) {
01251                         continue;
01252                 }
01253                 if (!g_allowHighPingDuelist.integer && client->ps.ping >= 999)
01254                 { //don't add people who are lagging out if cvar is not set to allow it.
01255                         continue;
01256                 }
01257                 if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
01258                         continue;
01259                 }
01260                 // never select the dedicated follow or scoreboard clients
01261                 if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD || 
01262                         client->sess.spectatorClient < 0  ) {
01263                         continue;
01264                 }
01265 
01266                 if ( !nextInLine || client->sess.spectatorTime < nextInLine->sess.spectatorTime ) {
01267                         nextInLine = client;
01268                 }
01269         }
01270 
01271         if ( !nextInLine ) {
01272                 return;
01273         }
01274 
01275         level.warmupTime = -1;
01276 
01277         // set them to free-for-all team
01278         SetTeam( &g_entities[ nextInLine - level.clients ], "f" );
01279 }
01280 
01281 /*
01282 =======================
01283 RemoveTournamentLoser
01284 
01285 Make the loser a spectator at the back of the line
01286 =======================
01287 */
01288 void RemoveTournamentLoser( void ) {
01289         int                     clientNum;
01290 
01291         if ( level.numPlayingClients != 2 ) {
01292                 return;
01293         }
01294 
01295         clientNum = level.sortedClients[1];
01296 
01297         if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) {
01298                 return;
01299         }
01300 
01301         // make them a spectator
01302         SetTeam( &g_entities[ clientNum ], "s" );
01303 }
01304 
01305 void G_PowerDuelCount(int *loners, int *doubles, qboolean countSpec)
01306 {
01307         int i = 0;
01308         gclient_t *cl;
01309 
01310         while (i < MAX_CLIENTS)
01311         {
01312                 cl = g_entities[i].client;
01313 
01314                 if (g_entities[i].inuse && cl && (countSpec || cl->sess.sessionTeam != TEAM_SPECTATOR))
01315                 {
01316                         if (cl->sess.duelTeam == DUELTEAM_LONE)
01317                         {
01318                                 (*loners)++;
01319                         }
01320                         else if (cl->sess.duelTeam == DUELTEAM_DOUBLE)
01321                         {
01322                                 (*doubles)++;
01323                         }
01324                 }
01325                 i++;
01326         }
01327 }
01328 
01329 qboolean g_duelAssigning = qfalse;
01330 void AddPowerDuelPlayers( void )
01331 {
01332         int                     i;
01333         int                     loners = 0;
01334         int                     doubles = 0;
01335         int                     nonspecLoners = 0;
01336         int                     nonspecDoubles = 0;
01337         gclient_t       *client;
01338         gclient_t       *nextInLine;
01339 
01340         if ( level.numPlayingClients >= 3 )
01341         {
01342                 return;
01343         }
01344 
01345         nextInLine = NULL;
01346 
01347         G_PowerDuelCount(&nonspecLoners, &nonspecDoubles, qfalse);
01348         if (nonspecLoners >= 1 && nonspecDoubles >= 2)
01349         { //we have enough people, stop
01350                 return;
01351         }
01352 
01353         //Could be written faster, but it's not enough to care I suppose.
01354         G_PowerDuelCount(&loners, &doubles, qtrue);
01355 
01356         if (loners < 1 || doubles < 2)
01357         { //don't bother trying to spawn anyone yet if the balance is not even set up between spectators
01358                 return;
01359         }
01360 
01361         //Count again, with only in-game clients in mind.
01362         loners = nonspecLoners;
01363         doubles = nonspecDoubles;
01364 //      G_PowerDuelCount(&loners, &doubles, qfalse);
01365 
01366         for ( i = 0 ; i < level.maxclients ; i++ ) {
01367                 client = &level.clients[i];
01368                 if ( client->pers.connected != CON_CONNECTED ) {
01369                         continue;
01370                 }
01371                 if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
01372                         continue;
01373                 }
01374                 if (client->sess.duelTeam == DUELTEAM_FREE)
01375                 {
01376                         continue;
01377                 }
01378                 if (client->sess.duelTeam == DUELTEAM_LONE && loners >= 1)
01379                 {
01380                         continue;
01381                 }
01382                 if (client->sess.duelTeam == DUELTEAM_DOUBLE && doubles >= 2)
01383                 {
01384                         continue;
01385                 }
01386 
01387                 // never select the dedicated follow or scoreboard clients
01388                 if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD || 
01389                         client->sess.spectatorClient < 0  ) {
01390                         continue;
01391                 }
01392 
01393                 if ( !nextInLine || client->sess.spectatorTime < nextInLine->sess.spectatorTime ) {
01394                         nextInLine = client;
01395                 }
01396         }
01397 
01398         if ( !nextInLine ) {
01399                 return;
01400         }
01401 
01402         level.warmupTime = -1;
01403 
01404         // set them to free-for-all team
01405         SetTeam( &g_entities[ nextInLine - level.clients ], "f" );
01406 
01407         //Call recursively until everyone is in
01408         AddPowerDuelPlayers();
01409 }
01410 
01411 qboolean g_dontFrickinCheck = qfalse;
01412 
01413 void RemovePowerDuelLosers(void)
01414 {
01415         int remClients[3];
01416         int remNum = 0;
01417         int i = 0;
01418         gclient_t *cl;
01419 
01420         while (i < MAX_CLIENTS && remNum < 3)
01421         {
01422                 //cl = &level.clients[level.sortedClients[i]];
01423                 cl = &level.clients[i];
01424 
01425                 if (cl->pers.connected == CON_CONNECTED)
01426                 {
01427                         if ((cl->ps.stats[STAT_HEALTH] <= 0 || cl->iAmALoser) &&
01428                                 (cl->sess.sessionTeam != TEAM_SPECTATOR || cl->iAmALoser))
01429                         { //he was dead or he was spectating as a loser
01430                 remClients[remNum] = cl->ps.clientNum;
01431                                 remNum++;
01432                         }
01433                 }
01434 
01435                 i++;
01436         }
01437 
01438         if (!remNum)
01439         { //Time ran out or something? Oh well, just remove the main guy.
01440                 remClients[remNum] = level.sortedClients[0];
01441                 remNum++;
01442         }
01443 
01444         i = 0;
01445         while (i < remNum)
01446         { //set them all to spectator
01447                 SetTeam( &g_entities[ remClients[i] ], "s" );
01448                 i++;
01449         }
01450 
01451         g_dontFrickinCheck = qfalse;
01452 
01453         //recalculate stuff now that we have reset teams.
01454         CalculateRanks();
01455 }
01456 
01457 void RemoveDuelDrawLoser(void)
01458 {
01459         int clFirst = 0;
01460         int clSec = 0;
01461         int clFailure = 0;
01462 
01463         if ( level.clients[ level.sortedClients[0] ].pers.connected != CON_CONNECTED )
01464         {
01465                 return;
01466         }
01467         if ( level.clients[ level.sortedClients[1] ].pers.connected != CON_CONNECTED )
01468         {
01469                 return;
01470         }
01471 
01472         clFirst = level.clients[ level.sortedClients[0] ].ps.stats[STAT_HEALTH] + level.clients[ level.sortedClients[0] ].ps.stats[STAT_ARMOR];
01473         clSec = level.clients[ level.sortedClients[1] ].ps.stats[STAT_HEALTH] + level.clients[ level.sortedClients[1] ].ps.stats[STAT_ARMOR];
01474 
01475         if (clFirst > clSec)
01476         {
01477                 clFailure = 1;
01478         }
01479         else if (clSec > clFirst)
01480         {
01481                 clFailure = 0;
01482         }
01483         else
01484         {
01485                 clFailure = 2;
01486         }
01487 
01488         if (clFailure != 2)
01489         {
01490                 SetTeam( &g_entities[ level.sortedClients[clFailure] ], "s" );
01491         }
01492         else
01493         { //we could be more elegant about this, but oh well.
01494                 SetTeam( &g_entities[ level.sortedClients[1] ], "s" );
01495         }
01496 }
01497 
01498 /*
01499 =======================
01500 RemoveTournamentWinner
01501 =======================
01502 */
01503 void RemoveTournamentWinner( void ) {
01504         int                     clientNum;
01505 
01506         if ( level.numPlayingClients != 2 ) {
01507                 return;
01508         }
01509 
01510         clientNum = level.sortedClients[0];
01511 
01512         if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) {
01513                 return;
01514         }
01515 
01516         // make them a spectator
01517         SetTeam( &g_entities[ clientNum ], "s" );
01518 }
01519 
01520 /*
01521 =======================
01522 AdjustTournamentScores
01523 =======================
01524 */
01525 void AdjustTournamentScores( void ) {
01526         int                     clientNum;
01527 
01528         if (level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE] ==
01529                 level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE] &&
01530                 level.clients[level.sortedClients[0]].pers.connected == CON_CONNECTED &&
01531                 level.clients[level.sortedClients[1]].pers.connected == CON_CONNECTED)
01532         {
01533                 int clFirst = level.clients[ level.sortedClients[0] ].ps.stats[STAT_HEALTH] + level.clients[ level.sortedClients[0] ].ps.stats[STAT_ARMOR];
01534                 int clSec = level.clients[ level.sortedClients[1] ].ps.stats[STAT_HEALTH] + level.clients[ level.sortedClients[1] ].ps.stats[STAT_ARMOR];
01535                 int clFailure = 0;
01536                 int clSuccess = 0;
01537 
01538                 if (clFirst > clSec)
01539                 {
01540                         clFailure = 1;
01541                         clSuccess = 0;
01542                 }
01543                 else if (clSec > clFirst)
01544                 {
01545                         clFailure = 0;
01546                         clSuccess = 1;
01547                 }
01548                 else
01549                 {
01550                         clFailure = 2;
01551                         clSuccess = 2;
01552                 }
01553 
01554                 if (clFailure != 2)
01555                 {
01556                         clientNum = level.sortedClients[clSuccess];
01557 
01558                         level.clients[ clientNum ].sess.wins++;
01559                         ClientUserinfoChanged( clientNum );
01560                         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", clientNum ) );
01561 
01562                         clientNum = level.sortedClients[clFailure];
01563 
01564                         level.clients[ clientNum ].sess.losses++;
01565                         ClientUserinfoChanged( clientNum );
01566                 }
01567                 else
01568                 {
01569                         clSuccess = 0;
01570                         clFailure = 1;
01571 
01572                         clientNum = level.sortedClients[clSuccess];
01573 
01574                         level.clients[ clientNum ].sess.wins++;
01575                         ClientUserinfoChanged( clientNum );
01576                         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", clientNum ) );
01577 
01578                         clientNum = level.sortedClients[clFailure];
01579 
01580                         level.clients[ clientNum ].sess.losses++;
01581                         ClientUserinfoChanged( clientNum );
01582                 }
01583         }
01584         else
01585         {
01586                 clientNum = level.sortedClients[0];
01587                 if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
01588                         level.clients[ clientNum ].sess.wins++;
01589                         ClientUserinfoChanged( clientNum );
01590 
01591                         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", clientNum ) );
01592                 }
01593 
01594                 clientNum = level.sortedClients[1];
01595                 if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
01596                         level.clients[ clientNum ].sess.losses++;
01597                         ClientUserinfoChanged( clientNum );
01598                 }
01599         }
01600 }
01601 
01602 /*
01603 =============
01604 SortRanks
01605 
01606 =============
01607 */
01608 int QDECL SortRanks( const void *a, const void *b ) {
01609         gclient_t       *ca, *cb;
01610 
01611         ca = &level.clients[*(int *)a];
01612         cb = &level.clients[*(int *)b];
01613 
01614         if (g_gametype.integer == GT_POWERDUEL)
01615         {
01616                 //sort single duelists first
01617                 if (ca->sess.duelTeam == DUELTEAM_LONE && ca->sess.sessionTeam != TEAM_SPECTATOR)
01618                 {
01619                         return -1;
01620                 }
01621                 if (cb->sess.duelTeam == DUELTEAM_LONE && cb->sess.sessionTeam != TEAM_SPECTATOR)
01622                 {
01623                         return 1;
01624                 }
01625 
01626                 //others will be auto-sorted below but above spectators.
01627         }
01628 
01629         // sort special clients last
01630         if ( ca->sess.spectatorState == SPECTATOR_SCOREBOARD || ca->sess.spectatorClient < 0 ) {
01631                 return 1;
01632         }
01633         if ( cb->sess.spectatorState == SPECTATOR_SCOREBOARD || cb->sess.spectatorClient < 0  ) {
01634                 return -1;
01635         }
01636 
01637         // then connecting clients
01638         if ( ca->pers.connected == CON_CONNECTING ) {
01639                 return 1;
01640         }
01641         if ( cb->pers.connected == CON_CONNECTING ) {
01642                 return -1;
01643         }
01644 
01645 
01646         // then spectators
01647         if ( ca->sess.sessionTeam == TEAM_SPECTATOR && cb->sess.sessionTeam == TEAM_SPECTATOR ) {
01648                 if ( ca->sess.spectatorTime < cb->sess.spectatorTime ) {
01649                         return -1;
01650                 }
01651                 if ( ca->sess.spectatorTime > cb->sess.spectatorTime ) {
01652                         return 1;
01653                 }
01654                 return 0;
01655         }
01656         if ( ca->sess.sessionTeam == TEAM_SPECTATOR ) {
01657                 return 1;
01658         }
01659         if ( cb->sess.sessionTeam == TEAM_SPECTATOR ) {
01660                 return -1;
01661         }
01662 
01663         // then sort by score
01664         if ( ca->ps.persistant[PERS_SCORE]
01665                 > cb->ps.persistant[PERS_SCORE] ) {
01666                 return -1;
01667         }
01668         if ( ca->ps.persistant[PERS_SCORE]
01669                 < cb->ps.persistant[PERS_SCORE] ) {
01670                 return 1;
01671         }
01672         return 0;
01673 }
01674 
01675 qboolean gQueueScoreMessage = qfalse;
01676 int gQueueScoreMessageTime = 0;
01677 
01678 //A new duel started so respawn everyone and make sure their stats are reset
01679 qboolean G_CanResetDuelists(void)
01680 {
01681         int i;
01682         gentity_t *ent;
01683 
01684         i = 0;
01685         while (i < 3)
01686         { //precheck to make sure they are all respawnable
01687                 ent = &g_entities[level.sortedClients[i]];
01688 
01689                 if (!ent->inuse || !ent->client || ent->health <= 0 ||
01690                         ent->client->sess.sessionTeam == TEAM_SPECTATOR ||
01691                         ent->client->sess.duelTeam <= DUELTEAM_FREE)
01692                 {
01693                         return qfalse;
01694                 }
01695                 i++;
01696         }
01697 
01698         return qtrue;
01699 }
01700 
01701 qboolean g_noPDuelCheck = qfalse;
01702 void G_ResetDuelists(void)
01703 {
01704         int i;
01705         gentity_t *ent;
01706         gentity_t *tent;
01707 
01708         i = 0;
01709         while (i < 3)
01710         {
01711                 ent = &g_entities[level.sortedClients[i]];
01712 
01713                 g_noPDuelCheck = qtrue;
01714                 player_die(ent, ent, ent, 999, MOD_SUICIDE);
01715                 g_noPDuelCheck = qfalse;
01716                 trap_UnlinkEntity (ent);
01717                 ClientSpawn(ent);
01718 
01719                 // add a teleportation effect
01720                 tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
01721                 tent->s.clientNum = ent->s.clientNum;
01722                 i++;
01723         }
01724 }
01725 
01726 /*
01727 ============
01728 CalculateRanks
01729 
01730 Recalculates the score ranks of all players
01731 This will be called on every client connect, begin, disconnect, death,
01732 and team change.
01733 ============
01734 */
01735 void CalculateRanks( void ) {
01736         int             i;
01737         int             rank;
01738         int             score;
01739         int             newScore;
01740         int             preNumSpec = 0;
01741         //int           nonSpecIndex = -1;
01742         gclient_t       *cl;
01743 
01744         preNumSpec = level.numNonSpectatorClients;
01745 
01746         level.follow1 = -1;
01747         level.follow2 = -1;
01748         level.numConnectedClients = 0;
01749         level.numNonSpectatorClients = 0;
01750         level.numPlayingClients = 0;
01751         level.numVotingClients = 0;             // don't count bots
01752         for ( i = 0; i < TEAM_NUM_TEAMS; i++ ) {
01753                 level.numteamVotingClients[i] = 0;
01754         }
01755         for ( i = 0 ; i < level.maxclients ; i++ ) {
01756                 if ( level.clients[i].pers.connected != CON_DISCONNECTED ) {
01757                         level.sortedClients[level.numConnectedClients] = i;
01758                         level.numConnectedClients++;
01759 
01760                         if ( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR || g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL )
01761                         {
01762                                 if (level.clients[i].sess.sessionTeam != TEAM_SPECTATOR)
01763                                 {
01764                                         level.numNonSpectatorClients++;
01765                                         //nonSpecIndex = i;
01766                                 }
01767                         
01768                                 // decide if this should be auto-followed
01769                                 if ( level.clients[i].pers.connected == CON_CONNECTED )
01770                                 {
01771                                         if (level.clients[i].sess.sessionTeam != TEAM_SPECTATOR || level.clients[i].iAmALoser)
01772                                         {
01773                                                 level.numPlayingClients++;
01774                                         }
01775                                         if ( !(g_entities[i].r.svFlags & SVF_BOT) )
01776                                         {
01777                                                 level.numVotingClients++;
01778                                                 if ( level.clients[i].sess.sessionTeam == TEAM_RED )
01779                                                         level.numteamVotingClients[0]++;
01780                                                 else if ( level.clients[i].sess.sessionTeam == TEAM_BLUE )
01781                                                         level.numteamVotingClients[1]++;
01782                                         }
01783                                         if ( level.follow1 == -1 ) {
01784                                                 level.follow1 = i;
01785                                         } else if ( level.follow2 == -1 ) {
01786                                                 level.follow2 = i;
01787                                         }
01788                                 }
01789                         }
01790                 }
01791         }
01792 
01793         //if (!g_warmup.integer)
01794         if (1)
01795         {
01796                 level.warmupTime = 0;
01797         }
01798 
01799         /*
01800         if (level.numNonSpectatorClients == 2 && preNumSpec < 2 && nonSpecIndex != -1 && g_gametype.integer == GT_DUEL && !level.warmupTime)
01801         {
01802                 gentity_t *currentWinner = G_GetDuelWinner(&level.clients[nonSpecIndex]);
01803 
01804                 if (currentWinner && currentWinner->client)
01805                 {
01806                         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s %s\n\"",
01807                         currentWinner->client->pers.netname, G_GetStringEdString("MP_SVGAME", "VERSUS"), level.clients[nonSpecIndex].pers.netname));
01808                 }
01809         }
01810         */
01811         //NOTE: for now not doing this either. May use later if appropriate.
01812 
01813         qsort( level.sortedClients, level.numConnectedClients, 
01814                 sizeof(level.sortedClients[0]), SortRanks );
01815 
01816         // set the rank value for all clients that are connected and not spectators
01817         if ( g_gametype.integer >= GT_TEAM ) {
01818                 // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied
01819                 for ( i = 0;  i < level.numConnectedClients; i++ ) {
01820                         cl = &level.clients[ level.sortedClients[i] ];
01821                         if ( level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] ) {
01822                                 cl->ps.persistant[PERS_RANK] = 2;
01823                         } else if ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) {
01824                                 cl->ps.persistant[PERS_RANK] = 0;
01825                         } else {
01826                                 cl->ps.persistant[PERS_RANK] = 1;
01827                         }
01828                 }
01829         } else {        
01830                 rank = -1;
01831                 score = 0;
01832                 for ( i = 0;  i < level.numPlayingClients; i++ ) {
01833                         cl = &level.clients[ level.sortedClients[i] ];
01834                         newScore = cl->ps.persistant[PERS_SCORE];
01835                         if ( i == 0 || newScore != score ) {
01836                                 rank = i;
01837                                 // assume we aren't tied until the next client is checked
01838                                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank;
01839                         } else {
01840                                 // we are tied with the previous client
01841                                 level.clients[ level.sortedClients[i-1] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
01842                                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
01843                         }
01844                         score = newScore;
01845                         if ( g_gametype.integer == GT_SINGLE_PLAYER && level.numPlayingClients == 1 ) {
01846                                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
01847                         }
01848                 }
01849         }
01850 
01851         // set the CS_SCORES1/2 configstrings, which will be visible to everyone
01852         if ( g_gametype.integer >= GT_TEAM ) {
01853                 trap_SetConfigstring( CS_SCORES1, va("%i", level.teamScores[TEAM_RED] ) );
01854                 trap_SetConfigstring( CS_SCORES2, va("%i", level.teamScores[TEAM_BLUE] ) );
01855         } else {
01856                 if ( level.numConnectedClients == 0 ) {
01857                         trap_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) );
01858                         trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
01859                 } else if ( level.numConnectedClients == 1 ) {
01860                         trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
01861                         trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
01862                 } else {
01863                         trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
01864                         trap_SetConfigstring( CS_SCORES2, va("%i", level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] ) );
01865                 }
01866 
01867                 if (g_gametype.integer != GT_DUEL || g_gametype.integer != GT_POWERDUEL)
01868                 { //when not in duel, use this configstring to pass the index of the player currently in first place
01869                         if ( level.numConnectedClients >= 1 )
01870                         {
01871                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", level.sortedClients[0] ) );
01872                         }
01873                         else
01874                         {
01875                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
01876                         }
01877                 }
01878         }
01879 
01880         // see if it is time to end the level
01881         CheckExitRules();
01882 
01883         // if we are at the intermission or in multi-frag Duel game mode, send the new info to everyone
01884         if ( level.intermissiontime || g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL ) {
01885                 gQueueScoreMessage = qtrue;
01886                 gQueueScoreMessageTime = level.time + 500;
01887                 //SendScoreboardMessageToAllClients();
01888                 //rww - Made this operate on a "queue" system because it was causing large overflows
01889         }
01890 }
01891 
01892 
01893 /*
01894 ========================================================================
01895 
01896 MAP CHANGING
01897 
01898 ========================================================================
01899 */
01900 
01901 /*
01902 ========================
01903 SendScoreboardMessageToAllClients
01904 
01905 Do this at BeginIntermission time and whenever ranks are recalculated
01906 due to enters/exits/forced team changes
01907 ========================
01908 */
01909 void SendScoreboardMessageToAllClients( void ) {
01910         int             i;
01911 
01912         for ( i = 0 ; i < level.maxclients ; i++ ) {
01913                 if ( level.clients[ i ].pers.connected == CON_CONNECTED ) {
01914                         DeathmatchScoreboardMessage( g_entities + i );
01915                 }
01916         }
01917 }
01918 
01919 /*
01920 ========================
01921 MoveClientToIntermission
01922 
01923 When the intermission starts, this will be called for all players.
01924 If a new client connects, this will be called after the spawn function.
01925 ========================
01926 */
01927 void MoveClientToIntermission( gentity_t *ent ) {
01928         // take out of follow mode if needed
01929         if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
01930                 StopFollowing( ent );
01931         }
01932 
01933 
01934         // move to the spot
01935         VectorCopy( level.intermission_origin, ent->s.origin );
01936         VectorCopy( level.intermission_origin, ent->client->ps.origin );
01937         VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
01938         ent->client->ps.pm_type = PM_INTERMISSION;
01939 
01940         // clean up powerup info
01941         memset( ent->client->ps.powerups, 0, sizeof(ent->client->ps.powerups) );
01942 
01943         ent->client->ps.eFlags = 0;
01944         ent->s.eFlags = 0;
01945         ent->s.eType = ET_GENERAL;
01946         ent->s.modelindex = 0;
01947         ent->s.loopSound = 0;
01948         ent->s.loopIsSoundset = qfalse;
01949         ent->s.event = 0;
01950         ent->r.contents = 0;
01951 }
01952 
01953 /*
01954 ==================
01955 FindIntermissionPoint
01956 
01957 This is also used for spectator spawns
01958 ==================
01959 */
01960 extern qboolean gSiegeRoundBegun;
01961 extern qboolean gSiegeRoundEnded;
01962 extern qboolean gSiegeRoundWinningTeam;
01963 void FindIntermissionPoint( void ) {
01964         gentity_t       *ent = NULL;
01965         gentity_t       *target;
01966         vec3_t          dir;
01967 
01968         // find the intermission spot
01969         if ( g_gametype.integer == GT_SIEGE
01970                 && level.intermissiontime
01971                 && level.intermissiontime <= level.time
01972                 && gSiegeRoundEnded )
01973         {
01974                 if (gSiegeRoundWinningTeam == SIEGETEAM_TEAM1)
01975                 {
01976                         ent = G_Find (NULL, FOFS(classname), "info_player_intermission_red");
01977                         if ( ent && ent->target2 ) 
01978                         {
01979                                 G_UseTargets2( ent, ent, ent->target2 );
01980                         }
01981                 }
01982                 else if (gSiegeRoundWinningTeam == SIEGETEAM_TEAM2)
01983                 {
01984                         ent = G_Find (NULL, FOFS(classname), "info_player_intermission_blue");
01985                         if ( ent && ent->target2 ) 
01986                         {
01987                                 G_UseTargets2( ent, ent, ent->target2 );
01988                         }
01989                 }
01990         }
01991         if ( !ent )
01992         {
01993                 ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
01994         }
01995         if ( !ent ) {   // the map creator forgot to put in an intermission point...
01996                 SelectSpawnPoint ( vec3_origin, level.intermission_origin, level.intermission_angle, TEAM_SPECTATOR );
01997         } else {
01998                 VectorCopy (ent->s.origin, level.intermission_origin);
01999                 VectorCopy (ent->s.angles, level.intermission_angle);
02000                 // if it has a target, look towards it
02001                 if ( ent->target ) {
02002                         target = G_PickTarget( ent->target );
02003                         if ( target ) {
02004                                 VectorSubtract( target->s.origin, level.intermission_origin, dir );
02005                                 vectoangles( dir, level.intermission_angle );
02006                         }
02007                 }
02008         }
02009 
02010 }
02011 
02012 qboolean DuelLimitHit(void);
02013 
02014 /*
02015 ==================
02016 BeginIntermission
02017 ==================
02018 */
02019 void BeginIntermission( void ) {
02020         int                     i;
02021         gentity_t       *client;
02022 
02023         if ( level.intermissiontime ) {
02024                 return;         // already active
02025         }
02026 
02027         // if in tournement mode, change the wins / losses
02028         if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL ) {
02029                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
02030 
02031                 if (g_gametype.integer != GT_POWERDUEL)
02032                 {
02033                         AdjustTournamentScores();
02034                 }
02035                 if (DuelLimitHit())
02036                 {
02037                         gDuelExit = qtrue;
02038                 }
02039                 else
02040                 {
02041                         gDuelExit = qfalse;
02042                 }
02043         }
02044 
02045         level.intermissiontime = level.time;
02046         FindIntermissionPoint();
02047 
02048         /*
02049         if (g_singlePlayer.integer) {
02050                 trap_Cvar_Set("ui_singlePlayerActive", "0");
02051                 UpdateTournamentInfo();
02052         }
02053         */
02054         //what the? Well, I don't want this to happen.
02055 
02056         // move all clients to the intermission point
02057         for (i=0 ; i< level.maxclients ; i++) {
02058                 client = g_entities + i;
02059                 if (!client->inuse)
02060                         continue;
02061                 // respawn if dead
02062                 if (client->health <= 0) {
02063                         if (g_gametype.integer != GT_POWERDUEL ||
02064                                 !client->client ||
02065                                 client->client->sess.sessionTeam != TEAM_SPECTATOR)
02066                         { //don't respawn spectators in powerduel or it will mess the line order all up
02067                                 respawn(client);
02068                         }
02069                 }
02070                 MoveClientToIntermission( client );
02071         }
02072 
02073         // send the current scoring to all clients
02074         SendScoreboardMessageToAllClients();
02075 
02076 }
02077 
02078 qboolean DuelLimitHit(void)
02079 {
02080         int i;
02081         gclient_t *cl;
02082 
02083         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
02084                 cl = level.clients + i;
02085                 if ( cl->pers.connected != CON_CONNECTED ) {
02086                         continue;
02087                 }
02088 
02089                 if ( g_duel_fraglimit.integer && cl->sess.wins >= g_duel_fraglimit.integer )
02090                 {
02091                         return qtrue;
02092                 }
02093         }
02094 
02095         return qfalse;
02096 }
02097 
02098 void DuelResetWinsLosses(void)
02099 {
02100         int i;
02101         gclient_t *cl;
02102 
02103         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
02104                 cl = level.clients + i;
02105                 if ( cl->pers.connected != CON_CONNECTED ) {
02106                         continue;
02107                 }
02108 
02109                 cl->sess.wins = 0;
02110                 cl->sess.losses = 0;
02111         }
02112 }
02113 
02114 /*
02115 =============
02116 ExitLevel
02117 
02118 When the intermission has been exited, the server is either killed
02119 or moved to a new level based on the "nextmap" cvar 
02120 
02121 =============
02122 */
02123 extern void SiegeDoTeamAssign(void); //g_saga.c
02124 extern siegePers_t g_siegePersistant; //g_saga.c
02125 void ExitLevel (void) {
02126         int             i;
02127         gclient_t *cl;
02128 
02129         // if we are running a tournement map, kick the loser to spectator status,
02130         // which will automatically grab the next spectator and restart
02131         if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL ) {
02132                 if (!DuelLimitHit())
02133                 {
02134                         if ( !level.restarted ) {
02135                                 trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
02136                                 level.restarted = qtrue;
02137                                 level.changemap = NULL;
02138                                 level.intermissiontime = 0;
02139                         }
02140                         return; 
02141                 }
02142 
02143                 DuelResetWinsLosses();
02144         }
02145 
02146 
02147         if (g_gametype.integer == GT_SIEGE &&
02148                 g_siegeTeamSwitch.integer &&
02149                 g_siegePersistant.beatingTime)
02150         { //restart same map...
02151                 trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
02152         }
02153         else
02154         {
02155                 trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
02156         }
02157         level.changemap = NULL;
02158         level.intermissiontime = 0;
02159 
02160         if (g_gametype.integer == GT_SIEGE &&
02161                 g_siegeTeamSwitch.integer)
02162         { //switch out now
02163                 SiegeDoTeamAssign();
02164         }
02165 
02166         // reset all the scores so we don't enter the intermission again
02167         level.teamScores[TEAM_RED] = 0;
02168         level.teamScores[TEAM_BLUE] = 0;
02169         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
02170                 cl = level.clients + i;
02171                 if ( cl->pers.connected != CON_CONNECTED ) {
02172                         continue;
02173                 }
02174                 cl->ps.persistant[PERS_SCORE] = 0;
02175         }
02176 
02177         // we need to do this here before chaning to CON_CONNECTING
02178         G_WriteSessionData();
02179 
02180         // change all client states to connecting, so the early players into the
02181         // next level will know the others aren't done reconnecting
02182         for (i=0 ; i< g_maxclients.integer ; i++) {
02183                 if ( level.clients[i].pers.connected == CON_CONNECTED ) {
02184                         level.clients[i].pers.connected = CON_CONNECTING;
02185                 }
02186         }
02187 
02188 }
02189 
02190 /*
02191 =================
02192 G_LogPrintf
02193 
02194 Print to the logfile with a time stamp if it is open
02195 =================
02196 */
02197 void QDECL G_LogPrintf( const char *fmt, ... ) {
02198         va_list         argptr;
02199         char            string[1024];
02200         int                     min, tens, sec;
02201 
02202         sec = level.time / 1000;
02203 
02204         min = sec / 60;
02205         sec -= min * 60;
02206         tens = sec / 10;
02207         sec -= tens * 10;
02208 
02209         Com_sprintf( string, sizeof(string), "%3i:%i%i ", min, tens, sec );
02210 
02211         va_start( argptr, fmt );
02212         vsprintf( string +7 , fmt,argptr );
02213         va_end( argptr );
02214 
02215         if ( g_dedicated.integer ) {
02216                 G_Printf( "%s", string + 7 );
02217         }
02218 
02219         if ( !level.logFile ) {
02220                 return;
02221         }
02222 
02223         trap_FS_Write( string, strlen( string ), level.logFile );
02224 }
02225 
02226 /*
02227 ================
02228 LogExit
02229 
02230 Append information about this game to the log file
02231 ================
02232 */
02233 void LogExit( const char *string ) {
02234         int                             i, numSorted;
02235         gclient_t               *cl;
02236 //      qboolean                won = qtrue;
02237         G_LogPrintf( "Exit: %s\n", string );
02238 
02239         level.intermissionQueued = level.time;
02240 
02241         // this will keep the clients from playing any voice sounds
02242         // that will get cut off when the queued intermission starts
02243         trap_SetConfigstring( CS_INTERMISSION, "1" );
02244 
02245         // don't send more than 32 scores (FIXME?)
02246         numSorted = level.numConnectedClients;
02247         if ( numSorted > 32 ) {
02248                 numSorted = 32;
02249         }
02250 
02251         if ( g_gametype.integer >= GT_TEAM ) {
02252                 G_LogPrintf( "red:%i  blue:%i\n",
02253                         level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE] );
02254         }
02255 
02256         for (i=0 ; i < numSorted ; i++) {
02257                 int             ping;
02258 
02259                 cl = &level.clients[level.sortedClients[i]];
02260 
02261                 if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) {
02262                         continue;
02263                 }
02264                 if ( cl->pers.connected == CON_CONNECTING ) {
02265                         continue;
02266                 }
02267 
02268                 ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
02269 
02270                 G_LogPrintf( "score: %i  ping: %i  client: %i %s\n", cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i],       cl->pers.netname );
02271 //              if (g_singlePlayer.integer && (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)) {
02272 //                      if (g_entities[cl - level.clients].r.svFlags & SVF_BOT && cl->ps.persistant[PERS_RANK] == 0) {
02273 //                              won = qfalse;
02274 //                      }
02275 //              }
02276         }
02277 
02278         //yeah.. how about not.
02279         /*
02280         if (g_singlePlayer.integer) {
02281                 if (g_gametype.integer >= GT_CTF) {
02282                         won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE];
02283                 }
02284                 trap_SendConsoleCommand( EXEC_APPEND, (won) ? "spWin\n" : "spLose\n" );
02285         }
02286         */
02287 }
02288 
02289 qboolean gDidDuelStuff = qfalse; //gets reset on game reinit
02290 
02291 /*
02292 =================
02293 CheckIntermissionExit
02294 
02295 The level will stay at the intermission for a minimum of 5 seconds
02296 If all players wish to continue, the level will then exit.
02297 If one or more players have not acknowledged the continue, the game will
02298 wait 10 seconds before going on.
02299 =================
02300 */
02301 void CheckIntermissionExit( void ) {
02302         int                     ready, notReady;
02303         int                     i;
02304         gclient_t       *cl;
02305         int                     readyMask;
02306 
02307         // see which players are ready
02308         ready = 0;
02309         notReady = 0;
02310         readyMask = 0;
02311         for (i=0 ; i< g_maxclients.integer ; i++) {
02312                 cl = level.clients + i;
02313                 if ( cl->pers.connected != CON_CONNECTED ) {
02314                         continue;
02315                 }
02316                 if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
02317                         continue;
02318                 }
02319 
02320                 if ( cl->readyToExit ) {
02321                         ready++;
02322                         if ( i < 16 ) {
02323                                 readyMask |= 1 << i;
02324                         }
02325                 } else {
02326                         notReady++;
02327                 }
02328         }
02329 
02330         if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && !gDidDuelStuff &&
02331                 (level.time > level.intermissiontime + 2000) )
02332         {
02333                 gDidDuelStuff = qtrue;
02334 
02335                 if ( g_austrian.integer && g_gametype.integer != GT_POWERDUEL )
02336                 {
02337                         G_LogPrintf("Duel Results:\n");
02338                         //G_LogPrintf("Duel Time: %d\n", level.time );
02339                         G_LogPrintf("winner: %s, score: %d, wins/losses: %d/%d\n", 
02340                                 level.clients[level.sortedClients[0]].pers.netname,
02341                                 level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE],
02342                                 level.clients[level.sortedClients[0]].sess.wins,
02343                                 level.clients[level.sortedClients[0]].sess.losses );
02344                         G_LogPrintf("loser: %s, score: %d, wins/losses: %d/%d\n", 
02345                                 level.clients[level.sortedClients[1]].pers.netname,
02346                                 level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE],
02347                                 level.clients[level.sortedClients[1]].sess.wins,
02348                                 level.clients[level.sortedClients[1]].sess.losses );
02349                 }
02350                 // if we are running a tournement map, kick the loser to spectator status,
02351                 // which will automatically grab the next spectator and restart
02352                 if (!DuelLimitHit())
02353                 {
02354                         if (g_gametype.integer == GT_POWERDUEL)
02355                         {
02356                                 RemovePowerDuelLosers();
02357                                 AddPowerDuelPlayers();
02358                         }
02359                         else
02360                         {
02361                                 if (level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE] ==
02362                                         level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE] &&
02363                                         level.clients[level.sortedClients[0]].pers.connected == CON_CONNECTED &&
02364                                         level.clients[level.sortedClients[1]].pers.connected == CON_CONNECTED)
02365                                 {
02366                                         RemoveDuelDrawLoser();
02367                                 }
02368                                 else
02369                                 {
02370                                         RemoveTournamentLoser();
02371                                 }
02372                                 AddTournamentPlayer();
02373                         }
02374 
02375                         if ( g_austrian.integer )
02376                         {
02377                                 if (g_gametype.integer == GT_POWERDUEL)
02378                                 {
02379                                         G_LogPrintf("Power Duel Initiated: %s %d/%d vs %s %d/%d and %s %d/%d, kill limit: %d\n", 
02380                                                 level.clients[level.sortedClients[0]].pers.netname,
02381                                                 level.clients[level.sortedClients[0]].sess.wins,
02382                                                 level.clients[level.sortedClients[0]].sess.losses,
02383                                                 level.clients[level.sortedClients[1]].pers.netname,
02384                                                 level.clients[level.sortedClients[1]].sess.wins,
02385                                                 level.clients[level.sortedClients[1]].sess.losses,
02386                                                 level.clients[level.sortedClients[2]].pers.netname,
02387                                                 level.clients[level.sortedClients[2]].sess.wins,
02388                                                 level.clients[level.sortedClients[2]].sess.losses,
02389                                                 g_fraglimit.integer );
02390                                 }
02391                                 else
02392                                 {
02393                                         G_LogPrintf("Duel Initiated: %s %d/%d vs %s %d/%d, kill limit: %d\n", 
02394                                                 level.clients[level.sortedClients[0]].pers.netname,
02395                                                 level.clients[level.sortedClients[0]].sess.wins,
02396                                                 level.clients[level.sortedClients[0]].sess.losses,
02397                                                 level.clients[level.sortedClients[1]].pers.netname,
02398                                                 level.clients[level.sortedClients[1]].sess.wins,
02399                                                 level.clients[level.sortedClients[1]].sess.losses,
02400                                                 g_fraglimit.integer );
02401                                 }
02402                         }
02403                         
02404                         if (g_gametype.integer == GT_POWERDUEL)
02405                         {
02406                                 if (level.numPlayingClients >= 3 && level.numNonSpectatorClients >= 3)
02407                                 {
02408                                         trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i|%i", level.sortedClients[0], level.sortedClients[1], level.sortedClients[2] ) );
02409                                         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
02410                                 }                       
02411                         }
02412                         else
02413                         {
02414                                 if (level.numPlayingClients >= 2)
02415                                 {
02416                                         trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i", level.sortedClients[0], level.sortedClients[1] ) );
02417                                         trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
02418                                 }
02419                         }
02420 
02421                         return; 
02422                 }
02423 
02424                 if ( g_austrian.integer && g_gametype.integer != GT_POWERDUEL )
02425                 {
02426                         G_LogPrintf("Duel Tournament Winner: %s wins/losses: %d/%d\n", 
02427                                 level.clients[level.sortedClients[0]].pers.netname,
02428                                 level.clients[level.sortedClients[0]].sess.wins,
02429                                 level.clients[level.sortedClients[0]].sess.losses );
02430                 }
02431 
02432                 if (g_gametype.integer == GT_POWERDUEL)
02433                 {
02434                         RemovePowerDuelLosers();
02435                         AddPowerDuelPlayers();
02436 
02437                         if (level.numPlayingClients >= 3 && level.numNonSpectatorClients >= 3)
02438                         {
02439                                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i|%i", level.sortedClients[0], level.sortedClients[1], level.sortedClients[2] ) );
02440                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
02441                         }
02442                 }
02443                 else
02444                 {
02445                         //this means we hit the duel limit so reset the wins/losses
02446                         //but still push the loser to the back of the line, and retain the order for
02447                         //the map change
02448                         if (level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE] ==
02449                                 level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE] &&
02450                                 level.clients[level.sortedClients[0]].pers.connected == CON_CONNECTED &&
02451                                 level.clients[level.sortedClients[1]].pers.connected == CON_CONNECTED)
02452                         {
02453                                 RemoveDuelDrawLoser();
02454                         }
02455                         else
02456                         {
02457                                 RemoveTournamentLoser();
02458                         }
02459 
02460                         AddTournamentPlayer();
02461 
02462                         if (level.numPlayingClients >= 2)
02463                         {
02464                                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i", level.sortedClients[0], level.sortedClients[1] ) );
02465                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, "-1" );
02466                         }
02467                 }
02468         }
02469 
02470         if ((g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && !gDuelExit)
02471         { //in duel, we have different behaviour for between-round intermissions
02472                 if ( level.time > level.intermissiontime + 4000 )
02473                 { //automatically go to next after 4 seconds
02474                         ExitLevel();
02475                         return;
02476                 }
02477 
02478                 for (i=0 ; i< g_maxclients.integer ; i++)
02479                 { //being in a "ready" state is not necessary here, so clear it for everyone
02480                   //yes, I also thinking holding this in a ps value uniquely for each player
02481                   //is bad and wrong, but it wasn't my idea.
02482                         cl = level.clients + i;
02483                         if ( cl->pers.connected != CON_CONNECTED )
02484                         {
02485                                 continue;
02486                         }
02487                         cl->ps.stats[STAT_CLIENTS_READY] = 0;
02488                 }
02489                 return;
02490         }
02491 
02492         // copy the readyMask to each player's stats so
02493         // it can be displayed on the scoreboard
02494         for (i=0 ; i< g_maxclients.integer ; i++) {
02495                 cl = level.clients + i;
02496                 if ( cl->pers.connected != CON_CONNECTED ) {
02497                         continue;
02498                 }
02499                 cl->ps.stats[STAT_CLIENTS_READY] = readyMask;
02500         }
02501 
02502         // never exit in less than five seconds
02503         if ( level.time < level.intermissiontime + 5000 ) {
02504                 return;
02505         }
02506 
02507         if (d_noIntermissionWait.integer)
02508         { //don't care who wants to go, just go.
02509                 ExitLevel();
02510                 return;
02511         }
02512 
02513         // if nobody wants to go, clear timer
02514         if ( !ready ) {
02515                 level.readyToExit = qfalse;
02516                 return;
02517         }
02518 
02519         // if everyone wants to go, go now
02520         if ( !notReady ) {
02521                 ExitLevel();
02522                 return;
02523         }
02524 
02525         // the first person to ready starts the ten second timeout
02526         if ( !level.readyToExit ) {
02527                 level.readyToExit = qtrue;
02528                 level.exitTime = level.time;
02529         }
02530 
02531         // if we have waited ten seconds since at least one player
02532         // wanted to exit, go ahead
02533         if ( level.time < level.exitTime + 10000 ) {
02534                 return;
02535         }
02536 
02537         ExitLevel();
02538 }
02539 
02540 /*
02541 =============
02542 ScoreIsTied
02543 =============
02544 */
02545 qboolean ScoreIsTied( void ) {
02546         int             a, b;
02547 
02548         if ( level.numPlayingClients < 2 ) {
02549                 return qfalse;
02550         }
02551         
02552         if ( g_gametype.integer >= GT_TEAM ) {
02553                 return level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE];
02554         }
02555 
02556         a = level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE];
02557         b = level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE];
02558 
02559         return a == b;
02560 }
02561 
02562 /*
02563 =================
02564 CheckExitRules
02565 
02566 There will be a delay between the time the exit is qualified for
02567 and the time everyone is moved to the intermission spot, so you
02568 can see the last frag.
02569 =================
02570 */
02571 qboolean g_endPDuel = qfalse;
02572 void CheckExitRules( void ) {
02573         int                     i;
02574         gclient_t       *cl;
02575         char *sKillLimit;
02576         qboolean printLimit = qtrue;
02577         // if at the intermission, wait for all non-bots to
02578         // signal ready, then go to next level
02579         if ( level.intermissiontime ) {
02580                 CheckIntermissionExit ();
02581                 return;
02582         }
02583 
02584         if (gDoSlowMoDuel)
02585         { //don't go to intermission while in slow motion
02586                 return;
02587         }
02588 
02589         if (gEscaping)
02590         {
02591                 int i = 0;
02592                 int numLiveClients = 0;
02593 
02594                 while (i < MAX_CLIENTS)
02595                 {
02596                         if (g_entities[i].inuse && g_entities[i].client && g_entities[i].health > 0)
02597                         {
02598                                 if (g_entities[i].client->sess.sessionTeam != TEAM_SPECTATOR &&
02599                                         !(g_entities[i].client->ps.pm_flags & PMF_FOLLOW))
02600                                 {
02601                                         numLiveClients++;
02602                                 }
02603                         }
02604 
02605                         i++;
02606                 }
02607                 if (gEscapeTime < level.time)
02608                 {
02609                         gEscaping = qfalse;
02610                         LogExit( "Escape time ended." );
02611                         return;
02612                 }
02613                 if (!numLiveClients)
02614                 {
02615                         gEscaping = qfalse;
02616                         LogExit( "Everyone failed to escape." );
02617                         return;
02618                 }
02619         }
02620 
02621         if ( level.intermissionQueued ) {
02622                 //int time = (g_singlePlayer.integer) ? SP_INTERMISSION_DELAY_TIME : INTERMISSION_DELAY_TIME;
02623                 int time = INTERMISSION_DELAY_TIME;
02624                 if ( level.time - level.intermissionQueued >= time ) {
02625                         level.intermissionQueued = 0;
02626                         BeginIntermission();
02627                 }
02628                 return;
02629         }
02630 
02631         /*
02632         if (g_gametype.integer == GT_POWERDUEL)
02633         {
02634                 if (level.numPlayingClients < 3)
02635                 {
02636                         if (!level.intermissiontime)
02637                         {
02638                                 if (d_powerDuelPrint.integer)
02639                                 {
02640                                         Com_Printf("POWERDUEL WIN CONDITION: Duel forfeit (1)\n");
02641                                 }
02642                                 LogExit("Duel forfeit.");
02643                                 return;
02644                         }
02645                 }
02646         }
02647         */
02648 
02649         // check for sudden death
02650         if (g_gametype.integer != GT_SIEGE)
02651         {
02652                 if ( ScoreIsTied() ) {
02653                         // always wait for sudden death
02654                         if ((g_gametype.integer != GT_DUEL) || !g_timelimit.integer)
02655                         {
02656                                 if (g_gametype.integer != GT_POWERDUEL)
02657                                 {
02658                                         return;
02659                                 }
02660                         }
02661                 }
02662         }
02663 
02664         if (g_gametype.integer != GT_SIEGE)
02665         {
02666                 if ( g_timelimit.integer && !level.warmupTime ) {
02667                         if ( level.time - level.startTime >= g_timelimit.integer*60000 ) {
02668 //                              trap_SendServerCommand( -1, "print \"Timelimit hit.\n\"");
02669                                 trap_SendServerCommand( -1, va("print \"%s.\n\"",G_GetStringEdString("MP_SVGAME", "TIMELIMIT_HIT")));
02670                                 if (d_powerDuelPrint.integer)
02671                                 {
02672                                         Com_Printf("POWERDUEL WIN CONDITION: Timelimit hit (1)\n");
02673                                 }
02674                                 LogExit( "Timelimit hit." );
02675                                 return;
02676                         }
02677                 }
02678         }
02679 
02680         if (g_gametype.integer == GT_POWERDUEL && level.numPlayingClients >= 3)
02681         {
02682                 if (g_endPDuel)
02683                 {
02684                         g_endPDuel = qfalse;
02685                         LogExit("Powerduel ended.");
02686                 }
02687 
02688                 //yeah, this stuff was completely insane.
02689                 /*
02690                 int duelists[3];
02691                 duelists[0] = level.sortedClients[0];
02692                 duelists[1] = level.sortedClients[1];
02693                 duelists[2] = level.sortedClients[2];
02694 
02695                 if (duelists[0] != -1 &&
02696                         duelists[1] != -1 &&
02697                         duelists[2] != -1)
02698                 {
02699                         if (!g_entities[duelists[0]].inuse ||
02700                                 !g_entities[duelists[0]].client ||
02701                                 g_entities[duelists[0]].client->ps.stats[STAT_HEALTH] <= 0 ||
02702                                 g_entities[duelists[0]].client->sess.sessionTeam != TEAM_FREE)
02703                         { //The lone duelist lost, give the other two wins (if applicable) and him a loss
02704                                 if (g_entities[duelists[0]].inuse &&
02705                                         g_entities[duelists[0]].client)
02706                                 {
02707                                         g_entities[duelists[0]].client->sess.losses++;
02708                                         ClientUserinfoChanged(duelists[0]);
02709                                 }
02710                                 if (g_entities[duelists[1]].inuse &&
02711                                         g_entities[duelists[1]].client)
02712                                 {
02713                                         if (g_entities[duelists[1]].client->ps.stats[STAT_HEALTH] > 0 &&
02714                                                 g_entities[duelists[1]].client->sess.sessionTeam == TEAM_FREE)
02715                                         {
02716                                                 g_entities[duelists[1]].client->sess.wins++;
02717                                         }
02718                                         else
02719                                         {
02720                                                 g_entities[duelists[1]].client->sess.losses++;
02721                                         }
02722                                         ClientUserinfoChanged(duelists[1]);
02723                                 }
02724                                 if (g_entities[duelists[2]].inuse &&
02725                                         g_entities[duelists[2]].client)
02726                                 {
02727                                         if (g_entities[duelists[2]].client->ps.stats[STAT_HEALTH] > 0 &&
02728                                                 g_entities[duelists[2]].client->sess.sessionTeam == TEAM_FREE)
02729                                         {
02730                                                 g_entities[duelists[2]].client->sess.wins++;
02731                                         }
02732                                         else
02733                                         {
02734                                                 g_entities[duelists[2]].client->sess.losses++;
02735                                         }
02736                                         ClientUserinfoChanged(duelists[2]);
02737                                 }
02738 
02739                                 //Will want to parse indecies for two out at some point probably
02740                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", duelists[1] ) );
02741 
02742                                 if (d_powerDuelPrint.integer)
02743                                 {
02744                                         Com_Printf("POWERDUEL WIN CONDITION: Coupled duelists won (1)\n");
02745                                 }
02746                                 LogExit( "Coupled duelists won." );
02747                                 gDuelExit = qfalse;
02748                         }
02749                         else if ((!g_entities[duelists[1]].inuse ||
02750                                 !g_entities[duelists[1]].client ||
02751                                 g_entities[duelists[1]].client->sess.sessionTeam != TEAM_FREE ||
02752                                 g_entities[duelists[1]].client->ps.stats[STAT_HEALTH] <= 0) &&
02753                                 (!g_entities[duelists[2]].inuse ||
02754                                 !g_entities[duelists[2]].client ||
02755                                 g_entities[duelists[2]].client->sess.sessionTeam != TEAM_FREE ||
02756                                 g_entities[duelists[2]].client->ps.stats[STAT_HEALTH] <= 0))
02757                         { //the coupled duelists lost, give the lone duelist a win (if applicable) and the couple both losses
02758                                 if (g_entities[duelists[1]].inuse &&
02759                                         g_entities[duelists[1]].client)
02760                                 {
02761                                         g_entities[duelists[1]].client->sess.losses++;
02762                                         ClientUserinfoChanged(duelists[1]);
02763                                 }
02764                                 if (g_entities[duelists[2]].inuse &&
02765                                         g_entities[duelists[2]].client)
02766                                 {
02767                                         g_entities[duelists[2]].client->sess.losses++;
02768                                         ClientUserinfoChanged(duelists[2]);
02769                                 }
02770 
02771                                 if (g_entities[duelists[0]].inuse &&
02772                                         g_entities[duelists[0]].client &&
02773                                         g_entities[duelists[0]].client->ps.stats[STAT_HEALTH] > 0 &&
02774                                         g_entities[duelists[0]].client->sess.sessionTeam == TEAM_FREE)
02775                                 {
02776                                         g_entities[duelists[0]].client->sess.wins++;
02777                                         ClientUserinfoChanged(duelists[0]);
02778                                 }
02779 
02780                                 trap_SetConfigstring ( CS_CLIENT_DUELWINNER, va("%i", duelists[0] ) );
02781 
02782                                 if (d_powerDuelPrint.integer)
02783                                 {
02784                                         Com_Printf("POWERDUEL WIN CONDITION: Lone duelist won (1)\n");
02785                                 }
02786                                 LogExit( "Lone duelist won." );
02787                                 gDuelExit = qfalse;
02788                         }
02789                 }
02790                 */
02791                 return;
02792         }
02793 
02794         if ( level.numPlayingClients < 2 ) {
02795                 return;
02796         }
02797 
02798         if (g_gametype.integer == GT_DUEL ||
02799                 g_gametype.integer == GT_POWERDUEL)
02800         {
02801                 if (g_fraglimit.integer > 1)
02802                 {
02803                         sKillLimit = "Kill limit hit.";
02804                 }
02805                 else
02806                 {
02807                         sKillLimit = "";
02808                         printLimit = qfalse;
02809                 }
02810         }
02811         else
02812         {
02813                 sKillLimit = "Kill limit hit.";
02814         }
02815         if ( g_gametype.integer < GT_SIEGE && g_fraglimit.integer ) {
02816                 if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) {
02817                         trap_SendServerCommand( -1, va("print \"Red %s\n\"", G_GetStringEdString("MP_SVGAME", "HIT_THE_KILL_LIMIT")) );
02818                         if (d_powerDuelPrint.integer)
02819                         {
02820                                 Com_Printf("POWERDUEL WIN CONDITION: Kill limit (1)\n");
02821                         }
02822                         LogExit( sKillLimit );
02823                         return;
02824                 }
02825 
02826                 if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer ) {
02827                         trap_SendServerCommand( -1, va("print \"Blue %s\n\"", G_GetStringEdString("MP_SVGAME", "HIT_THE_KILL_LIMIT")) );
02828                         if (d_powerDuelPrint.integer)
02829                         {
02830                                 Com_Printf("POWERDUEL WIN CONDITION: Kill limit (2)\n");
02831                         }
02832                         LogExit( sKillLimit );
02833                         return;
02834                 }
02835 
02836                 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
02837                         cl = level.clients + i;
02838                         if ( cl->pers.connected != CON_CONNECTED ) {
02839                                 continue;
02840                         }
02841                         if ( cl->sess.sessionTeam != TEAM_FREE ) {
02842                                 continue;
02843                         }
02844 
02845                         if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && g_duel_fraglimit.integer && cl->sess.wins >= g_duel_fraglimit.integer )
02846                         {
02847                                 if (d_powerDuelPrint.integer)
02848                                 {
02849                                         Com_Printf("POWERDUEL WIN CONDITION: Duel limit hit (1)\n");
02850                                 }
02851                                 LogExit( "Duel limit hit." );
02852                                 gDuelExit = qtrue;
02853                                 trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " hit the win limit.\n\"",
02854                                         cl->pers.netname ) );
02855                                 return;
02856                         }
02857 
02858                         if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) {
02859                                 if (d_powerDuelPrint.integer)
02860                                 {
02861                                         Com_Printf("POWERDUEL WIN CONDITION: Kill limit (3)\n");
02862                                 }
02863                                 LogExit( sKillLimit );
02864                                 gDuelExit = qfalse;
02865                                 if (printLimit)
02866                                 {
02867                                         trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s.\n\"",
02868                                                                                                         cl->pers.netname,
02869                                                                                                         G_GetStringEdString("MP_SVGAME", "HIT_THE_KILL_LIMIT")
02870                                                                                                         ) 
02871                                                                                         );
02872                                 }
02873                                 return;
02874                         }
02875                 }
02876         }
02877 
02878         if ( g_gametype.integer >= GT_CTF && g_capturelimit.integer ) {
02879 
02880                 if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) 
02881                 {
02882                         trap_SendServerCommand( -1,  va("print \"%s \"", G_GetStringEdString("MP_SVGAME", "PRINTREDTEAM")));
02883                         trap_SendServerCommand( -1,  va("print \"%s.\n\"", G_GetStringEdString("MP_SVGAME", "HIT_CAPTURE_LIMIT")));
02884                         LogExit( "Capturelimit hit." );
02885                         return;
02886                 }
02887 
02888                 if ( level.teamScores[TEAM_BLUE] >= g_capturelimit.integer ) {
02889                         trap_SendServerCommand( -1,  va("print \"%s \"", G_GetStringEdString("MP_SVGAME", "PRINTBLUETEAM")));
02890                         trap_SendServerCommand( -1,  va("print \"%s.\n\"", G_GetStringEdString("MP_SVGAME", "HIT_CAPTURE_LIMIT")));
02891                         LogExit( "Capturelimit hit." );
02892                         return;
02893                 }
02894         }
02895 }
02896 
02897 
02898 
02899 /*
02900 ========================================================================
02901 
02902 FUNCTIONS CALLED EVERY FRAME
02903 
02904 ========================================================================
02905 */
02906 
02907 void G_RemoveDuelist(int team)
02908 {
02909         int i = 0;
02910         gentity_t *ent;
02911         while (i < MAX_CLIENTS)
02912         {
02913                 ent = &g_entities[i];
02914 
02915                 if (ent->inuse && ent->client && ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
02916                         ent->client->sess.duelTeam == team)
02917                 {
02918                         SetTeam(ent, "s");
02919                 }
02920         i++;
02921         }
02922 }
02923 
02924 /*
02925 =============
02926 CheckTournament
02927 
02928 Once a frame, check for changes in tournement player state
02929 =============
02930 */
02931 int g_duelPrintTimer = 0;
02932 void CheckTournament( void ) {
02933         // check because we run 3 game frames before calling Connect and/or ClientBegin
02934         // for clients on a map_restart
02935 //      if ( level.numPlayingClients == 0 && (g_gametype.integer != GT_POWERDUEL) ) {
02936 //              return;
02937 //      }
02938 
02939         if (g_gametype.integer == GT_POWERDUEL)
02940         {
02941                 if (level.numPlayingClients >= 3 && level.numNonSpectatorClients >= 3)
02942                 {
02943                         trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i|%i", level.sortedClients[0], level.sortedClients[1], level.sortedClients[2] ) );
02944                 }
02945         }
02946         else
02947         {
02948                 if (level.numPlayingClients >= 2)
02949                 {
02950                         trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i", level.sortedClients[0], level.sortedClients[1] ) );
02951                 }
02952         }
02953 
02954         if ( g_gametype.integer == GT_DUEL )
02955         {
02956                 // pull in a spectator if needed
02957                 if ( level.numPlayingClients < 2 && !level.intermissiontime && !level.intermissionQueued ) {
02958                         AddTournamentPlayer();
02959 
02960                         if (level.numPlayingClients >= 2)
02961                         {
02962                                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i", level.sortedClients[0], level.sortedClients[1] ) );
02963                         }
02964                 }
02965 
02966                 if (level.numPlayingClients >= 2)
02967                 {
02968 // nmckenzie: DUEL_HEALTH
02969                         if ( g_showDuelHealths.integer >= 1 )
02970                         {
02971                                 playerState_t *ps1, *ps2;
02972                                 ps1 = &level.clients[level.sortedClients[0]].ps;
02973                                 ps2 = &level.clients[level.sortedClients[1]].ps;
02974                                 trap_SetConfigstring ( CS_CLIENT_DUELHEALTHS, va("%i|%i|!", 
02975                                         ps1->stats[STAT_HEALTH], ps2->stats[STAT_HEALTH]));
02976                         }
02977                 }
02978 
02979                 //rww - It seems we have decided there will be no warmup in duel.
02980                 //if (!g_warmup.integer)
02981                 { //don't care about any of this stuff then, just add people and leave me alone
02982                         level.warmupTime = 0;
02983                         return;
02984                 }
02985 #if 0
02986                 // if we don't have two players, go back to "waiting for players"
02987                 if ( level.numPlayingClients != 2 ) {
02988                         if ( level.warmupTime != -1 ) {
02989                                 level.warmupTime = -1;
02990                                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
02991                                 G_LogPrintf( "Warmup:\n" );
02992                         }
02993                         return;
02994                 }
02995 
02996                 if ( level.warmupTime == 0 ) {
02997                         return;
02998                 }
02999 
03000                 // if the warmup is changed at the console, restart it
03001                 if ( g_warmup.modificationCount != level.warmupModificationCount ) {
03002                         level.warmupModificationCount = g_warmup.modificationCount;
03003                         level.warmupTime = -1;
03004                 }
03005 
03006                 // if all players have arrived, start the countdown
03007                 if ( level.warmupTime < 0 ) {
03008                         if ( level.numPlayingClients == 2 ) {
03009                                 // fudge by -1 to account for extra delays
03010                                 level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
03011 
03012                                 if (level.warmupTime < (level.time + 3000))
03013                                 { //rww - this is an unpleasent hack to keep the level from resetting completely on the client (this happens when two map_restarts are issued rapidly)
03014                                         level.warmupTime = level.time + 3000;
03015                                 }
03016                                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
03017                         }
03018                         return;
03019                 }
03020 
03021                 // if the warmup time has counted down, restart
03022                 if ( level.time > level.warmupTime ) {
03023                         level.warmupTime += 10000;
03024                         trap_Cvar_Set( "g_restarted", "1" );
03025                         trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
03026                         level.restarted = qtrue;
03027                         return;
03028                 }
03029 #endif
03030         }
03031         else if (g_gametype.integer == GT_POWERDUEL)
03032         {
03033                 if (level.numPlayingClients < 2)
03034                 { //hmm, ok, pull more in.
03035                         g_dontFrickinCheck = qfalse;
03036                 }
03037 
03038                 if (level.numPlayingClients > 3)
03039                 { //umm..yes..lets take care of that then.
03040                         int lone = 0, dbl = 0;
03041 
03042                         G_PowerDuelCount(&lone, &dbl, qfalse);
03043                         if (lone > 1)
03044                         {
03045                                 G_RemoveDuelist(DUELTEAM_LONE);
03046                         }
03047                         else if (dbl > 2)
03048                         {
03049                                 G_RemoveDuelist(DUELTEAM_DOUBLE);
03050                         }
03051                 }
03052                 else if (level.numPlayingClients < 3)
03053                 { //hmm, someone disconnected or something and we need em
03054                         int lone = 0, dbl = 0;
03055 
03056                         G_PowerDuelCount(&lone, &dbl, qfalse);
03057                         if (lone < 1)
03058                         {
03059                                 g_dontFrickinCheck = qfalse;
03060                         }
03061                         else if (dbl < 1)
03062                         {
03063                                 g_dontFrickinCheck = qfalse;
03064                         }
03065                 }
03066 
03067                 // pull in a spectator if needed
03068                 if (level.numPlayingClients < 3 && !g_dontFrickinCheck)
03069                 {
03070                         AddPowerDuelPlayers();
03071 
03072                         if (level.numPlayingClients >= 3 &&
03073                                 G_CanResetDuelists())
03074                         {
03075                                 gentity_t *te = G_TempEntity(vec3_origin, EV_GLOBAL_DUEL);
03076                                 te->r.svFlags |= SVF_BROADCAST;
03077                                 //this is really pretty nasty, but..
03078                                 te->s.otherEntityNum = level.sortedClients[0];
03079                                 te->s.otherEntityNum2 = level.sortedClients[1];
03080                                 te->s.groundEntityNum = level.sortedClients[2];
03081 
03082                                 trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i|%i", level.sortedClients[0], level.sortedClients[1], level.sortedClients[2] ) );
03083                                 G_ResetDuelists();
03084 
03085                                 g_dontFrickinCheck = qtrue;
03086                         }
03087                         else if (level.numPlayingClients > 0 ||
03088                                 level.numConnectedClients > 0)
03089                         {
03090                                 if (g_duelPrintTimer < level.time)
03091                                 { //print once every 10 seconds
03092                                         int lone = 0, dbl = 0;
03093 
03094                                         G_PowerDuelCount(&lone, &dbl, qtrue);
03095                                         if (lone < 1)
03096                                         {
03097                                                 trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStringEdString("MP_SVGAME", "DUELMORESINGLE")) );
03098                                         }
03099                                         else
03100                                         {
03101                                                 trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStringEdString("MP_SVGAME", "DUELMOREPAIRED")) );
03102                                         }
03103                                         g_duelPrintTimer = level.time + 10000;
03104                                 }
03105                         }
03106 
03107                         if (level.numPlayingClients >= 3 && level.numNonSpectatorClients >= 3)
03108                         { //pulled in a needed person
03109                                 if (G_CanResetDuelists())
03110                                 {
03111                                         gentity_t *te = G_TempEntity(vec3_origin, EV_GLOBAL_DUEL);
03112                                         te->r.svFlags |= SVF_BROADCAST;
03113                                         //this is really pretty nasty, but..
03114                                         te->s.otherEntityNum = level.sortedClients[0];
03115                                         te->s.otherEntityNum2 = level.sortedClients[1];
03116                                         te->s.groundEntityNum = level.sortedClients[2];
03117 
03118                                         trap_SetConfigstring ( CS_CLIENT_DUELISTS, va("%i|%i|%i", level.sortedClients[0], level.sortedClients[1], level.sortedClients[2] ) );
03119 
03120                                         if ( g_austrian.integer )
03121                                         {
03122                                                 G_LogPrintf("Duel Initiated: %s %d/%d vs %s %d/%d and %s %d/%d, kill limit: %d\n", 
03123                                                         level.clients[level.sortedClients[0]].pers.netname,
03124                                                         level.clients[level.sortedClients[0]].sess.wins,
03125                                                         level.clients[level.sortedClients[0]].sess.losses,
03126                                                         level.clients[level.sortedClients[1]].pers.netname,
03127                                                         level.clients[level.sortedClients[1]].sess.wins,
03128                                                         level.clients[level.sortedClients[1]].sess.losses,
03129                                                         level.clients[level.sortedClients[2]].pers.netname,
03130                                                         level.clients[level.sortedClients[2]].sess.wins,
03131                                                         level.clients[level.sortedClients[2]].sess.losses,
03132                                                         g_fraglimit.integer );
03133                                         }
03134                                         //trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
03135                                         //FIXME: This seems to cause problems. But we'd like to reset things whenever a new opponent is set.
03136                                 }
03137                         }
03138                 }
03139                 else
03140                 { //if you have proper num of players then don't try to add again
03141                         g_dontFrickinCheck = qtrue;
03142                 }
03143 
03144                 level.warmupTime = 0;
03145                 return;
03146         }
03147         else if ( level.warmupTime != 0 ) {
03148                 int             counts[TEAM_NUM_TEAMS];
03149                 qboolean        notEnough = qfalse;
03150 
03151                 if ( g_gametype.integer > GT_TEAM ) {
03152                         counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE );
03153                         counts[TEAM_RED] = TeamCount( -1, TEAM_RED );
03154 
03155                         if (counts[TEAM_RED] < 1 || counts[TEAM_BLUE] < 1) {
03156                                 notEnough = qtrue;
03157                         }
03158                 } else if ( level.numPlayingClients < 2 ) {
03159                         notEnough = qtrue;
03160                 }
03161 
03162                 if ( notEnough ) {
03163                         if ( level.warmupTime != -1 ) {
03164                                 level.warmupTime = -1;
03165                                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
03166                                 G_LogPrintf( "Warmup:\n" );
03167                         }
03168                         return; // still waiting for team members
03169                 }
03170 
03171                 if ( level.warmupTime == 0 ) {
03172                         return;
03173                 }
03174 
03175                 // if the warmup is changed at the console, restart it
03176                 /*
03177                 if ( g_warmup.modificationCount != level.warmupModificationCount ) {
03178                         level.warmupModificationCount = g_warmup.modificationCount;
03179                         level.warmupTime = -1;
03180                 }
03181                 */
03182 
03183                 // if all players have arrived, start the countdown
03184                 if ( level.warmupTime < 0 ) {
03185                         // fudge by -1 to account for extra delays
03186                         level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
03187                         trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
03188                         return;
03189                 }
03190 
03191                 // if the warmup time has counted down, restart
03192                 if ( level.time > level.warmupTime ) {
03193                         level.warmupTime += 10000;
03194                         trap_Cvar_Set( "g_restarted", "1" );
03195                         trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
03196                         level.restarted = qtrue;
03197                         return;
03198                 }
03199         }
03200 }
03201 
03202 void G_KickAllBots(void)
03203 {
03204         int i;
03205         char netname[36];
03206         gclient_t       *cl;
03207 
03208         for ( i=0 ; i< g_maxclients.integer ; i++ )
03209         {
03210                 cl = level.clients + i;
03211                 if ( cl->pers.connected != CON_CONNECTED )
03212                 {
03213                         continue;
03214                 }
03215                 if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) )
03216                 {
03217                         continue;
03218                 }
03219                 strcpy(netname, cl->pers.netname);
03220                 Q_CleanStr(netname);
03221                 trap_SendConsoleCommand( EXEC_INSERT, va("kick \"%s\"\n", netname) );
03222         }
03223 }
03224 
03225 /*
03226 ==================
03227 CheckVote
03228 ==================
03229 */
03230 void CheckVote( void ) {
03231         if ( level.voteExecuteTime && level.voteExecuteTime < level.time ) {
03232                 level.voteExecuteTime = 0;
03233                 trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
03234 
03235                 if (level.votingGametype)
03236                 {
03237                         if (trap_Cvar_VariableIntegerValue("g_gametype") != level.votingGametypeTo)
03238                         { //If we're voting to a different game type, be sure to refresh all the map stuff
03239                                 const char *nextMap = G_RefreshNextMap(level.votingGametypeTo, qtrue);
03240 
03241                                 if (level.votingGametypeTo == GT_SIEGE)
03242                                 { //ok, kick all the bots, cause the aren't supported!
03243                     G_KickAllBots();
03244                                         //just in case, set this to 0 too... I guess...maybe?
03245                                         //trap_Cvar_Set("bot_minplayers", "0");
03246                                 }
03247 
03248                                 if (nextMap && nextMap[0])
03249                                 {
03250                                         trap_SendConsoleCommand( EXEC_APPEND, va("map %s\n", nextMap ) );
03251                                 }
03252                         }
03253                         else
03254                         { //otherwise, just leave the map until a restart
03255                                 G_RefreshNextMap(level.votingGametypeTo, qfalse);
03256                         }
03257 
03258                         if (g_fraglimitVoteCorrection.integer)
03259                         { //This means to auto-correct fraglimit when voting to and from duel.
03260                                 const int currentGT = trap_Cvar_VariableIntegerValue("g_gametype");
03261                                 const int currentFL = trap_Cvar_VariableIntegerValue("fraglimit");
03262                                 const int currentTL = trap_Cvar_VariableIntegerValue("timelimit");
03263 
03264                                 if ((level.votingGametypeTo == GT_DUEL || level.votingGametypeTo == GT_POWERDUEL) && currentGT != GT_DUEL && currentGT != GT_POWERDUEL)
03265                                 {
03266                                         if (currentFL > 3 || !currentFL)
03267                                         { //if voting to duel, and fraglimit is more than 3 (or unlimited), then set it down to 3
03268                                                 trap_SendConsoleCommand(EXEC_APPEND, "fraglimit 3\n");
03269                                         }
03270                                         if (currentTL)
03271                                         { //if voting to duel, and timelimit is set, make it unlimited
03272                                                 trap_SendConsoleCommand(EXEC_APPEND, "timelimit 0\n");
03273                                         }
03274                                 }
03275                                 else if ((level.votingGametypeTo != GT_DUEL && level.votingGametypeTo != GT_POWERDUEL) &&
03276                                         (currentGT == GT_DUEL || currentGT == GT_POWERDUEL))
03277                                 {
03278                                         if (currentFL && currentFL < 20)
03279                                         { //if voting from duel, an fraglimit is less than 20, then set it up to 20
03280                                                 trap_SendConsoleCommand(EXEC_APPEND, "fraglimit 20\n");
03281                                         }
03282                                 }
03283                         }
03284 
03285                         level.votingGametype = qfalse;
03286                         level.votingGametypeTo = 0;
03287                 }
03288         }
03289         if ( !level.voteTime ) {
03290                 return;
03291         }
03292         if ( level.time - level.voteTime >= VOTE_TIME ) {
03293                 trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEFAILED")) );
03294         } else {
03295                 if ( level.voteYes > level.numVotingClients/2 ) {
03296                         // execute the command, then remove the vote
03297                         trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEPASSED")) );
03298                         level.voteExecuteTime = level.time + 3000;
03299                 } else if ( level.voteNo >= level.numVotingClients/2 ) {
03300                         // same behavior as a timeout
03301                         trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEFAILED")) );
03302                 } else {
03303                         // still waiting for a majority
03304                         return;
03305                 }
03306         }
03307         level.voteTime = 0;
03308         trap_SetConfigstring( CS_VOTE_TIME, "" );
03309 
03310 }
03311 
03312 /*
03313 ==================
03314 PrintTeam
03315 ==================
03316 */
03317 void PrintTeam(int team, char *message) {
03318         int i;
03319 
03320         for ( i = 0 ; i < level.maxclients ; i++ ) {
03321                 if (level.clients[i].sess.sessionTeam != team)
03322                         continue;
03323                 trap_SendServerCommand( i, message );
03324         }
03325 }
03326 
03327 /*
03328 ==================
03329 SetLeader
03330 ==================
03331 */
03332 void SetLeader(int team, int client) {
03333         int i;
03334 
03335         if ( level.clients[client].pers.connected == CON_DISCONNECTED ) {
03336                 PrintTeam(team, va("print \"%s is not connected\n\"", level.clients[client].pers.netname) );
03337                 return;
03338         }
03339         if (level.clients[client].sess.sessionTeam != team) {
03340                 PrintTeam(team, va("print \"%s is not on the team anymore\n\"", level.clients[client].pers.netname) );
03341                 return;
03342         }
03343         for ( i = 0 ; i < level.maxclients ; i++ ) {
03344                 if (level.clients[i].sess.sessionTeam != team)
03345                         continue;
03346                 if (level.clients[i].sess.teamLeader) {
03347                         level.clients[i].sess.teamLeader = qfalse;
03348                         ClientUserinfoChanged(i);
03349                 }
03350         }
03351         level.clients[client].sess.teamLeader = qtrue;
03352         ClientUserinfoChanged( client );
03353         PrintTeam(team, va("print \"%s %s\n\"", level.clients[client].pers.netname, G_GetStringEdString("MP_SVGAME", "NEWTEAMLEADER")) );
03354 }
03355 
03356 /*
03357 ==================
03358 CheckTeamLeader
03359 ==================
03360 */
03361 void CheckTeamLeader( int team ) {
03362         int i;
03363 
03364         for ( i = 0 ; i < level.maxclients ; i++ ) {
03365                 if (level.clients[i].sess.sessionTeam != team)
03366                         continue;
03367                 if (level.clients[i].sess.teamLeader)
03368                         break;
03369         }
03370         if (i >= level.maxclients) {
03371                 for ( i = 0 ; i < level.maxclients ; i++ ) {
03372                         if (level.clients[i].sess.sessionTeam != team)
03373                                 continue;
03374                         if (!(g_entities[i].r.svFlags & SVF_BOT)) {
03375                                 level.clients[i].sess.teamLeader = qtrue;
03376                                 break;
03377                         }
03378                 }
03379                 for ( i = 0 ; i < level.maxclients ; i++ ) {
03380                         if (level.clients[i].sess.sessionTeam != team)
03381                                 continue;
03382                         level.clients[i].sess.teamLeader = qtrue;
03383                         break;
03384                 }
03385         }
03386 }
03387 
03388 /*
03389 ==================
03390 CheckTeamVote
03391 ==================
03392 */
03393 void CheckTeamVote( int team ) {
03394         int cs_offset;
03395 
03396         if ( team == TEAM_RED )
03397                 cs_offset = 0;
03398         else if ( team == TEAM_BLUE )
03399                 cs_offset = 1;
03400         else
03401                 return;
03402 
03403         if ( !level.teamVoteTime[cs_offset] ) {
03404                 return;
03405         }
03406         if ( level.time - level.teamVoteTime[cs_offset] >= VOTE_TIME ) {
03407                 trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TEAMVOTEFAILED")) );
03408         } else {
03409                 if ( level.teamVoteYes[cs_offset] > level.numteamVotingClients[cs_offset]/2 ) {
03410                         // execute the command, then remove the vote
03411                         trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TEAMVOTEPASSED")) );
03412                         //
03413                         if ( !Q_strncmp( "leader", level.teamVoteString[cs_offset], 6) ) {
03414                                 //set the team leader
03415                                 //SetLeader(team, atoi(level.teamVoteString[cs_offset] + 7));
03416                         }
03417                         else {
03418                                 trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.teamVoteString[cs_offset] ) );
03419                         }
03420                 } else if ( level.teamVoteNo[cs_offset] >= level.numteamVotingClients[cs_offset]/2 ) {
03421                         // same behavior as a timeout
03422                         trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TEAMVOTEFAILED")) );
03423                 } else {
03424                         // still waiting for a majority
03425                         return;
03426                 }
03427         }
03428         level.teamVoteTime[cs_offset] = 0;
03429         trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" );
03430 
03431 }
03432 
03433 
03434 /*
03435 ==================
03436 CheckCvars
03437 ==================
03438 */
03439 void CheckCvars( void ) {
03440         static int lastMod = -1;
03441         
03442         if ( g_password.modificationCount != lastMod ) {
03443                 char password[MAX_INFO_STRING];
03444                 char *c = password;
03445                 lastMod = g_password.modificationCount;
03446                 
03447                 strcpy( password, g_password.string );
03448                 while( *c )
03449                 {
03450                         if ( *c == '%' )
03451                         {
03452                                 *c = '.';
03453                         }
03454                         c++;
03455                 }
03456                 trap_Cvar_Set("g_password", password );
03457 
03458                 if ( *g_password.string && Q_stricmp( g_password.string, "none" ) ) {
03459                         trap_Cvar_Set( "g_needpass", "1" );
03460                 } else {
03461                         trap_Cvar_Set( "g_needpass", "0" );
03462                 }
03463         }
03464 }
03465 
03466 /*
03467 =============
03468 G_RunThink
03469 
03470 Runs thinking code for this frame if necessary
03471 =============
03472 */
03473 void G_RunThink (gentity_t *ent) {
03474         float   thinktime;
03475 
03476         thinktime = ent->nextthink;
03477         if (thinktime <= 0) {
03478                 goto runicarus;
03479         }
03480         if (thinktime > level.time) {
03481                 goto runicarus;
03482         }
03483         
03484         ent->nextthink = 0;
03485         if (!ent->think) {
03486                 //G_Error ( "NULL ent->think");
03487                 goto runicarus;
03488         }
03489         ent->think (ent);
03490 
03491 runicarus:
03492         if ( ent->inuse )
03493         {
03494                 trap_ICARUS_MaintainTaskManager(ent->s.number);
03495         }
03496 }
03497 
03498 int g_LastFrameTime = 0;
03499 int g_TimeSinceLastFrame = 0;
03500 
03501 qboolean gDoSlowMoDuel = qfalse;
03502 int gSlowMoDuelTime = 0;
03503 
03504 //#define _G_FRAME_PERFANAL
03505 
03506 void NAV_CheckCalcPaths( void )
03507 {       
03508         if ( navCalcPathTime && navCalcPathTime < level.time )
03509         {//first time we've ever loaded this map...
03510                 vmCvar_t        mapname;
03511                 vmCvar_t        ckSum;
03512 
03513                 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
03514                 trap_Cvar_Register( &ckSum, "sv_mapChecksum", "", CVAR_ROM );
03515 
03516                 //clear all the failed edges
03517                 trap_Nav_ClearAllFailedEdges();
03518 
03519                 //Calculate all paths
03520                 NAV_CalculatePaths( mapname.string, ckSum.integer );
03521                 
03522                 trap_Nav_CalculatePaths(qfalse);
03523 
03524 #ifndef FINAL_BUILD
03525                 if ( fatalErrors )
03526                 {
03527                         Com_Printf( S_COLOR_RED"Not saving .nav file due to fatal nav errors\n" );
03528                 }
03529                 else 
03530 #endif
03531 #ifndef _XBOX
03532                 if ( trap_Nav_Save( mapname.string, ckSum.integer ) == qfalse )
03533                 {
03534                         Com_Printf("Unable to save navigations data for map \"%s\" (checksum:%d)\n", mapname.string, ckSum.integer );
03535                 }
03536 #endif
03537                 navCalcPathTime = 0;
03538         }
03539 }
03540 
03541 //so shared code can get the local time depending on the side it's executed on
03542 #include "../namespace_begin.h"
03543 int BG_GetTime(void)
03544 {
03545         return level.time;
03546 }
03547 #include "../namespace_end.h"
03548 
03549 /*
03550 ================
03551 G_RunFrame
03552 
03553 Advances the non-player objects in the world
03554 ================
03555 */
03556 void ClearNPCGlobals( void );
03557 void AI_UpdateGroups( void );
03558 void ClearPlayerAlertEvents( void );
03559 void SiegeCheckTimers(void);
03560 void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd );
03561 extern void Jedi_Decloak( gentity_t *self );
03562 qboolean G_PointInBounds( vec3_t point, vec3_t mins, vec3_t maxs );
03563 
03564 int g_siegeRespawnCheck = 0;
03565 
03566 void G_RunFrame( int levelTime ) {
03567         int                     i;
03568         gentity_t       *ent;
03569         int                     msec;
03570 #ifdef _G_FRAME_PERFANAL
03571         int                     iTimer_ItemRun = 0;
03572         int                     iTimer_ROFF = 0;
03573         int                     iTimer_ClientEndframe = 0;
03574         int                     iTimer_GameChecks = 0;
03575         int                     iTimer_Queues = 0;
03576         void            *timer_ItemRun;
03577         void            *timer_ROFF;
03578         void            *timer_ClientEndframe;
03579         void            *timer_GameChecks;
03580         void            *timer_Queues;
03581 #endif
03582 
03583         if (g_gametype.integer == GT_SIEGE &&
03584                 g_siegeRespawn.integer &&
03585                 g_siegeRespawnCheck < level.time)
03586         { //check for a respawn wave
03587                 int i = 0;
03588                 gentity_t *clEnt;
03589                 while (i < MAX_CLIENTS)
03590                 {
03591                         clEnt = &g_entities[i];
03592 
03593                         if (clEnt->inuse && clEnt->client &&
03594                                 clEnt->client->tempSpectate > level.time &&
03595                                 clEnt->client->sess.sessionTeam != TEAM_SPECTATOR)
03596                         {
03597                                 respawn(clEnt);
03598                                 clEnt->client->tempSpectate = 0;
03599                         }
03600                         i++;
03601                 }
03602 
03603                 g_siegeRespawnCheck = level.time + g_siegeRespawn.integer * 1000;
03604         }
03605 
03606         if (gDoSlowMoDuel)
03607         {
03608                 if (level.restarted)
03609                 {
03610                         char buf[128];
03611                         float tFVal = 0;
03612 
03613                         trap_Cvar_VariableStringBuffer("timescale", buf, sizeof(buf));
03614 
03615                         tFVal = atof(buf);
03616 
03617                         trap_Cvar_Set("timescale", "1");
03618                         if (tFVal == 1.0f)
03619                         {
03620                                 gDoSlowMoDuel = qfalse;
03621                         }
03622                 }
03623                 else
03624                 {
03625                         float timeDif = (level.time - gSlowMoDuelTime); //difference in time between when the slow motion was initiated and now
03626                         float useDif = 0; //the difference to use when actually setting the timescale
03627 
03628                         if (timeDif < 150)
03629                         {
03630                                 trap_Cvar_Set("timescale", "0.1f");
03631                         }
03632                         else if (timeDif < 1150)
03633                         {
03634                                 useDif = (timeDif/1000); //scale from 0.1 up to 1
03635                                 if (useDif < 0.1)
03636                                 {
03637                                         useDif = 0.1;
03638                                 }
03639                                 if (useDif > 1.0)
03640                                 {
03641                                         useDif = 1.0;
03642                                 }
03643                                 trap_Cvar_Set("timescale", va("%f", useDif));
03644                         }
03645                         else
03646                         {
03647                                 char buf[128];
03648                                 float tFVal = 0;
03649 
03650                                 trap_Cvar_VariableStringBuffer("timescale", buf, sizeof(buf));
03651 
03652                                 tFVal = atof(buf);
03653 
03654                                 trap_Cvar_Set("timescale", "1");
03655                                 if (timeDif > 1500 && tFVal == 1.0f)
03656                                 {
03657                                         gDoSlowMoDuel = qfalse;
03658                                 }
03659                         }
03660                 }
03661         }
03662 
03663         // if we are waiting for the level to restart, do nothing
03664         if ( level.restarted ) {
03665                 return;
03666         }
03667 
03668         level.framenum++;
03669         level.previousTime = level.time;
03670         level.time = levelTime;
03671         msec = level.time - level.previousTime;
03672 
03673         if (g_allowNPC.integer)
03674         {
03675                 NAV_CheckCalcPaths();
03676         }
03677 
03678         AI_UpdateGroups();
03679 
03680         if (g_allowNPC.integer)
03681         {
03682                 if ( d_altRoutes.integer )
03683                 {
03684                         trap_Nav_CheckAllFailedEdges();
03685                 }
03686                 trap_Nav_ClearCheckedNodes();
03687 
03688                 //remember last waypoint, clear current one
03689                 for ( i = 0; i < level.num_entities ; i++) 
03690                 {
03691                         ent = &g_entities[i];
03692 
03693                         if ( !ent->inuse )
03694                                 continue;
03695 
03696                         if ( ent->waypoint != WAYPOINT_NONE 
03697                                 && ent->noWaypointTime < level.time )
03698                         {
03699                                 ent->lastWaypoint = ent->waypoint;
03700                                 ent->waypoint = WAYPOINT_NONE;
03701                         }
03702                         if ( d_altRoutes.integer )
03703                         {
03704                                 trap_Nav_CheckFailedNodes( ent );
03705                         }
03706                 }
03707 
03708                 //Look to clear out old events
03709                 ClearPlayerAlertEvents();
03710         }
03711 
03712         g_TimeSinceLastFrame = (level.time - g_LastFrameTime);
03713 
03714         // get any cvar changes
03715         G_UpdateCvars();
03716 
03717 
03718 
03719 #ifdef _G_FRAME_PERFANAL
03720         trap_PrecisionTimer_Start(&timer_ItemRun);
03721 #endif
03722         //
03723         // go through all allocated objects
03724         //
03725         ent = &g_entities[0];
03726         for (i=0 ; i<level.num_entities ; i++, ent++) {
03727                 if ( !ent->inuse ) {
03728                         continue;
03729                 }
03730 
03731                 // clear events that are too old
03732                 if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
03733                         if ( ent->s.event ) {
03734                                 ent->s.event = 0;       // &= EV_EVENT_BITS;
03735                                 if ( ent->client ) {
03736                                         ent->client->ps.externalEvent = 0;
03737                                         // predicted events should never be set to zero
03738                                         //ent->client->ps.events[0] = 0;
03739                                         //ent->client->ps.events[1] = 0;
03740                                 }
03741                         }
03742                         if ( ent->freeAfterEvent ) {
03743                                 // tempEntities or dropped items completely go away after their event
03744                                 if (ent->s.eFlags & EF_SOUNDTRACKER)
03745                                 { //don't trigger the event again..
03746                                         ent->s.event = 0;
03747                                         ent->s.eventParm = 0;
03748                                         ent->s.eType = 0;
03749                                         ent->eventTime = 0;
03750                                 }
03751                                 else
03752                                 {
03753                                         G_FreeEntity( ent );
03754                                         continue;
03755                                 }
03756                         } else if ( ent->unlinkAfterEvent ) {
03757                                 // items that will respawn will hide themselves after their pickup event
03758                                 ent->unlinkAfterEvent = qfalse;
03759                                 trap_UnlinkEntity( ent );
03760                         }
03761                 }
03762 
03763                 // temporary entities don't think
03764                 if ( ent->freeAfterEvent ) {
03765                         continue;
03766                 }
03767 
03768                 if ( !ent->r.linked && ent->neverFree ) {
03769                         continue;
03770                 }
03771 
03772                 if ( ent->s.eType == ET_MISSILE ) {
03773                         G_RunMissile( ent );
03774                         continue;
03775                 }
03776 
03777                 if ( ent->s.eType == ET_ITEM || ent->physicsObject ) {
03778 #if 0 //use if body dragging enabled?
03779                         if (ent->s.eType == ET_BODY)
03780                         { //special case for bodies
03781                                 float grav = 3.0f;
03782                                 float mass = 0.14f;
03783                                 float bounce = 1.15f;
03784 
03785                                 G_RunExPhys(ent, grav, mass, bounce, qfalse, NULL, 0);
03786                         }
03787                         else
03788                         {
03789                                 G_RunItem( ent );
03790                         }
03791 #else
03792                         G_RunItem( ent );
03793 #endif
03794                         continue;
03795                 }
03796 
03797                 if ( ent->s.eType == ET_MOVER ) {
03798                         G_RunMover( ent );
03799                         continue;
03800                 }
03801 
03802                 if ( i < MAX_CLIENTS ) 
03803                 {
03804                         G_CheckClientTimeouts ( ent );
03805                         
03806                         if (ent->client->inSpaceIndex && ent->client->inSpaceIndex != ENTITYNUM_NONE)
03807                         { //we're in space, check for suffocating and for exiting
03808                 gentity_t *spacetrigger = &g_entities[ent->client->inSpaceIndex];
03809 
03810                                 if (!spacetrigger->inuse ||
03811                                         !G_PointInBounds(ent->client->ps.origin, spacetrigger->r.absmin, spacetrigger->r.absmax))
03812                                 { //no longer in space then I suppose
03813                     ent->client->inSpaceIndex = 0;                                      
03814                                 }
03815                                 else
03816                                 { //check for suffocation
03817                     if (ent->client->inSpaceSuffocation < level.time)
03818                                         { //suffocate!
03819                                                 if (ent->health > 0 && ent->takedamage)
03820                                                 { //if they're still alive..
03821                                                         G_Damage(ent, spacetrigger, spacetrigger, NULL, ent->client->ps.origin, Q_irand(50, 70), DAMAGE_NO_ARMOR, MOD_SUICIDE);
03822 
03823                                                         if (ent->health > 0)
03824                                                         { //did that last one kill them?
03825                                                                 //play the choking sound
03826                                                                 G_EntitySound(ent, CHAN_VOICE, G_SoundIndex(va( "*choke%d.wav", Q_irand( 1, 3 ) )));
03827 
03828                                                                 //make them grasp their throat
03829                                                                 ent->client->ps.forceHandExtend = HANDEXTEND_CHOKE;
03830                                                                 ent->client->ps.forceHandExtendTime = level.time + 2000;
03831                                                         }
03832                                                 }
03833 
03834                                                 ent->client->inSpaceSuffocation = level.time + Q_irand(100, 200);
03835                                         }
03836                                 }
03837                         }
03838 
03839                         if (ent->client->isHacking)
03840                         { //hacking checks
03841                                 gentity_t *hacked = &g_entities[ent->client->isHacking];
03842                                 vec3_t angDif;
03843 
03844                                 VectorSubtract(ent->client->ps.viewangles, ent->client->hackingAngles, angDif);
03845 
03846                                 //keep him in the "use" anim
03847                                 if (ent->client->ps.torsoAnim != BOTH_CONSOLE1)
03848                                 {
03849                                         G_SetAnim( ent, NULL, SETANIM_TORSO, BOTH_CONSOLE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
03850                                 }
03851                                 else
03852                                 {
03853                                         ent->client->ps.torsoTimer = 500;
03854                                 }
03855                                 ent->client->ps.weaponTime = ent->client->ps.torsoTimer;
03856 
03857                                 if (!(ent->client->pers.cmd.buttons & BUTTON_USE))
03858                                 { //have to keep holding use
03859                                         ent->client->isHacking = 0;
03860                                         ent->client->ps.hackingTime = 0;
03861                                 }
03862                                 else if (!hacked || !hacked->inuse)
03863                                 { //shouldn't happen, but safety first
03864                                         ent->client->isHacking = 0;
03865                                         ent->client->ps.hackingTime = 0;
03866                                 }
03867                                 else if (!G_PointInBounds( ent->client->ps.origin, hacked->r.absmin, hacked->r.absmax ))
03868                                 { //they stepped outside the thing they're hacking, so reset hacking time
03869                                         ent->client->isHacking = 0;
03870                                         ent->client->ps.hackingTime = 0;
03871                                 }
03872                                 else if (VectorLength(angDif) > 10.0f)
03873                                 { //must remain facing generally the same angle as when we start
03874                                         ent->client->isHacking = 0;
03875                                         ent->client->ps.hackingTime = 0;
03876                                 }
03877                         }
03878 
03879 #define JETPACK_DEFUEL_RATE             200 //approx. 20 seconds of idle use from a fully charged fuel amt
03880 #define JETPACK_REFUEL_RATE             150 //seems fair
03881                         if (ent->client->jetPackOn)
03882                         { //using jetpack, drain fuel
03883                                 if (ent->client->jetPackDebReduce < level.time)
03884                                 {
03885                                         if (ent->client->pers.cmd.upmove > 0)
03886                                         { //take more if they're thrusting
03887                                                 ent->client->ps.jetpackFuel -= 2;
03888                                         }
03889                                         else
03890                                         {
03891                                                 ent->client->ps.jetpackFuel--;
03892                                         }
03893                                         
03894                                         if (ent->client->ps.jetpackFuel <= 0)
03895                                         { //turn it off
03896                                                 ent->client->ps.jetpackFuel = 0;
03897                                                 Jetpack_Off(ent);
03898                                         }
03899                                         ent->client->jetPackDebReduce = level.time + JETPACK_DEFUEL_RATE;
03900                                 }
03901                         }
03902                         else if (ent->client->ps.jetpackFuel < 100)
03903                         { //recharge jetpack
03904                                 if (ent->client->jetPackDebRecharge < level.time)
03905                                 {
03906                                         ent->client->ps.jetpackFuel++;
03907                                         ent->client->jetPackDebRecharge = level.time + JETPACK_REFUEL_RATE;
03908                                 }
03909                         }
03910 
03911 #define CLOAK_DEFUEL_RATE               200 //approx. 20 seconds of idle use from a fully charged fuel amt
03912 #define CLOAK_REFUEL_RATE               150 //seems fair
03913                         if (ent->client->ps.powerups[PW_CLOAKED])
03914                         { //using cloak, drain battery
03915                                 if (ent->client->cloakDebReduce < level.time)
03916                                 {
03917                                         ent->client->ps.cloakFuel--;
03918                                         
03919                                         if (ent->client->ps.cloakFuel <= 0)
03920                                         { //turn it off
03921                                                 ent->client->ps.cloakFuel = 0;
03922                                                 Jedi_Decloak(ent);
03923                                         }
03924                                         ent->client->cloakDebReduce = level.time + CLOAK_DEFUEL_RATE;
03925                                 }
03926                         }
03927                         else if (ent->client->ps.cloakFuel < 100)
03928                         { //recharge cloak
03929                                 if (ent->client->cloakDebRecharge < level.time)
03930                                 {
03931                                         ent->client->ps.cloakFuel++;
03932                                         ent->client->cloakDebRecharge = level.time + CLOAK_REFUEL_RATE;
03933                                 }
03934                         }
03935 
03936                         if (g_gametype.integer == GT_SIEGE &&
03937                                 ent->client->siegeClass != -1 &&
03938                                 (bgSiegeClasses[ent->client->siegeClass].classflags & (1<<CFL_STATVIEWER)))
03939                         { //see if it's time to send this guy an update of extended info
03940                                 if (ent->client->siegeEDataSend < level.time)
03941                                 {
03942                     G_SiegeClientExData(ent);
03943                                         ent->client->siegeEDataSend = level.time + 1000; //once every sec seems ok
03944                                 }
03945                         }
03946 
03947                         if((!level.intermissiontime)&&!(ent->client->ps.pm_flags&PMF_FOLLOW) && ent->client->sess.sessionTeam != TEAM_SPECTATOR)
03948                         {
03949                                 WP_ForcePowersUpdate(ent, &ent->client->pers.cmd );
03950                                 WP_SaberPositionUpdate(ent, &ent->client->pers.cmd);
03951                                 WP_SaberStartMissileBlockCheck(ent, &ent->client->pers.cmd);
03952                         }
03953 
03954                         if (g_allowNPC.integer)
03955                         {
03956                                 //This was originally intended to only be done for client 0.
03957                                 //Make sure it doesn't slow things down too much with lots of clients in game.
03958                                 NAV_FindPlayerWaypoint(i);
03959                         }
03960 
03961                         trap_ICARUS_MaintainTaskManager(ent->s.number);
03962 
03963                         G_RunClient( ent );
03964                         continue;
03965                 }
03966                 else if (ent->s.eType == ET_NPC)
03967                 {
03968                         int j;
03969                         // turn off any expired powerups
03970                         for ( j = 0 ; j < MAX_POWERUPS ; j++ ) {
03971                                 if ( ent->client->ps.powerups[ j ] < level.time ) {
03972                                         ent->client->ps.powerups[ j ] = 0;
03973                                 }
03974                         }
03975 
03976                         WP_ForcePowersUpdate(ent, &ent->client->pers.cmd );
03977                         WP_SaberPositionUpdate(ent, &ent->client->pers.cmd);
03978                         WP_SaberStartMissileBlockCheck(ent, &ent->client->pers.cmd);
03979                 }
03980 
03981                 G_RunThink( ent );
03982 
03983                 if (g_allowNPC.integer)
03984                 {
03985                         ClearNPCGlobals();
03986                 }
03987         }
03988 #ifdef _G_FRAME_PERFANAL
03989         iTimer_ItemRun = trap_PrecisionTimer_End(timer_ItemRun);
03990 #endif
03991 
03992         SiegeCheckTimers();
03993 
03994 #ifdef _G_FRAME_PERFANAL
03995         trap_PrecisionTimer_Start(&timer_ROFF);
03996 #endif
03997         trap_ROFF_UpdateEntities();
03998 #ifdef _G_FRAME_PERFANAL
03999         iTimer_ROFF = trap_PrecisionTimer_End(timer_ROFF);
04000 #endif
04001 
04002 
04003 
04004 #ifdef _G_FRAME_PERFANAL
04005         trap_PrecisionTimer_Start(&timer_ClientEndframe);
04006 #endif
04007         // perform final fixups on the players
04008         ent = &g_entities[0];
04009         for (i=0 ; i < level.maxclients ; i++, ent++ ) {
04010                 if ( ent->inuse ) {
04011                         ClientEndFrame( ent );
04012                 }
04013         }
04014 #ifdef _G_FRAME_PERFANAL
04015         iTimer_ClientEndframe = trap_PrecisionTimer_End(timer_ClientEndframe);
04016 #endif
04017 
04018 
04019 
04020 #ifdef _G_FRAME_PERFANAL
04021         trap_PrecisionTimer_Start(&timer_GameChecks);
04022 #endif
04023         // see if it is time to do a tournement restart
04024         CheckTournament();
04025 
04026         // see if it is time to end the level
04027         CheckExitRules();
04028 
04029         // update to team status?
04030         CheckTeamStatus();
04031 
04032         // cancel vote if timed out
04033         CheckVote();
04034 
04035         // check team votes
04036         CheckTeamVote( TEAM_RED );
04037         CheckTeamVote( TEAM_BLUE );
04038 
04039         // for tracking changes
04040         CheckCvars();
04041 
04042         if (g_listEntity.integer) {
04043                 for (i = 0; i < MAX_GENTITIES; i++) {
04044                         G_Printf("%4i: %s\n", i, g_entities[i].classname);
04045                 }
04046                 trap_Cvar_Set("g_listEntity", "0");
04047         }
04048 #ifdef _G_FRAME_PERFANAL
04049         iTimer_GameChecks = trap_PrecisionTimer_End(timer_GameChecks);
04050 #endif
04051 
04052 
04053 
04054 #ifdef _G_FRAME_PERFANAL
04055         trap_PrecisionTimer_Start(&timer_Queues);
04056 #endif
04057         //At the end of the frame, send out the ghoul2 kill queue, if there is one
04058         G_SendG2KillQueue();
04059 
04060         if (gQueueScoreMessage)
04061         {
04062                 if (gQueueScoreMessageTime < level.time)
04063                 {
04064                         SendScoreboardMessageToAllClients();
04065 
04066                         gQueueScoreMessageTime = 0;
04067                         gQueueScoreMessage = 0;
04068                 }
04069         }
04070 #ifdef _G_FRAME_PERFANAL
04071         iTimer_Queues = trap_PrecisionTimer_End(timer_Queues);
04072 #endif
04073 
04074 
04075 
04076 #ifdef _G_FRAME_PERFANAL
04077         Com_Printf("---------------\nItemRun: %i\nROFF: %i\nClientEndframe: %i\nGameChecks: %i\nQueues: %i\n---------------\n",
04078                 iTimer_ItemRun,
04079                 iTimer_ROFF,
04080                 iTimer_ClientEndframe,
04081                 iTimer_GameChecks,
04082                 iTimer_Queues);
04083 #endif
04084 
04085         g_LastFrameTime = level.time;
04086 }
04087 
04088 const char *G_GetStringEdString(char *refSection, char *refName)
04089 {
04090         /*
04091         static char text[1024]={0};
04092         trap_SP_GetStringTextString(va("%s_%s", refSection, refName), text, sizeof(text));
04093         return text;
04094         */
04095 
04096         //Well, it would've been lovely doing it the above way, but it would mean mixing
04097         //languages for the client depending on what the server is. So we'll mark this as
04098         //a stringed reference with @@@ and send the refname to the client, and when it goes
04099         //to print it will get scanned for the stringed reference indication and dealt with
04100         //properly.
04101         static char text[1024]={0};
04102         Com_sprintf(text, sizeof(text), "@@@%s", refName);
04103         return text;
04104 }