diff options
Diffstat (limited to 'src/game/g_main.c')
-rw-r--r-- | src/game/g_main.c | 1808 |
1 files changed, 621 insertions, 1187 deletions
diff --git a/src/game/g_main.c b/src/game/g_main.c index 6623959..f0f2158 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -1,13 +1,14 @@ /* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2006 Tim Angus +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub This file is part of Tremulous. Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, +published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be @@ -16,44 +17,38 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +along with Tremulous; if not, see <https://www.gnu.org/licenses/> + =========================================================================== */ #include "g_local.h" -#define QVM_NAME "AA-QVM" -#define QVM_VERSIONNUM "MULTIPROTOCOL" - level_locals_t level; typedef struct { vmCvar_t *vmCvar; - char *cvarName; - char *defaultString; - int cvarFlags; - int modificationCount; // for tracking changes - qboolean trackChange; // track this variable, and announce if changed - qboolean teamShader; // track and if changed, update shader state + char *cvarName; + char *defaultString; + int cvarFlags; + int modificationCount; // for tracking changes + qboolean trackChange; // track this variable, and announce if changed + /* certain cvars can be set in worldspawn, but we don't want those values to + persist, so keep track of non-worldspawn changes and restore that on map + end. unfortunately, if the server crashes, the value set in worldspawn may + persist */ + char *explicit; } cvarTable_t; gentity_t g_entities[ MAX_GENTITIES ]; gclient_t g_clients[ MAX_CLIENTS ]; -vmCvar_t g_fraglimit; vmCvar_t g_timelimit; vmCvar_t g_suddenDeathTime; -vmCvar_t g_suddenDeath; -vmCvar_t g_suddenDeathMode; -vmCvar_t g_capturelimit; vmCvar_t g_friendlyFire; -vmCvar_t g_friendlyFireAliens; -vmCvar_t g_friendlyFireHumans; -vmCvar_t g_friendlyFireMovementAttacks; -vmCvar_t g_retribution; vmCvar_t g_friendlyBuildableFire; +vmCvar_t g_dretchPunt; vmCvar_t g_password; vmCvar_t g_needpass; vmCvar_t g_maxclients; @@ -63,79 +58,47 @@ vmCvar_t g_speed; vmCvar_t g_gravity; vmCvar_t g_cheats; vmCvar_t g_knockback; -vmCvar_t g_quadfactor; vmCvar_t g_inactivity; vmCvar_t g_debugMove; vmCvar_t g_debugDamage; -vmCvar_t g_debugAlloc; -vmCvar_t g_weaponRespawn; -vmCvar_t g_weaponTeamRespawn; vmCvar_t g_motd; vmCvar_t g_synchronousClients; vmCvar_t g_warmup; -vmCvar_t g_warmupMode; vmCvar_t g_doWarmup; vmCvar_t g_restarted; vmCvar_t g_lockTeamsAtStart; vmCvar_t g_logFile; vmCvar_t g_logFileSync; -vmCvar_t g_blood; -vmCvar_t g_podiumDist; -vmCvar_t g_podiumDrop; vmCvar_t g_allowVote; -vmCvar_t g_requireVoteReasons; vmCvar_t g_voteLimit; vmCvar_t g_suddenDeathVotePercent; vmCvar_t g_suddenDeathVoteDelay; -vmCvar_t g_extendVotesPercent; -vmCvar_t g_extendVotesTime; -vmCvar_t g_extendVotesCount; -vmCvar_t g_kickVotesPercent; -vmCvar_t g_customVote1; -vmCvar_t g_customVote2; -vmCvar_t g_customVote3; -vmCvar_t g_customVote4; -vmCvar_t g_customVote5; -vmCvar_t g_customVote6; -vmCvar_t g_customVote7; -vmCvar_t g_customVote8; -vmCvar_t g_customVotePercent; -vmCvar_t g_mapVotesPercent; -vmCvar_t g_extendVotesPercent; -vmCvar_t g_extendVotesTime; -vmCvar_t g_extendVotesCount; -vmCvar_t g_mapRotationVote; -vmCvar_t g_readyPercent; -vmCvar_t g_designateVotes; -vmCvar_t g_teamAutoJoin; vmCvar_t g_teamForceBalance; -vmCvar_t g_banIPs; -vmCvar_t g_filterBan; vmCvar_t g_smoothClients; -vmCvar_t g_outdatedClientMessage; vmCvar_t pmove_fixed; vmCvar_t pmove_msec; -vmCvar_t g_rankings; -vmCvar_t g_listEntity; -vmCvar_t g_minCommandPeriod; vmCvar_t g_minNameChangePeriod; vmCvar_t g_maxNameChanges; -vmCvar_t g_newbieNumbering; -vmCvar_t g_newbieNamePrefix; -vmCvar_t g_humanBuildPoints; vmCvar_t g_alienBuildPoints; +vmCvar_t g_alienBuildQueueTime; +vmCvar_t g_humanBuildPoints; +vmCvar_t g_humanBuildQueueTime; +vmCvar_t g_humanRepeaterBuildPoints; +vmCvar_t g_humanRepeaterBuildQueueTime; +vmCvar_t g_humanRepeaterMaxZones; vmCvar_t g_humanStage; -vmCvar_t g_humanKills; +vmCvar_t g_humanCredits; vmCvar_t g_humanMaxStage; vmCvar_t g_humanStage2Threshold; vmCvar_t g_humanStage3Threshold; vmCvar_t g_alienStage; -vmCvar_t g_alienKills; +vmCvar_t g_alienCredits; vmCvar_t g_alienMaxStage; vmCvar_t g_alienStage2Threshold; vmCvar_t g_alienStage3Threshold; vmCvar_t g_teamImbalanceWarnings; +vmCvar_t g_freeFundPeriod; vmCvar_t g_unlagged; @@ -144,105 +107,52 @@ vmCvar_t g_disabledClasses; vmCvar_t g_disabledBuildables; vmCvar_t g_markDeconstruct; -vmCvar_t g_markDeconstructMode; -vmCvar_t g_deconDead; vmCvar_t g_debugMapRotation; vmCvar_t g_currentMapRotation; -vmCvar_t g_currentMap; +vmCvar_t g_mapRotationNodes; +vmCvar_t g_mapRotationStack; vmCvar_t g_nextMap; vmCvar_t g_initialMapRotation; +vmCvar_t g_debugVoices; +vmCvar_t g_voiceChats; + vmCvar_t g_shove; vmCvar_t g_mapConfigs; -vmCvar_t g_chatTeamPrefix; -vmCvar_t g_actionPrefix; +vmCvar_t g_sayAreaRange; + vmCvar_t g_floodMaxDemerits; vmCvar_t g_floodMinTime; -vmCvar_t g_spamTime; -vmCvar_t g_layouts; +vmCvar_t g_nextLayout; +vmCvar_t g_layouts[ 9 ]; vmCvar_t g_layoutAuto; +vmCvar_t g_emoticonsAllowedInNames; + vmCvar_t g_admin; -vmCvar_t g_adminLog; -vmCvar_t g_adminParseSay; -vmCvar_t g_adminSayFilter; -vmCvar_t g_adminNameProtect; -vmCvar_t g_adminTempMute; vmCvar_t g_adminTempBan; vmCvar_t g_adminMaxBan; -vmCvar_t g_adminTempSpec; -vmCvar_t g_adminMapLog; -vmCvar_t g_minLevelToJoinTeam; -vmCvar_t g_minDeconLevel; -vmCvar_t g_minDeconAffectsMark; -vmCvar_t g_forceAutoSelect; vmCvar_t g_privateMessages; -vmCvar_t g_fullIgnore; -vmCvar_t g_decolourLogfiles; -vmCvar_t g_minLevelToSpecMM1; -vmCvar_t g_publicSayadmins; -vmCvar_t g_myStats; -vmCvar_t g_AllStats; -vmCvar_t g_AllStatsTime; -vmCvar_t g_teamStatus; -vmCvar_t g_antiSpawnBlock; -vmCvar_t g_banNotice; - -vmCvar_t g_devmapKillerHP; -vmCvar_t g_killerHP; - -vmCvar_t g_buildLogMaxLength; - -vmCvar_t g_tag; - -vmCvar_t g_dretchPunt; - -vmCvar_t g_allowShare; -vmCvar_t g_creditOverflow; - -vmCvar_t g_devmapNoGod; -vmCvar_t g_devmapNoStructDmg; - -vmCvar_t g_slapKnockback; -vmCvar_t g_slapDamage; - -vmCvar_t g_voteMinTime; -vmCvar_t g_mapvoteMaxTime; -vmCvar_t g_votableMaps; +vmCvar_t g_specChat; +vmCvar_t g_publicAdminMessages; +vmCvar_t g_allowTeamOverlay; -vmCvar_t g_msg; -vmCvar_t g_msgTime; -vmCvar_t g_welcomeMsg; -vmCvar_t g_welcomeMsgTime; -vmCvar_t g_deconBanTime; +vmCvar_t g_censorship; +vmCvar_t g_tag; -vmCvar_t mod_jetpackFuel; -vmCvar_t mod_jetpackConsume; -vmCvar_t mod_jetpackRegen; - -vmCvar_t g_adminExpireTime; - -vmCvar_t g_autoGhost; - -vmCvar_t g_teamKillThreshold; - -vmCvar_t g_aimbotAdvertBan; -vmCvar_t g_aimbotAdvertBanTime; -vmCvar_t g_aimbotAdvertBanReason; -vmCvar_t g_Bubbles; -vmCvar_t g_scrimMode; -vmCvar_t g_gradualFreeFunds; -vmCvar_t g_bleedingSpree; -vmCvar_t g_schachtmeisterClearThreshold; -vmCvar_t g_schachtmeisterAutobahnThreshold; -vmCvar_t g_schachtmeisterAutobahnMessage; -vmCvar_t g_adminAutobahnNotify; +// copy cvars that can be set in worldspawn so they can be restored later +static char cv_gravity[ MAX_CVAR_VALUE_STRING ]; +static char cv_humanMaxStage[ MAX_CVAR_VALUE_STRING ]; +static char cv_alienMaxStage[ MAX_CVAR_VALUE_STRING ]; +static char cv_humanRepeaterBuildPoints[ MAX_CVAR_VALUE_STRING ]; +static char cv_humanBuildPoints[ MAX_CVAR_VALUE_STRING ]; +static char cv_alienBuildPoints[ MAX_CVAR_VALUE_STRING ]; static cvarTable_t gameCvarTable[ ] = { @@ -251,223 +161,133 @@ static cvarTable_t gameCvarTable[ ] = // noset vars { NULL, "gamename", GAME_VERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, - { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse }, { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse }, { &g_lockTeamsAtStart, "g_lockTeamsAtStart", "0", CVAR_ROM, 0, qfalse }, { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, { NULL, "P", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, - { NULL, "ff", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, - { NULL, "qvm_version", QVM_NAME " " QVM_VERSIONNUM " (" __DATE__ ", " __TIME__ ")", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, // latched vars { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse }, - { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, // change anytime vars - { &g_timelimit, "timelimit", "45", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, - { &g_suddenDeathTime, "g_suddenDeathTime", "30", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, - { &g_suddenDeathMode, "g_suddenDeathMode", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, - { &g_suddenDeath, "g_suddenDeath", "0", CVAR_SERVERINFO | CVAR_NORESTART, 0, qtrue }, + { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, - { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse }, + { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, + { &g_suddenDeathTime, "g_suddenDeathTime", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, - { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qtrue }, - { &g_friendlyFireAliens, "g_friendlyFireAliens", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_friendlyFireHumans, "g_friendlyFireHumans", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_retribution, "g_retribution", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_friendlyBuildableFire, "g_friendlyBuildableFire", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qtrue }, - { &g_friendlyFireMovementAttacks, "g_friendlyFireMovementAttacks", "1", CVAR_ARCHIVE, 0, qtrue }, - { &g_devmapNoGod, "g_devmapNoGod", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_devmapNoStructDmg, "g_devmapNoStructDmg", "0", CVAR_ARCHIVE, 0, qtrue }, + { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse }, - { &g_slapKnockback, "g_slapKnockback", "200", CVAR_ARCHIVE, 0, qfalse}, - { &g_slapDamage, "g_slapDamage", "0", CVAR_ARCHIVE, 0, qfalse}, + { &g_friendlyFire, "g_friendlyFire", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, + { &g_friendlyBuildableFire, "g_friendlyBuildableFire", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, + { &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qtrue }, - { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE }, - { &g_teamForceBalance, "g_teamForceBalance", "1", CVAR_ARCHIVE }, + { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE, 0, qtrue }, { &g_warmup, "g_warmup", "10", CVAR_ARCHIVE, 0, qtrue }, - { &g_warmupMode, "g_warmupMode", "1", CVAR_ARCHIVE, 0, qtrue }, - { &g_doWarmup, "g_doWarmup", "1", CVAR_ARCHIVE, 0, qtrue }, + { &g_doWarmup, "g_doWarmup", "0", CVAR_ARCHIVE, 0, qtrue }, { &g_logFile, "g_logFile", "games.log", CVAR_ARCHIVE, 0, qfalse }, { &g_logFileSync, "g_logFileSync", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse }, - { &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, - - { &g_autoGhost, "g_autoGhost", "1", CVAR_SERVERINFO, 0, qfalse }, { &g_dedicated, "dedicated", "0", 0, 0, qfalse }, - { &g_speed, "g_speed", "320", CVAR_SERVERINFO, 0, qtrue }, - { &g_gravity, "g_gravity", "800", CVAR_SERVERINFO, 0, qtrue }, - { &g_knockback, "g_knockback", "1000", CVAR_SERVERINFO, 0, qtrue }, - { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue }, - { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue }, - { &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue }, + { &g_speed, "g_speed", "320", 0, 0, qtrue }, + { &g_gravity, "g_gravity", "800", 0, 0, qtrue, cv_gravity }, + { &g_knockback, "g_knockback", "1000", 0, 0, qtrue }, { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue }, { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse }, { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse }, - { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse }, { &g_motd, "g_motd", "", 0, 0, qfalse }, - { &g_blood, "com_blood", "1", 0, 0, qfalse }, - - { &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse }, - { &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse }, { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_requireVoteReasons, "g_requireVoteReasons", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_voteLimit, "g_voteLimit", "5", CVAR_ARCHIVE, 0, qfalse }, - { &g_voteMinTime, "g_voteMinTime", "120", CVAR_ARCHIVE, 0, qfalse }, - { &g_mapvoteMaxTime, "g_mapvoteMaxTime", "240", CVAR_ARCHIVE, 0, qfalse }, - { &g_votableMaps, "g_votableMaps", "", CVAR_ARCHIVE, 0, qtrue }, { &g_suddenDeathVotePercent, "g_suddenDeathVotePercent", "74", CVAR_ARCHIVE, 0, qfalse }, { &g_suddenDeathVoteDelay, "g_suddenDeathVoteDelay", "180", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote1, "g_customVote1", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote2, "g_customVote2", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote3, "g_customVote3", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote4, "g_customVote4", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote5, "g_customVote5", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote6, "g_customVote6", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote7, "g_customVote7", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVote8, "g_customVote8", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_customVotePercent, "g_customVotePercent", "50", CVAR_ARCHIVE, 0, qfalse }, - { &g_mapVotesPercent, "g_mapVotesPercent", "50", CVAR_ARCHIVE, 0, qfalse }, - { &g_extendVotesPercent, "g_extendVotesPercent", "74", CVAR_ARCHIVE, 0, qfalse }, - { &g_extendVotesTime, "g_extendVotesTime", "10", CVAR_ARCHIVE, 0, qfalse }, - { &g_extendVotesCount, "g_extendVotesCount", "2", CVAR_ARCHIVE, 0, qfalse }, - { &g_mapRotationVote, "g_mapRotationVote", "15", CVAR_ARCHIVE, 0, qfalse }, - { &g_readyPercent, "g_readyPercent", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_designateVotes, "g_designateVotes", "0", CVAR_ARCHIVE, 0, qfalse }, - - { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse }, - { &g_minCommandPeriod, "g_minCommandPeriod", "500", 0, 0, qfalse}, { &g_minNameChangePeriod, "g_minNameChangePeriod", "5", 0, 0, qfalse}, { &g_maxNameChanges, "g_maxNameChanges", "5", 0, 0, qfalse}, - { &g_newbieNumbering, "g_newbieNumbering", "0", CVAR_ARCHIVE, 0, qfalse}, - { &g_newbieNamePrefix, "g_newbieNamePrefix", "Newbie#", CVAR_ARCHIVE, 0, qfalse}, { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse}, - { &g_outdatedClientMessage, "g_outdatedClientMessage", "", CVAR_ARCHIVE, 0, qfalse}, { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse}, { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse}, - { &g_humanBuildPoints, "g_humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, CVAR_SERVERINFO, 0, qfalse }, - { &g_alienBuildPoints, "g_alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, CVAR_SERVERINFO, 0, qfalse }, + { &g_alienBuildPoints, "g_alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, 0, 0, qfalse, cv_alienBuildPoints }, + { &g_alienBuildQueueTime, "g_alienBuildQueueTime", DEFAULT_ALIEN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, + { &g_humanBuildPoints, "g_humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, 0, 0, qfalse, cv_humanBuildPoints }, + { &g_humanBuildQueueTime, "g_humanBuildQueueTime", DEFAULT_HUMAN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, + { &g_humanRepeaterBuildPoints, "g_humanRepeaterBuildPoints", DEFAULT_HUMAN_REPEATER_BUILDPOINTS, CVAR_ARCHIVE, 0, qfalse, cv_humanRepeaterBuildPoints }, + { &g_humanRepeaterMaxZones, "g_humanRepeaterMaxZones", DEFAULT_HUMAN_REPEATER_MAX_ZONES, CVAR_ARCHIVE, 0, qfalse }, + { &g_humanRepeaterBuildQueueTime, "g_humanRepeaterBuildQueueTime", DEFAULT_HUMAN_REPEATER_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, { &g_humanStage, "g_humanStage", "0", 0, 0, qfalse }, - { &g_humanKills, "g_humanKills", "0", 0, 0, qfalse }, - { &g_humanMaxStage, "g_humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, 0, 0, qfalse }, + { &g_humanCredits, "g_humanCredits", "0", 0, 0, qfalse }, + { &g_humanMaxStage, "g_humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, 0, 0, qfalse, cv_humanMaxStage }, { &g_humanStage2Threshold, "g_humanStage2Threshold", DEFAULT_HUMAN_STAGE2_THRESH, 0, 0, qfalse }, { &g_humanStage3Threshold, "g_humanStage3Threshold", DEFAULT_HUMAN_STAGE3_THRESH, 0, 0, qfalse }, { &g_alienStage, "g_alienStage", "0", 0, 0, qfalse }, - { &g_alienKills, "g_alienKills", "0", 0, 0, qfalse }, - { &g_alienMaxStage, "g_alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, 0, 0, qfalse }, + { &g_alienCredits, "g_alienCredits", "0", 0, 0, qfalse }, + { &g_alienMaxStage, "g_alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, 0, 0, qfalse, cv_alienMaxStage }, { &g_alienStage2Threshold, "g_alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, 0, 0, qfalse }, { &g_alienStage3Threshold, "g_alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, 0, 0, qfalse }, - { &g_teamImbalanceWarnings, "g_teamImbalanceWarnings", "30", CVAR_ARCHIVE, 0, qfalse }, - + { &g_freeFundPeriod, "g_freeFundPeriod", DEFAULT_FREEKILL_PERIOD, CVAR_ARCHIVE, 0, qtrue }, + { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, - { &g_disabledEquipment, "g_disabledEquipment", "", CVAR_ROM, 0, qfalse }, - { &g_disabledClasses, "g_disabledClasses", "", CVAR_ROM, 0, qfalse }, - { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM, 0, qfalse }, + { &g_disabledEquipment, "g_disabledEquipment", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse }, + { &g_disabledClasses, "g_disabledClasses", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse }, + { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse }, + + { &g_sayAreaRange, "g_sayAreaRange", "1000", CVAR_ARCHIVE, 0, qtrue }, - { &g_chatTeamPrefix, "g_chatTeamPrefix", "1", CVAR_ARCHIVE }, - { &g_actionPrefix, "g_actionPrefix", "* ", CVAR_ARCHIVE, 0, qfalse }, { &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse }, { &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse }, - { &g_spamTime, "g_spamTime", "2", CVAR_ARCHIVE, 0, qfalse }, - { &g_markDeconstruct, "g_markDeconstruct", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_markDeconstructMode, "g_markDeconstructMode", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_deconDead, "g_deconDead", "0", CVAR_ARCHIVE, 0, qtrue }, + { &g_markDeconstruct, "g_markDeconstruct", "3", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, { &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse }, { &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING - { &g_currentMap, "g_currentMap", "0", 0, 0, qfalse }, + { &g_mapRotationNodes, "g_mapRotationNodes", "", CVAR_ROM, 0, qfalse }, + { &g_mapRotationStack, "g_mapRotationStack", "", CVAR_ROM, 0, qfalse }, { &g_nextMap, "g_nextMap", "", 0 , 0, qtrue }, { &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_shove, "g_shove", "15", CVAR_ARCHIVE, 0, qfalse }, + { &g_debugVoices, "g_debugVoices", "0", 0, 0, qfalse }, + { &g_voiceChats, "g_voiceChats", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_shove, "g_shove", "0.0", CVAR_ARCHIVE, 0, qfalse }, { &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse }, { NULL, "g_mapConfigsLoaded", "0", CVAR_ROM, 0, qfalse }, - { &g_layouts, "g_layouts", "", CVAR_LATCH, 0, qfalse }, + { &g_nextLayout, "g_nextLayout", "", 0, 0, qfalse }, + { &g_layouts[ 0 ], "g_layouts", "", 0, 0, qfalse }, + { &g_layouts[ 1 ], "g_layouts2", "", 0, 0, qfalse }, + { &g_layouts[ 2 ], "g_layouts3", "", 0, 0, qfalse }, + { &g_layouts[ 3 ], "g_layouts4", "", 0, 0, qfalse }, + { &g_layouts[ 4 ], "g_layouts5", "", 0, 0, qfalse }, + { &g_layouts[ 5 ], "g_layouts6", "", 0, 0, qfalse }, + { &g_layouts[ 6 ], "g_layouts7", "", 0, 0, qfalse }, + { &g_layouts[ 7 ], "g_layouts8", "", 0, 0, qfalse }, + { &g_layouts[ 8 ], "g_layouts9", "", 0, 0, qfalse }, { &g_layoutAuto, "g_layoutAuto", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_emoticonsAllowedInNames, "g_emoticonsAllowedInNames", "1", CVAR_LATCH|CVAR_ARCHIVE, 0, qfalse }, + { &g_admin, "g_admin", "admin.dat", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminLog, "g_adminLog", "admin.log", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminParseSay, "g_adminParseSay", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminSayFilter, "g_adminSayFilter", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminTempMute, "g_adminTempMute", "5m", CVAR_ARCHIVE, 0, qfalse }, { &g_adminTempBan, "g_adminTempBan", "2m", CVAR_ARCHIVE, 0, qfalse }, { &g_adminMaxBan, "g_adminMaxBan", "2w", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminTempSpec, "g_adminTempSpec", "2m", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminMapLog, "g_adminMapLog", "", CVAR_ROM, 0, qfalse }, - { &g_minLevelToJoinTeam, "g_minLevelToJoinTeam", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_minDeconLevel, "g_minDeconLevel", "0", CVAR_ARCHIVE, 0, qfalse}, - { &g_minDeconAffectsMark, "g_minDeconAffectsMark", "0", CVAR_ARCHIVE, 0, qfalse}, - { &g_forceAutoSelect, "g_forceAutoSelect", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_adminExpireTime, "g_adminExpireTime", "0", CVAR_ARCHIVE, 0, qfalse }, - + { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_fullIgnore, "g_fullIgnore", "1", CVAR_ARCHIVE, 0, qtrue }, - { &g_decolourLogfiles, "g_decolourLogfiles", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_buildLogMaxLength, "g_buildLogMaxLength", "50", CVAR_ARCHIVE, 0, qfalse }, - { &g_myStats, "g_myStats", "1", CVAR_ARCHIVE, 0, qtrue }, - { &g_AllStats, "g_AllStats", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_AllStatsTime, "g_AllStatsTime", "60", CVAR_ARCHIVE, 0, qfalse }, - { &g_teamStatus, "g_teamStatus", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_publicSayadmins, "g_publicSayadmins", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_minLevelToSpecMM1, "g_minLevelToSpecMM1", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_antiSpawnBlock, "g_antiSpawnBlock", "0", CVAR_ARCHIVE, 0, qfalse }, - - { &g_devmapKillerHP, "g_devmapKillerHP", "0", CVAR_ARCHIVE, 0, qtrue }, - { &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qtrue }, - - { &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse }, - - { &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qfalse }, - - { &g_msg, "g_msg", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_msgTime, "g_msgTime", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_welcomeMsg, "g_welcomeMsg", "", CVAR_ARCHIVE, 0, qfalse }, - { &g_welcomeMsgTime, "g_welcomeMsgTime", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_deconBanTime, "g_deconBanTime", "2h", CVAR_ARCHIVE, 0, qfalse }, - - { &g_rankings, "g_rankings", "0", 0, 0, qfalse }, - { &g_allowShare, "g_allowShare", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse}, - { &g_creditOverflow, "g_creditOverflow", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse}, - { &g_banNotice, "g_banNotice", "", CVAR_ARCHIVE, 0, qfalse }, - - { &mod_jetpackFuel, "mod_jetpackFuel", "0", CVAR_ARCHIVE, 0, qtrue }, - { &mod_jetpackConsume, "mod_jetpackConsume", "2", CVAR_ARCHIVE, 0, qfalse }, - { &mod_jetpackRegen, "mod_jetpackRegen", "3", CVAR_ARCHIVE, 0, qfalse }, - - { &g_teamKillThreshold, "g_teamKillThreshold", "0", CVAR_ARCHIVE, 0, qfalse }, - - { &g_aimbotAdvertBan, "g_aimbotAdvertBan", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_aimbotAdvertBanTime, "g_aimbotAdvertBanTime", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_aimbotAdvertBanReason, "g_aimbotAdvertBanReason", "AUTOBAN: AIMBOT", CVAR_ARCHIVE, 0, qfalse }, - - { &g_Bubbles, "g_Bubbles", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_scrimMode, "g_scrimMode", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_gradualFreeFunds, "g_gradualFreeFunds", "2", CVAR_ARCHIVE, 0, qtrue }, - { &g_bleedingSpree, "g_bleedingSpree", "0", CVAR_ARCHIVE, 0, qfalse }, - { &g_gradualFreeFunds, "g_gradualFreeFunds", "2", CVAR_ARCHIVE, 0, qtrue }, - { &g_schachtmeisterClearThreshold, "g_schachtmeisterClearThreshold", "-10", CVAR_ARCHIVE, 0, qfalse }, - { &g_schachtmeisterAutobahnThreshold, "g_schachtmeisterAutobahnThreshold", "-30", CVAR_ARCHIVE, 0, qfalse }, - { &g_schachtmeisterAutobahnMessage, "g_schachtmeisterAutobahnMessage", "Your host is blacklisted.", CVAR_ARCHIVE, 0, qfalse }, - { &g_adminAutobahnNotify, "g_adminAutobahnNotify", "1", CVAR_ARCHIVE, 0, qfalse } + { &g_specChat, "g_specChat", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_publicAdminMessages, "g_publicAdminMessages", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_allowTeamOverlay, "g_allowTeamOverlay", "1", CVAR_ARCHIVE, 0, qtrue }, + + { &g_censorship, "g_censorship", "", CVAR_ARCHIVE, 0, qfalse }, + + { &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse } }; -static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] ); +static size_t gameCvarTableSize = ARRAY_LEN( gameCvarTable ); void G_InitGame( int levelTime, int randomSeed, int restart ); @@ -486,9 +306,7 @@ This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ -Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, - int arg5, int arg6, int arg7, int arg8, int arg9, - int arg10, int arg11 ) +Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2 ) { switch( command ) { @@ -541,10 +359,10 @@ void QDECL G_Printf( const char *fmt, ... ) char text[ 1024 ]; va_start( argptr, fmt ); - vsprintf( text, fmt, argptr ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); va_end( argptr ); - trap_Printf( text ); + trap_Print( text ); } void QDECL G_Error( const char *fmt, ... ) @@ -553,7 +371,7 @@ void QDECL G_Error( const char *fmt, ... ) char text[ 1024 ]; va_start( argptr, fmt ); - vsprintf( text, fmt, argptr ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); va_end( argptr ); trap_Error( text ); @@ -579,11 +397,8 @@ void G_FindTeams( void ) c = 0; c2 = 0; - for( i = 1, e = g_entities+i; i < level.num_entities; i++, e++ ) + for( i = MAX_CLIENTS, e = g_entities + i; i < level.num_entities; i++, e++ ) { - if( !e->inuse ) - continue; - if( !e->team ) continue; @@ -596,9 +411,6 @@ void G_FindTeams( void ) for( j = i + 1, e2 = e + 1; j < level.num_entities; j++, e2++ ) { - if( !e2->inuse ) - continue; - if( !e2->team ) continue; @@ -626,10 +438,6 @@ void G_FindTeams( void ) G_Printf( "%i teams with %i entities\n", c, c2 ); } -void G_RemapTeamShaders( void ) -{ -} - /* ================= @@ -640,7 +448,6 @@ void G_RegisterCvars( void ) { int i; cvarTable_t *cv; - qboolean remapped = qfalse; for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ ) { @@ -650,12 +457,9 @@ void G_RegisterCvars( void ) if( cv->vmCvar ) cv->modificationCount = cv->vmCvar->modificationCount; - if( cv->teamShader ) - remapped = qtrue; + if( cv->explicit ) + strcpy( cv->explicit, cv->vmCvar->string ); } - - if( remapped ) - G_RemapTeamShaders( ); } /* @@ -667,7 +471,6 @@ void G_UpdateCvars( void ) { int i; cvarTable_t *cv; - qboolean remapped = qfalse; for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ ) { @@ -680,21 +483,31 @@ void G_UpdateCvars( void ) cv->modificationCount = cv->vmCvar->modificationCount; if( cv->trackChange ) - { trap_SendServerCommand( -1, va( "print \"Server: %s changed to %s\n\"", cv->cvarName, cv->vmCvar->string ) ); - // update serverinfo in case this cvar is passed to clients indirectly - CalculateRanks( ); - } - if( cv->teamShader ) - remapped = qtrue; + if( !level.spawning && cv->explicit ) + strcpy( cv->explicit, cv->vmCvar->string ); } } } +} + +/* +================= +G_RestoreCvars +================= +*/ +void G_RestoreCvars( void ) +{ + int i; + cvarTable_t *cv; - if( remapped ) - G_RemapTeamShaders( ); + for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ ) + { + if( cv->vmCvar && cv->explicit ) + trap_Cvar_Set( cv->cvarName, cv->explicit ); + } } /* @@ -713,7 +526,7 @@ void G_MapConfigs( const char *mapname ) trap_SendConsoleCommand( EXEC_APPEND, va( "exec \"%s/default.cfg\"\n", g_mapConfigs.string ) ); - + trap_SendConsoleCommand( EXEC_APPEND, va( "exec \"%s/%s.cfg\"\n", g_mapConfigs.string, mapname ) ); @@ -736,11 +549,8 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) G_Printf( "------- Game Initialization -------\n" ); G_Printf( "gamename: %s\n", GAME_VERSION ); - G_Printf( "gamedate: %s\n", __DATE__ ); - G_ProcessIPBans( ); - - G_InitMemory( ); + BG_InitMemory( ); // set some level globals memset( &level, 0, sizeof( level ) ); @@ -751,9 +561,6 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) level.snd_fry = G_SoundIndex( "sound/misc/fry.wav" ); // FIXME standing in lava / slime - trap_Cvar_Set( "qvm_version", - QVM_NAME " " QVM_VERSIONNUM " (" __DATE__ ", " __TIME__ ")" ); - if( g_logFile.string[ 0 ] ) { if( g_logFileSync.integer ) @@ -767,17 +574,15 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) { char serverinfo[ MAX_INFO_STRING ]; qtime_t qt; - int t; - trap_GetServerinfo( serverinfo, sizeof( serverinfo ) ); G_LogPrintf( "------------------------------------------------------------\n" ); G_LogPrintf( "InitGame: %s\n", serverinfo ); - t = trap_RealTime( &qt ); - G_LogPrintf("RealTime: %04i/%02i/%02i %02i:%02i:%02i\n", - qt.tm_year+1900, qt.tm_mon+1, qt.tm_mday, + trap_RealTime( &qt ); + G_LogPrintf("RealTime: %04i-%02i-%02i %02i:%02i:%02i\n", + qt.tm_year+1900, qt.tm_mon+1, qt.tm_mday, qt.tm_hour, qt.tm_min, qt.tm_sec ); } @@ -785,19 +590,28 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) else G_Printf( "Not logging to disk\n" ); + if( g_mapConfigs.string[ 0 ] && !trap_Cvar_VariableIntegerValue( "g_mapConfigsLoaded" ) ) { char map[ MAX_CVAR_VALUE_STRING ] = {""}; + G_Printf( "InitGame: executing map configuration scripts and restarting\n" ); trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); G_MapConfigs( map ); + trap_SendConsoleCommand( EXEC_APPEND, "wait\nmap_restart 0\n" ); + } + else + { + // we're done with g_mapConfigs, so reset this for the next map + trap_Cvar_Set( "g_mapConfigsLoaded", "0" ); } - // we're done with g_mapConfigs, so reset this for the next map - trap_Cvar_Set( "g_mapConfigsLoaded", "0" ); + // set this cvar to 0 if it exists, but otherwise avoid its creation + if( trap_Cvar_VariableIntegerValue( "g_rangeMarkerWarningGiven" ) ) + trap_Cvar_Set( "g_rangeMarkerWarningGiven", "0" ); - if ( g_admin.string[ 0 ] ) { - G_admin_readconfig( NULL, 0 ); - } + G_RegisterCommands( ); + G_admin_readconfig( NULL ); + G_LoadCensors( ); // initialize all entities for this game memset( g_entities, 0, MAX_GENTITIES * sizeof( g_entities[ 0 ] ) ); @@ -817,26 +631,29 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) // range are NEVER anything but clients level.num_entities = MAX_CLIENTS; + for( i = 0; i < MAX_CLIENTS; i++ ) + g_entities[ i ].classname = "clientslot"; + // let the server system know where the entites are trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), &level.clients[ 0 ].ps, sizeof( level.clients[ 0 ] ) ); + level.emoticonCount = BG_LoadEmoticons( level.emoticons, MAX_EMOTICONS ); + trap_SetConfigstring( CS_INTERMISSION, "0" ); - // update maplog - G_admin_maplog_update( ); + G_InitPlayerModel(); // test to see if a custom buildable layout will be loaded G_LayoutSelect( ); + // this has to be flipped after the first UpdateCvars + level.spawning = qtrue; // parse the key/value pairs and spawn gentities G_SpawnEntitiesFromString( ); // load up a custom building layout if there is one - G_LayoutLoad( ); - - // load any nobuild markers that have been saved - G_NobuildLoad( ); + G_LayoutLoad( level.layout ); // the map might disable some things BG_InitAllowedGameElements( ); @@ -844,9 +661,8 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) // general initialization G_FindTeams( ); - //TA: - BG_InitClassOverrides( ); - BG_InitBuildableOverrides( ); + BG_InitClassConfigs( ); + BG_InitBuildableConfigs( ); G_InitDamageLocations( ); G_InitMapRotations( ); G_InitSpawnQueue( &level.alienSpawnQueue ); @@ -855,27 +671,27 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) if( g_debugMapRotation.integer ) G_PrintRotations( ); + level.voices = BG_VoiceInit( ); + BG_PrintVoices( level.voices, g_debugVoices.integer ); + //reset stages trap_Cvar_Set( "g_alienStage", va( "%d", S1 ) ); trap_Cvar_Set( "g_humanStage", va( "%d", S1 ) ); - trap_Cvar_Set( "g_alienKills", 0 ); - trap_Cvar_Set( "g_humanKills", 0 ); - trap_Cvar_Set( "g_suddenDeath", 0 ); + trap_Cvar_Set( "g_alienCredits", 0 ); + trap_Cvar_Set( "g_humanCredits", 0 ); level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000; G_Printf( "-----------------------------------\n" ); - G_RemapTeamShaders( ); - - //TA: so the server counts the spawns without a client attached + // So the server counts the spawns without a client attached G_CountSpawns( ); - G_ResetPTRConnections( ); - - if(g_lockTeamsAtStart.integer) + G_UpdateTeamConfigStrings( ); + + if( g_lockTeamsAtStart.integer ) { - level.alienTeamLocked=qtrue; - level.humanTeamLocked=qtrue; + level.alienTeamLocked = qtrue; + level.humanTeamLocked = qtrue; trap_Cvar_Set( "g_lockTeamsAtStart", "0" ); } } @@ -889,15 +705,13 @@ remove all currently active votes */ static void G_ClearVotes( void ) { - level.voteTime = 0; - trap_SetConfigstring( CS_VOTE_TIME, "" ); - trap_SetConfigstring( CS_VOTE_STRING, "" ); - level.teamVoteTime[ 0 ] = 0; - trap_SetConfigstring( CS_TEAMVOTE_TIME, "" ); - trap_SetConfigstring( CS_TEAMVOTE_STRING, "" ); - level.teamVoteTime[ 1 ] = 0; - trap_SetConfigstring( CS_TEAMVOTE_TIME + 1, "" ); - trap_SetConfigstring( CS_TEAMVOTE_STRING + 1, "" ); + int i; + memset( level.voteTime, 0, sizeof( level.voteTime ) ); + for( i = 0; i < NUM_TEAMS; i++ ) + { + trap_SetConfigstring( CS_VOTE_TIME + i, "" ); + trap_SetConfigstring( CS_VOTE_STRING + i, "" ); + } } /* @@ -910,6 +724,8 @@ void G_ShutdownGame( int restart ) // in case of a map_restart G_ClearVotes( ); + G_RestoreCvars( ); + G_Printf( "==== ShutdownGame ====\n" ); if( level.logFile ) @@ -917,20 +733,21 @@ void G_ShutdownGame( int restart ) G_LogPrintf( "ShutdownGame:\n" ); G_LogPrintf( "------------------------------------------------------------\n" ); trap_FS_FCloseFile( level.logFile ); + level.logFile = 0; } - // write admin.dat for !seen data - admin_writeconfig(); - // write all the client session data so we can get it back G_WriteSessionData( ); G_admin_cleanup( ); - G_admin_namelog_cleanup( ); - G_admin_adminlog_cleanup( ); + G_namelog_cleanup( ); + G_UnregisterCommands( ); + + G_FreePlayerModel( ); + G_ShutdownMapRotations( ); level.restarted = qfalse; - level.surrenderTeam = PTE_NONE; + level.surrenderTeam = TEAM_NONE; trap_SetConfigstring( CS_WINNER, "" ); } @@ -944,7 +761,7 @@ void QDECL Com_Error( int level, const char *error, ... ) char text[ 1024 ]; va_start( argptr, error ); - vsprintf( text, error, argptr ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); va_end( argptr ); G_Error( "%s", text ); @@ -956,7 +773,7 @@ void QDECL Com_Printf( const char *msg, ... ) char text[ 1024 ]; va_start( argptr, msg ); - vsprintf( text, msg, argptr ); + Q_vsnprintf( text, sizeof( text ), msg, argptr ); va_end( argptr ); G_Printf( "%s", text ); @@ -985,9 +802,9 @@ int QDECL SortRanks( const void *a, const void *b ) cb = &level.clients[ *(int *)b ]; // then sort by score - if( ca->pers.score > cb->pers.score ) + if( ca->ps.persistant[ PERS_SCORE ] > cb->ps.persistant[ PERS_SCORE ] ) return -1; - else if( ca->pers.score < cb->pers.score ) + if( ca->ps.persistant[ PERS_SCORE ] < cb->ps.persistant[ PERS_SCORE ] ) return 1; else return 0; @@ -1016,7 +833,7 @@ void G_InitSpawnQueue( spawnQueue_t *sq ) ============ G_GetSpawnQueueLength -Return tha length of a spawn queue +Return the length of a spawn queue ============ */ int G_GetSpawnQueueLength( spawnQueue_t *sq ) @@ -1047,6 +864,7 @@ int G_PopSpawnQueue( spawnQueue_t *sq ) { sq->clients[ sq->front ] = -1; sq->front = QUEUE_PLUS1( sq->front ); + G_StopFollowing( g_entities + clientNum ); g_entities[ clientNum ].client->ps.pm_flags &= ~PMF_QUEUED; return clientNum; @@ -1079,8 +897,11 @@ qboolean G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum ) int i; for( i = 0; i < MAX_CLIENTS; i++ ) + { if( sq->clients[ i ] == clientNum ) return qtrue; + } + return qfalse; } @@ -1211,24 +1032,20 @@ G_SpawnClients Spawn queued clients ============ */ -void G_SpawnClients( pTeam_t team ) +void G_SpawnClients( team_t team ) { int clientNum; gentity_t *ent, *spawn; vec3_t spawn_origin, spawn_angles; spawnQueue_t *sq = NULL; int numSpawns = 0; - if( g_doWarmup.integer && ( g_warmupMode.integer==1 || g_warmupMode.integer == 2 ) && - level.time - level.startTime < g_warmup.integer * 1000 ) - { - return; - } - if( team == PTE_ALIENS ) + + if( team == TEAM_ALIENS ) { sq = &level.alienSpawnQueue; numSpawns = level.numAlienSpawns; } - else if( team == PTE_HUMANS ) + else if( team == TEAM_HUMANS ) { sq = &level.humanSpawnQueue; numSpawns = level.numHumanSpawns; @@ -1250,7 +1067,7 @@ void G_SpawnClients( pTeam_t team ) ent = &g_entities[ clientNum ]; - ent->client->sess.sessionTeam = TEAM_FREE; + ent->client->sess.spectatorState = SPECTATOR_NOT; ClientUserinfoChanged( clientNum, qfalse ); ClientSpawn( ent, spawn, spawn_origin, spawn_angles ); } @@ -1271,33 +1088,31 @@ void G_CountSpawns( void ) level.numAlienSpawns = 0; level.numHumanSpawns = 0; - for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ ) { - if( !ent->inuse ) + if( !ent->inuse || ent->s.eType != ET_BUILDABLE || ent->health <= 0 ) continue; - if( ent->s.modelindex == BA_A_SPAWN && ent->health > 0 ) + if( ent->s.modelindex == BA_A_SPAWN ) level.numAlienSpawns++; - if( ent->s.modelindex == BA_H_SPAWN && ent->health > 0 ) + if( ent->s.modelindex == BA_H_SPAWN ) level.numHumanSpawns++; } - - //let the client know how many spawns there are - trap_SetConfigstring( CS_SPAWNS, va( "%d %d", - level.numAlienSpawns, level.numHumanSpawns ) ); } + /* ============ G_TimeTilSuddenDeath ============ */ +#define SUDDENDEATHWARNING 60000 int G_TimeTilSuddenDeath( void ) { - if( (!g_suddenDeathTime.integer && level.suddenDeathBeginTime==0 ) || level.suddenDeathBeginTime<0 ) - return 999999999; // Always some time away + if( ( !g_suddenDeathTime.integer && level.suddenDeathBeginTime == 0 ) || + ( level.suddenDeathBeginTime < 0 ) ) + return SUDDENDEATHWARNING + 1; // Always some time away return ( ( level.suddenDeathBeginTime ) - ( level.time - level.startTime ) ); } @@ -1314,180 +1129,152 @@ Recalculate the quantity of building points available to the teams */ void G_CalculateBuildPoints( void ) { - int i; - buildable_t buildable; - gentity_t *ent; - int localHTP = g_humanBuildPoints.integer, - localATP = g_alienBuildPoints.integer; - - // g_suddenDeath sets what state we want it to be. - // level.suddenDeath says whether we've calculated BPs at the 'start' of SD or not + int i; + buildable_t buildable; + buildPointZone_t *zone; - // reset if SD was on, but now it's off - if(!g_suddenDeath.integer && level.suddenDeath) + // BP queue updates + while( level.alienBuildPointQueue > 0 && + level.alienNextQueueTime < level.time ) { - level.suddenDeath = qfalse; - level.suddenDeathWarning = 0; - level.suddenDeathBeginTime = -1; - if((level.time - level.startTime) < (g_suddenDeathTime.integer * 60000 ) ) - level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000; - else - level.suddenDeathBeginTime = -1; + level.alienBuildPointQueue--; + level.alienNextQueueTime += G_NextQueueTime( level.alienBuildPointQueue, + g_alienBuildPoints.integer, + g_alienBuildQueueTime.integer ); } - if(!level.suddenDeath) + while( level.humanBuildPointQueue > 0 && + level.humanNextQueueTime < level.time ) { - if(g_suddenDeath.integer || G_TimeTilSuddenDeath( ) <= 0 ) //Conditions to enter SD - { - //begin sudden death - if( level.suddenDeathWarning < TW_PASSED ) - { - trap_SendServerCommand( -1, "cp \"Sudden Death!\"" ); - G_LogPrintf("Beginning Sudden Death (Mode %d)\n",g_suddenDeathMode.integer); - localHTP = 0; - localATP = 0; + level.humanBuildPointQueue--; + level.humanNextQueueTime += G_NextQueueTime( level.humanBuildPointQueue, + g_humanBuildPoints.integer, + g_humanBuildQueueTime.integer ); + } - if( g_suddenDeathMode.integer == SDMODE_SELECTIVE ) - { - for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( BG_FindReplaceableTestForBuildable( ent->s.modelindex ) ) - { - int t = BG_FindTeamForBuildable( ent->s.modelindex ); - - if( t == BIT_HUMANS ) - localHTP += BG_FindBuildPointsForBuildable( ent->s.modelindex ); - else if( t == BIT_ALIENS ) - localATP += BG_FindBuildPointsForBuildable( ent->s.modelindex ); - } - } - } - level.suddenDeathHBuildPoints = localHTP; - level.suddenDeathABuildPoints = localATP; - level.suddenDeathBeginTime = level.time; - level.suddenDeath=qtrue; - trap_Cvar_Set( "g_suddenDeath", "1" ); + // Sudden Death checks + if( G_TimeTilSuddenDeath( ) <= 0 && level.suddenDeathWarning < TW_PASSED ) + { + G_LogPrintf( "Beginning Sudden Death\n" ); + trap_SendServerCommand( -1, "cp \"Sudden Death!\"" ); + trap_SendServerCommand( -1, "print \"Beginning Sudden Death.\n\"" ); + level.suddenDeathWarning = TW_PASSED; + G_ClearDeconMarks( ); - level.suddenDeathWarning = TW_PASSED; - } - } - else + // Clear blueprints, or else structs that cost 0 BP can still be built after SD + for( i = 0; i < level.maxclients; i++ ) { - //warn about sudden death - if( ( G_TimeTilSuddenDeath( ) <= 60000 ) && - ( level.suddenDeathWarning < TW_IMMINENT ) ) - { - trap_SendServerCommand( -1, va("cp \"Sudden Death in %d seconds!\"", - (int)(G_TimeTilSuddenDeath() / 1000 ) ) ); - level.suddenDeathWarning = TW_IMMINENT; - } + if( g_entities[ i ].client->ps.stats[ STAT_BUILDABLE ] != BA_NONE ) + g_entities[ i ].client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; } } - - //set BP at each cycle - if( g_suddenDeath.integer ) + else if( G_TimeTilSuddenDeath( ) <= SUDDENDEATHWARNING && + level.suddenDeathWarning < TW_IMMINENT ) { - localHTP = level.suddenDeathHBuildPoints; - localATP = level.suddenDeathABuildPoints; + trap_SendServerCommand( -1, va( "cp \"Sudden Death in %d seconds!\"", + (int)( G_TimeTilSuddenDeath( ) / 1000 ) ) ); + trap_SendServerCommand( -1, va( "print \"Sudden Death will begin in %d seconds.\n\"", + (int)( G_TimeTilSuddenDeath( ) / 1000 ) ) ); + level.suddenDeathWarning = TW_IMMINENT; } - else + + level.humanBuildPoints = g_humanBuildPoints.integer - level.humanBuildPointQueue; + level.alienBuildPoints = g_alienBuildPoints.integer - level.alienBuildPointQueue; + + // Reset buildPointZones + for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ ) { - localHTP = g_humanBuildPoints.integer; - localATP = g_alienBuildPoints.integer; + buildPointZone_t *zone = &level.buildPointZones[ i ]; + + zone->active = qfalse; + zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer; } - level.humanBuildPoints = level.humanBuildPointsPowered = localHTP; - level.alienBuildPoints = localATP; + // Iterate through entities + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) + { + gentity_t *ent = &g_entities[ i ]; + buildPointZone_t *zone; + buildable_t buildable; + int cost; - level.reactorPresent = qfalse; - level.overmindPresent = qfalse; + if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD ) + continue; - for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ ) + // mark a zone as active + if( ent->usesBuildPointZone ) + { + assert( ent->buildPointZone >= 0 && ent->buildPointZone < g_humanRepeaterMaxZones.integer ); + + zone = &level.buildPointZones[ ent->buildPointZone ]; + zone->active = qtrue; + } + + // Subtract the BP from the appropriate pool + buildable = ent->s.modelindex; + cost = BG_Buildable( buildable )->buildPoints; + + if( ent->buildableTeam == TEAM_ALIENS ) + level.alienBuildPoints -= cost; + if( buildable == BA_H_REPEATER ) + level.humanBuildPoints -= cost; + else if( buildable != BA_H_REACTOR ) + { + gentity_t *power = G_PowerEntityForEntity( ent ); + + if( power ) + { + if( power->s.modelindex == BA_H_REACTOR ) + level.humanBuildPoints -= cost; + else if( power->s.modelindex == BA_H_REPEATER && power->usesBuildPointZone ) + level.buildPointZones[ power->buildPointZone ].totalBuildPoints -= cost; + } + } + } + + // Finally, update repeater zones and their queues + // note that this has to be done after the used BP is calculated + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) { - if( !ent->inuse ) - continue; + gentity_t *ent = &g_entities[ i ]; - if( ent->s.eType != ET_BUILDABLE ) + if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD || + ent->buildableTeam != TEAM_HUMANS ) continue; buildable = ent->s.modelindex; - if( buildable != BA_NONE ) - { - if( buildable == BA_H_REACTOR && ent->spawned && ent->health > 0 ) - level.reactorPresent = qtrue; + if( buildable != BA_H_REPEATER ) + continue; - if( buildable == BA_A_OVERMIND && ent->spawned && ent->health > 0 ) - level.overmindPresent = qtrue; + if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active ) + { + zone = &level.buildPointZones[ ent->buildPointZone ]; - if( !g_suddenDeath.integer || BG_FindReplaceableTestForBuildable( buildable ) ) + if( G_TimeTilSuddenDeath( ) > 0 ) { - if( BG_FindTeamForBuildable( buildable ) == BIT_HUMANS ) - { - level.humanBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); - if( ent->powered ) - level.humanBuildPointsPowered -= BG_FindBuildPointsForBuildable( buildable ); - } - else + // BP queue updates + while( zone->queuedBuildPoints > 0 && + zone->nextQueueTime < level.time ) { - level.alienBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); + zone->queuedBuildPoints--; + zone->nextQueueTime += G_NextQueueTime( zone->queuedBuildPoints, + zone->totalBuildPoints, + g_humanRepeaterBuildQueueTime.integer ); } } + else + { + zone->totalBuildPoints = zone->queuedBuildPoints = 0; + } } } if( level.humanBuildPoints < 0 ) - { - localHTP -= level.humanBuildPoints; - level.humanBuildPointsPowered -= level.humanBuildPoints; level.humanBuildPoints = 0; - } if( level.alienBuildPoints < 0 ) - { - localATP -= level.alienBuildPoints; level.alienBuildPoints = 0; - } - - trap_SetConfigstring( CS_BUILDPOINTS, va( "%d %d %d %d %d", - level.alienBuildPoints, localATP, - level.humanBuildPoints, localHTP, - level.humanBuildPointsPowered ) ); - - //may as well pump the stages here too - { - float alienPlayerCountMod = level.averageNumAlienClients / PLAYER_COUNT_MOD; - float humanPlayerCountMod = level.averageNumHumanClients / PLAYER_COUNT_MOD; - int alienNextStageThreshold, humanNextStageThreshold; - - if( alienPlayerCountMod < 0.1f ) - alienPlayerCountMod = 0.1f; - - if( humanPlayerCountMod < 0.1f ) - humanPlayerCountMod = 0.1f; - - if( g_alienStage.integer == S1 && g_alienMaxStage.integer > S1 ) - alienNextStageThreshold = (int)( ceil( (float)g_alienStage2Threshold.integer * alienPlayerCountMod ) ); - else if( g_alienStage.integer == S2 && g_alienMaxStage.integer > S2 ) - alienNextStageThreshold = (int)( ceil( (float)g_alienStage3Threshold.integer * alienPlayerCountMod ) ); - else - alienNextStageThreshold = -1; - - if( g_humanStage.integer == S1 && g_humanMaxStage.integer > S1 ) - humanNextStageThreshold = (int)( ceil( (float)g_humanStage2Threshold.integer * humanPlayerCountMod ) ); - else if( g_humanStage.integer == S2 && g_humanMaxStage.integer > S2 ) - humanNextStageThreshold = (int)( ceil( (float)g_humanStage3Threshold.integer * humanPlayerCountMod ) ); - else - humanNextStageThreshold = -1; - - trap_SetConfigstring( CS_STAGES, va( "%d %d %d %d %d %d", - g_alienStage.integer, g_humanStage.integer, - g_alienKills.integer, g_humanKills.integer, - alienNextStageThreshold, humanNextStageThreshold ) ); - } } /* @@ -1499,8 +1286,11 @@ void G_CalculateStages( void ) { float alienPlayerCountMod = level.averageNumAlienClients / PLAYER_COUNT_MOD; float humanPlayerCountMod = level.averageNumHumanClients / PLAYER_COUNT_MOD; + int alienNextStageThreshold, humanNextStageThreshold; static int lastAlienStageModCount = 1; static int lastHumanStageModCount = 1; + static int alienTriggerStage = 0; + static int humanTriggerStage = 0; if( alienPlayerCountMod < 0.1f ) alienPlayerCountMod = 0.1f; @@ -1508,7 +1298,7 @@ void G_CalculateStages( void ) if( humanPlayerCountMod < 0.1f ) humanPlayerCountMod = 0.1f; - if( g_alienKills.integer >= + if( g_alienCredits.integer >= (int)( ceil( (float)g_alienStage2Threshold.integer * alienPlayerCountMod ) ) && g_alienStage.integer == S1 && g_alienMaxStage.integer > S1 ) { @@ -1518,7 +1308,7 @@ void G_CalculateStages( void ) G_LogPrintf("Stage: A 2: Aliens reached Stage 2\n"); } - if( g_alienKills.integer >= + if( g_alienCredits.integer >= (int)( ceil( (float)g_alienStage3Threshold.integer * alienPlayerCountMod ) ) && g_alienStage.integer == S2 && g_alienMaxStage.integer > S2 ) { @@ -1528,7 +1318,7 @@ void G_CalculateStages( void ) G_LogPrintf("Stage: A 3: Aliens reached Stage 3\n"); } - if( g_humanKills.integer >= + if( g_humanCredits.integer >= (int)( ceil( (float)g_humanStage2Threshold.integer * humanPlayerCountMod ) ) && g_humanStage.integer == S1 && g_humanMaxStage.integer > S1 ) { @@ -1538,30 +1328,33 @@ void G_CalculateStages( void ) G_LogPrintf("Stage: H 2: Humans reached Stage 2\n"); } - if( g_humanKills.integer >= + if( g_humanCredits.integer >= (int)( ceil( (float)g_humanStage3Threshold.integer * humanPlayerCountMod ) ) && g_humanStage.integer == S2 && g_humanMaxStage.integer > S2 ) { trap_Cvar_Set( "g_humanStage", va( "%d", S3 ) ); level.humanStage3Time = level.time; - G_LogPrintf("Stage: H 3: Humans reached Stage 3\n"); lastHumanStageModCount = g_humanStage.modificationCount; + G_LogPrintf("Stage: H 3: Humans reached Stage 3\n"); } - + if( g_alienStage.modificationCount > lastAlienStageModCount ) { - G_Checktrigger_stages( PTE_ALIENS, g_alienStage.integer ); - if( g_alienStage.integer == S2 ) + while( alienTriggerStage < MIN( g_alienStage.integer, S3 ) ) + G_Checktrigger_stages( TEAM_ALIENS, ++alienTriggerStage ); + + if( g_alienStage.integer == S2 ) level.alienStage2Time = level.time; else if( g_alienStage.integer == S3 ) level.alienStage3Time = level.time; - + lastAlienStageModCount = g_alienStage.modificationCount; } - + if( g_humanStage.modificationCount > lastHumanStageModCount ) { - G_Checktrigger_stages( PTE_HUMANS, g_humanStage.integer ); + while( humanTriggerStage < MIN( g_humanStage.integer, S3 ) ) + G_Checktrigger_stages( TEAM_HUMANS, ++humanTriggerStage ); if( g_humanStage.integer == S2 ) level.humanStage2Time = level.time; @@ -1570,6 +1363,35 @@ void G_CalculateStages( void ) lastHumanStageModCount = g_humanStage.modificationCount; } + + if( g_alienStage.integer == S1 && g_alienMaxStage.integer > S1 ) + alienNextStageThreshold = (int)( ceil( (float)g_alienStage2Threshold.integer * alienPlayerCountMod ) ); + else if( g_alienStage.integer == S2 && g_alienMaxStage.integer > S2 ) + alienNextStageThreshold = (int)( ceil( (float)g_alienStage3Threshold.integer * alienPlayerCountMod ) ); + else + alienNextStageThreshold = -1; + + if( g_humanStage.integer == S1 && g_humanMaxStage.integer > S1 ) + humanNextStageThreshold = (int)( ceil( (float)g_humanStage2Threshold.integer * humanPlayerCountMod ) ); + else if( g_humanStage.integer == S2 && g_humanMaxStage.integer > S2 ) + humanNextStageThreshold = (int)( ceil( (float)g_humanStage3Threshold.integer * humanPlayerCountMod ) ); + else + humanNextStageThreshold = -1; + + // save a lot of bandwidth by rounding thresholds up to the nearest 100 + if( alienNextStageThreshold > 0 ) + alienNextStageThreshold = ceil( (float)alienNextStageThreshold / 100 ) * 100; + + if( humanNextStageThreshold > 0 ) + humanNextStageThreshold = ceil( (float)humanNextStageThreshold / 100 ) * 100; + + trap_SetConfigstring( CS_ALIEN_STAGES, va( "%d %d %d", + g_alienStage.integer, g_alienCredits.integer, + alienNextStageThreshold ) ); + + trap_SetConfigstring( CS_HUMAN_STAGES, va( "%d %d %d", + g_humanStage.integer, g_humanCredits.integer, + humanNextStageThreshold ) ); } /* @@ -1587,13 +1409,13 @@ void G_CalculateAvgPlayers( void ) if( !level.numAlienClients ) { level.numAlienSamples = 0; - trap_Cvar_Set( "g_alienKills", "0" ); + trap_Cvar_Set( "g_alienCredits", "0" ); } if( !level.numHumanClients ) { level.numHumanSamples = 0; - trap_Cvar_Set( "g_humanKills", "0" ); + trap_Cvar_Set( "g_humanCredits", "0" ); } //calculate average number of clients for stats @@ -1623,16 +1445,14 @@ void CalculateRanks( void ) { int i; char P[ MAX_CLIENTS + 1 ] = {""}; - int ff = 0; level.numConnectedClients = 0; - level.numNonSpectatorClients = 0; level.numPlayingClients = 0; - level.numVotingClients = 0; // don't count bots + memset( level.numVotingClients, 0, sizeof( level.numVotingClients ) ); level.numAlienClients = 0; level.numHumanClients = 0; - level.numLiveAlienClients = 0; - level.numLiveHumanClients = 0; + level.numAlienClientsAlive = 0; + level.numHumanClientsAlive = 0; for( i = 0; i < level.maxclients; i++ ) { @@ -1643,46 +1463,36 @@ void CalculateRanks( void ) level.numConnectedClients++; P[ i ] = (char)'0' + level.clients[ i ].pers.teamSelection; + level.numVotingClients[ TEAM_NONE ]++; + if( level.clients[ i ].pers.connected != CON_CONNECTED ) continue; - level.numVotingClients++; - if( level.clients[ i ].pers.teamSelection != PTE_NONE ) + if( level.clients[ i ].pers.teamSelection != TEAM_NONE ) { level.numPlayingClients++; - if( level.clients[ i ].sess.sessionTeam != TEAM_SPECTATOR ) - level.numNonSpectatorClients++; - - if( level.clients[ i ].pers.teamSelection == PTE_ALIENS ) + if( level.clients[ i ].pers.teamSelection == TEAM_ALIENS ) { level.numAlienClients++; - if( level.clients[ i ].sess.sessionTeam != TEAM_SPECTATOR ) - level.numLiveAlienClients++; + if( level.clients[ i ].sess.spectatorState == SPECTATOR_NOT ) + level.numAlienClientsAlive++; } - else if( level.clients[ i ].pers.teamSelection == PTE_HUMANS ) + else if( level.clients[ i ].pers.teamSelection == TEAM_HUMANS ) { level.numHumanClients++; - if( level.clients[ i ].sess.sessionTeam != TEAM_SPECTATOR ) - level.numLiveHumanClients++; + if( level.clients[ i ].sess.spectatorState == SPECTATOR_NOT ) + level.numHumanClientsAlive++; } } } } - level.numteamVotingClients[ 0 ] = level.numHumanClients; - level.numteamVotingClients[ 1 ] = level.numAlienClients; + level.numNonSpectatorClients = level.numAlienClientsAlive + + level.numHumanClientsAlive; + level.numVotingClients[ TEAM_ALIENS ] = level.numAlienClients; + level.numVotingClients[ TEAM_HUMANS ] = level.numHumanClients; P[ i ] = '\0'; trap_Cvar_Set( "P", P ); - if( g_friendlyFire.value>0 ) - ff |= ( FFF_HUMANS | FFF_ALIENS ); - if( g_friendlyFireHumans.value>0 ) - ff |= FFF_HUMANS; - if( g_friendlyFireAliens.value>0 ) - ff |= FFF_ALIENS; - if( g_friendlyBuildableFire.value>0 ) - ff |= FFF_BUILDABLES; - trap_Cvar_Set( "ff", va( "%i", ff ) ); - qsort( level.sortedClients, level.numConnectedClients, sizeof( level.sortedClients[ 0 ] ), SortRanks ); @@ -1690,7 +1500,7 @@ void CalculateRanks( void ) CheckExitRules( ); // if we are at the intermission, send the new info to everyone - if( level.intermissiontime && !level.mapRotationVoteTime ) + if( level.intermissiontime ) SendScoreboardMessageToAllClients( ); } @@ -1737,8 +1547,10 @@ void MoveClientToIntermission( gentity_t *ent ) G_StopFollowing( ent ); // move to the spot - VectorCopy( level.intermission_origin, ent->s.origin ); + VectorCopy( level.intermission_origin, ent->s.pos.trBase ); + VectorCopy( level.intermission_origin, ent->r.currentOrigin ); VectorCopy( level.intermission_origin, ent->client->ps.origin ); + VectorCopy( level.intermission_angle, ent->s.apos.trBase ); VectorCopy( level.intermission_angle, ent->client->ps.viewangles ); ent->client->ps.pm_type = PM_INTERMISSION; @@ -1748,7 +1560,6 @@ void MoveClientToIntermission( gentity_t *ent ) ent->client->ps.eFlags = 0; ent->s.eFlags = 0; ent->s.eType = ET_GENERAL; - ent->s.modelindex = 0; ent->s.loopSound = 0; ent->s.event = 0; ent->r.contents = 0; @@ -1775,8 +1586,8 @@ void FindIntermissionPoint( void ) } else { - VectorCopy( ent->s.origin, level.intermission_origin ); - VectorCopy( ent->s.angles, level.intermission_angle ); + VectorCopy( ent->r.currentOrigin, level.intermission_origin ); + VectorCopy( ent->r.currentAngles, level.intermission_angle ); // if it has a target, look towards it if( ent->target ) { @@ -1784,7 +1595,7 @@ void FindIntermissionPoint( void ) if( target ) { - VectorSubtract( target->s.origin, level.intermission_origin, dir ); + VectorSubtract( target->r.currentOrigin, level.intermission_origin, dir ); vectoangles( dir, level.intermission_angle ); } } @@ -1805,12 +1616,12 @@ void BeginIntermission( void ) if( level.intermissiontime ) return; // already active - level.numTeamWarnings = 99; - level.intermissiontime = level.time; G_ClearVotes( ); + G_UpdateTeamConfigStrings( ); + FindIntermissionPoint( ); // move all clients to the intermission point @@ -1830,42 +1641,8 @@ void BeginIntermission( void ) // send the current scoring to all clients SendScoreboardMessageToAllClients( ); - - if( g_nextMap.string[ 0 ] ) - { - trap_SendServerCommand( -1, - va( "print \"next map has been set to %s^7%s\n\"", - g_nextMap.string, - ( G_CheckMapRotationVote() ) ? ", voting will be skipped" : "" ) ); - } } -void BeginMapRotationVote( void ) -{ - gentity_t *ent; - int length; - int i; - - if( level.mapRotationVoteTime ) - return; - - length = g_mapRotationVote.integer; - if( length > 60 ) - length = 60; - level.mapRotationVoteTime = level.time + ( length * 1000 ); - - for( i = 0; i < level.maxclients; i++ ) - { - ent = g_entities + i; - - if( !ent->inuse ) - continue; - - ent->client->ps.pm_type = PM_SPECTATOR; - ent->client->sess.sessionTeam = TEAM_SPECTATOR; - ent->client->sess.spectatorState = SPECTATOR_LOCKED; - } -} /* ============= @@ -1879,44 +1656,22 @@ void ExitLevel( void ) { int i; gclient_t *cl; - buildHistory_t *tmp, *mark; - - char currentmap[ MAX_CVAR_VALUE_STRING ]; - - trap_Cvar_VariableStringBuffer( "mapname", currentmap, sizeof( currentmap )); - - if( level.mapRotationVoteTime ) - { - if( level.time < level.mapRotationVoteTime && - !G_IntermissionMapVoteWinner( ) ) - return; - } - else if( g_mapRotationVote.integer > 0 && - G_CheckMapRotationVote() && - !g_nextMap.string[ 0 ] ) - { - BeginMapRotationVote( ); - return; - } - while( ( tmp = level.buildHistory ) ) + if ( G_MapExists( g_nextMap.string ) ) { - level.buildHistory = level.buildHistory->next; - while( ( mark = tmp ) ) - { - tmp = tmp->marked; - G_Free( mark ); - } + G_MapConfigs( g_nextMap.string ); + trap_SendConsoleCommand( EXEC_APPEND, va( "%smap \"%s\"\n", + ( g_cheats.integer ? "dev" : "" ), g_nextMap.string ) ); } - - if( !Q_stricmp( currentmap, g_nextMap.string ) ) - trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" ); - else if ( G_MapExists( g_nextMap.string ) ) - trap_SendConsoleCommand( EXEC_APPEND, va("!map %s\n", g_nextMap.string ) ); else if( G_MapRotationActive( ) ) - G_AdvanceMapRotation( ); + G_AdvanceMapRotation( 0 ); else + { + char map[ MAX_CVAR_VALUE_STRING ]; + trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); + G_MapConfigs( map ); trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" ); + } trap_Cvar_Set( "g_nextMap", "" ); @@ -1934,7 +1689,7 @@ void ExitLevel( void ) cl->ps.persistant[ PERS_SCORE ] = 0; } - // we need to do this here before chaning to CON_CONNECTING + // we need to do this here before changing to CON_CONNECTING G_WriteSessionData( ); // change all client states to connecting, so the early players into the @@ -1946,79 +1701,48 @@ void ExitLevel( void ) } } + /* ================= -G_AdminsPrintf +G_AdminMessage -Print to all active admins, and the logfile with a time stamp if it is open, and to the console +Print to all active server admins, and to the logfile, and to the server console ================= */ -void QDECL G_AdminsPrintf( const char *fmt, ... ) +void G_AdminMessage( gentity_t *ent, const char *msg ) { - va_list argptr; char string[ 1024 ]; - gentity_t *tempent; - int j; - - va_start( argptr, fmt ); - vsprintf( string, fmt,argptr ); - va_end( argptr ); + int i; - for( j = 0; j < level.maxclients; j++ ) - { - tempent = &g_entities[ j ]; - if( G_admin_permission( tempent, ADMF_ADMINCHAT ) && - !tempent->client->pers.ignoreAdminWarnings ) - { - trap_SendServerCommand(tempent-g_entities,va( "print \"^6[Admins]^7 %s\"", string) ); - } - } - - G_LogPrintf("%s",string); + Com_sprintf( string, sizeof( string ), "chat %d %d \"%s\"", + (int)( ent ? ent - g_entities : -1 ), + G_admin_permission( ent, ADMF_ADMINCHAT ) ? SAY_ADMINS : SAY_ADMINS_PUBLIC, + msg ); + // Send to all appropriate clients + for( i = 0; i < level.maxclients; i++ ) + if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) ) + trap_SendServerCommand( i, string ); + + // Send to the logfile and server console + G_LogPrintf( "%s: %d \"%s" S_COLOR_WHITE "\": " S_COLOR_MAGENTA "%s\n", + G_admin_permission( ent, ADMF_ADMINCHAT ) ? "AdminMsg" : "AdminMsgPublic", + (int)( ent ? ent - g_entities : -1 ), ent ? ent->client->pers.netname : "console", + msg ); } -/* -================= -G_WarningsPrintf - -Print to everyone with a certain flag, and the logfile with a time stamp if it is open, and to the console -(just a copy of the G_AdminsPrintf with flag suport) -================= -*/ -void QDECL G_WarningsPrintf( char *flag, const char *fmt, ... ) -{ - va_list argptr; - char string[ 1024 ]; - gentity_t *tempent; - int j; - va_start( argptr, fmt ); - vsprintf( string, fmt,argptr ); - va_end( argptr ); - - for( j = 0; j < level.maxclients; j++ ) - { - tempent = &g_entities[ j ]; - if( G_admin_permission( tempent, flag ) ) - { - trap_SendServerCommand(tempent-g_entities,va( "print \"^6[Warnings]^7 %s\"", string) ); - } - } - - G_LogPrintf("%s",string); -} /* ================= G_LogPrintf -Print to the logfile with a time stamp if it is open +Print to the logfile with a time stamp if it is open, and to the server console ================= */ void QDECL G_LogPrintf( const char *fmt, ... ) { va_list argptr; - char string[ 1024 ], decoloured[ 1024 ]; + char string[ 1024 ], decolored[ 1024 ]; int min, tens, sec; sec = ( level.time - level.startTime ) / 1000; @@ -2031,186 +1755,20 @@ void QDECL G_LogPrintf( const char *fmt, ... ) Com_sprintf( string, sizeof( string ), "%3i:%i%i ", min, tens, sec ); va_start( argptr, fmt ); - vsprintf( string +7 , fmt,argptr ); + Q_vsnprintf( string + 7, sizeof( string ) - 7, fmt, argptr ); va_end( argptr ); if( g_dedicated.integer ) - G_Printf( "%s", string + 7 ); - - if( !level.logFile ) - return; - - if( g_decolourLogfiles.integer ) - { - G_DecolorString( string, decoloured ); - trap_FS_Write( decoloured, strlen( decoloured ), level.logFile ); - } - else { - trap_FS_Write( string, strlen( string ), level.logFile ); + G_UnEscapeString( string, decolored, sizeof( decolored ) ); + G_Printf( "%s", decolored + 7 ); } -} - -/* -================= -G_LogPrintfColoured - -Bypasses g_decolourLogfiles for events that need colors in the logs -================= -*/ -void QDECL G_LogPrintfColoured( const char *fmt, ... ) -{ - va_list argptr; - char string[ 1024 ]; - int min, tens, sec; - - sec = (level.time - level.startTime) / 1000; - - min = sec / 60; - sec -= min * 60; - tens = sec / 10; - sec -= tens * 10; - - Com_sprintf( string, sizeof( string ), "%3i:%i%i ", min, tens, sec ); - - va_start( argptr, fmt ); - vsprintf( string +7 , fmt,argptr ); - va_end( argptr ); - - if( g_dedicated.integer ) - G_Printf( "%s", string + 7 ); if( !level.logFile ) return; - trap_FS_Write( string, strlen( string ), level.logFile ); -} - -/* -================= -G_LogOnlyPrintf - -Print to the logfile only (not console) with a time stamp if it is open -================= -*/ -void QDECL G_LogOnlyPrintf( const char *fmt, ... ) -{ - va_list argptr; - char string[ 1024 ], decoloured[ 1024 ]; - int min, tens, sec; - - sec = (level.time - level.startTime) / 1000; - - min = sec / 60; - sec -= min * 60; - tens = sec / 10; - sec -= tens * 10; - - Com_sprintf( string, sizeof( string ), "%3i:%i%i ", min, tens, sec ); - - va_start( argptr, fmt ); - vsprintf( string +7 , fmt,argptr ); - va_end( argptr ); - - if( !level.logFile ) - return; - - if( g_decolourLogfiles.integer ) - { - G_DecolorString( string, decoloured ); - trap_FS_Write( decoloured, strlen( decoloured ), level.logFile ); - } - else - { - trap_FS_Write( string, strlen( string ), level.logFile ); - } -} - -/* -================= -G_SendGameStat -================= -*/ -void G_SendGameStat( pTeam_t team ) -{ - char map[ MAX_STRING_CHARS ]; - char teamChar; - char data[ BIG_INFO_STRING ]; - char entry[ MAX_STRING_CHARS ]; - int i, dataLength, entryLength; - gclient_t *cl; - - trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); - - switch( team ) - { - case PTE_ALIENS: teamChar = 'A'; break; - case PTE_HUMANS: teamChar = 'H'; break; - case PTE_NONE: teamChar = 'L'; break; - default: return; - } - - Com_sprintf( data, BIG_INFO_STRING, - "%s %s T:%c A:%f H:%f M:%s D:%d SD:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d CL:%d", - Q3_VERSION, - g_tag.string, - teamChar, - level.averageNumAlienClients, - level.averageNumHumanClients, - map, - level.time - level.startTime, - G_TimeTilSuddenDeath( ), - g_alienStage.integer, - level.alienStage2Time - level.startTime, - level.alienStage3Time - level.startTime, - g_humanStage.integer, - level.humanStage2Time - level.startTime, - level.humanStage3Time - level.startTime, - level.numConnectedClients ); - - dataLength = strlen( data ); - - for( i = 0; i < level.numConnectedClients; i++ ) - { - int ping; - - cl = &level.clients[ level.sortedClients[ i ] ]; - - // Ignore invisible players - if ( cl->sess.invisible == qtrue ) - continue; - - if( cl->pers.connected == CON_CONNECTING ) - ping = -1; - else - ping = cl->ps.ping < 999 ? cl->ps.ping : 999; - - switch( cl->ps.stats[ STAT_PTEAM ] ) - { - case PTE_ALIENS: teamChar = 'A'; break; - case PTE_HUMANS: teamChar = 'H'; break; - case PTE_NONE: teamChar = 'S'; break; - default: return; - } - - Com_sprintf( entry, MAX_STRING_CHARS, - " \"%s\" %c %d %d %d", - cl->pers.netname, - teamChar, - cl->ps.persistant[ PERS_SCORE ], - ping, - ( level.time - cl->pers.enterTime ) / 60000 ); - - entryLength = strlen( entry ); - - if( dataLength + entryLength >= BIG_INFO_STRING ) - break; - - strcpy( data + dataLength, entry ); - dataLength += entryLength; - } - - trap_SendGameStat( data ); + G_DecolorString( string, decolored, sizeof( decolored ) ); + trap_FS_Write( decolored, strlen( decolored ), level.logFile ); } /* @@ -2226,6 +1784,8 @@ void LogExit( const char *string ) gclient_t *cl; gentity_t *ent; + level.exited = qtrue; + G_LogPrintf( "Exit: %s\n", string ); level.intermissionQueued = level.time; @@ -2245,7 +1805,7 @@ void LogExit( const char *string ) cl = &level.clients[ level.sortedClients[ i ] ]; - if( cl->ps.stats[ STAT_PTEAM ] == PTE_NONE ) + if( cl->ps.stats[ STAT_TEAM ] == TEAM_NONE ) continue; if( cl->pers.connected == CON_CONNECTING ) @@ -2270,8 +1830,6 @@ void LogExit( const char *string ) ent->use( ent, ent, ent ); } } - - G_SendGameStat( level.lastWin ); } @@ -2287,20 +1845,13 @@ wait 10 seconds before going on. */ void CheckIntermissionExit( void ) { - int ready, notReady, numPlayers; + int ready, notReady; int i; gclient_t *cl; - int readyMask; + clientList_t readyMasks; //if no clients are connected, just exit - if( !level.numConnectedClients ) - { - ExitLevel( ); - return; - } - - // map vote started - if( level.mapRotationVoteTime ) + if( level.numConnectedClients == 0 ) { ExitLevel( ); return; @@ -2309,30 +1860,28 @@ void CheckIntermissionExit( void ) // see which players are ready ready = 0; notReady = 0; - readyMask = 0; - numPlayers = 0; + Com_Memset( &readyMasks, 0, sizeof( readyMasks ) ); for( i = 0; i < g_maxclients.integer; i++ ) { cl = level.clients + i; + if( cl->pers.connected != CON_CONNECTED ) continue; - if( cl->ps.stats[ STAT_PTEAM ] == PTE_NONE ) + if( cl->ps.stats[ STAT_TEAM ] == TEAM_NONE ) continue; if( cl->readyToExit ) { ready++; - if( i < 16 ) - readyMask |= 1 << i; + + Com_ClientListAdd( &readyMasks, i ); } else notReady++; - - numPlayers++; } - trap_SetConfigstring( CS_CLIENTS_READY, va( "%d", readyMask ) ); + trap_SetConfigstring( CS_CLIENTS_READY, Com_ClientListString( &readyMasks ) ); // never exit in less than five seconds if( level.time < level.intermissiontime + 5000 ) @@ -2346,22 +1895,14 @@ void CheckIntermissionExit( void ) } // if nobody wants to go, clear timer - if( !ready && numPlayers ) + if( ready == 0 && notReady > 0 ) { level.readyToExit = qfalse; return; } // if everyone wants to go, go now - if( !notReady ) - { - ExitLevel( ); - return; - } - - // if only a percent is needed to ready, check for it - if( g_readyPercent.integer && numPlayers && - ready * 100 / numPlayers >= g_readyPercent.integer ) + if( notReady == 0 ) { ExitLevel( ); return; @@ -2434,11 +1975,10 @@ void CheckExitRules( void ) { if( level.time - level.startTime >= g_timelimit.integer * 60000 ) { - level.lastWin = PTE_NONE; + level.lastWin = TEAM_NONE; trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" ); trap_SetConfigstring( CS_WINNER, "Stalemate" ); LogExit( "Timelimit hit." ); - G_admin_maplog_result( "t" ); return; } else if( level.time - level.startTime >= ( g_timelimit.integer - 5 ) * 60000 && @@ -2456,295 +1996,177 @@ void CheckExitRules( void ) } if( level.uncondHumanWin || - ( ( level.time > level.startTime + 1000 ) && + ( !level.uncondAlienWin && + ( level.time > level.startTime + 1000 ) && ( level.numAlienSpawns == 0 ) && - ( level.numLiveAlienClients == 0 ) ) ) + ( level.numAlienClientsAlive == 0 ) ) ) { //humans win - level.lastWin = PTE_HUMANS; + level.lastWin = TEAM_HUMANS; trap_SendServerCommand( -1, "print \"Humans win\n\""); trap_SetConfigstring( CS_WINNER, "Humans Win" ); LogExit( "Humans win." ); - G_admin_maplog_result( "h" ); } else if( level.uncondAlienWin || ( ( level.time > level.startTime + 1000 ) && ( level.numHumanSpawns == 0 ) && - ( level.numLiveHumanClients == 0 ) ) ) + ( level.numHumanClientsAlive == 0 ) ) ) { //aliens win - level.lastWin = PTE_ALIENS; + level.lastWin = TEAM_ALIENS; trap_SendServerCommand( -1, "print \"Aliens win\n\""); trap_SetConfigstring( CS_WINNER, "Aliens Win" ); LogExit( "Aliens win." ); - G_admin_maplog_result( "a" ); } } - - -/* -======================================================================== - -FUNCTIONS CALLED EVERY FRAME - -======================================================================== -*/ - - /* ================== -CheckVote +G_Vote ================== */ -void CheckVote( void ) +void G_Vote( gentity_t *ent, team_t team, qboolean voting ) { - int votePassThreshold=level.votePassThreshold; - int voteYesPercent; - - if( level.voteExecuteTime && level.voteExecuteTime < level.time ) - { - level.voteExecuteTime = 0; - - if( !Q_stricmp( level.voteString, "map_restart" ) ) - { - G_admin_maplog_result( "r" ); - } - else if( !Q_stricmpn( level.voteString, "map", 3 ) ) - { - G_admin_maplog_result( "m" ); - } - - - if( !Q_stricmp( level.voteString, "suddendeath" ) ) - { - level.suddenDeathBeginTime = level.time + ( 1000 * g_suddenDeathVoteDelay.integer ) - level.startTime; - - level.voteString[0] = '\0'; - - if( g_suddenDeathVoteDelay.integer ) - trap_SendServerCommand( -1, va("cp \"Sudden Death will begin in %d seconds\n\"", g_suddenDeathVoteDelay.integer ) ); - } - - if( level.voteString[0] ) - trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) ); + if( !level.voteTime[ team ] ) + return; - if( !Q_stricmp( level.voteString, "map_restart" ) || - !Q_stricmpn( level.voteString, "map", 3 ) ) - { - level.restarted = qtrue; - } - } + if( voting && ent->client->pers.voted & ( 1 << team ) ) + return; - if( !level.voteTime ) + if( !voting && !( ent->client->pers.voted & ( 1 << team ) ) ) return; - if( level.voteYes + level.voteNo > 0 ) - voteYesPercent = (int)( 100 * ( level.voteYes ) / ( level.voteYes + level.voteNo ) ); - else - voteYesPercent = 0; - - if( ( level.time - level.voteTime >= VOTE_TIME ) || - ( level.voteYes + level.voteNo == level.numConnectedClients ) ) + ent->client->pers.voted |= 1 << team; + + if( ent->client->pers.vote & ( 1 << team ) ) { - if( voteYesPercent> votePassThreshold || level.voteNo == 0 ) - { - // execute the command, then remove the vote - trap_SendServerCommand( -1, va("print \"Vote passed (%d - %d)\n\"", - level.voteYes, level.voteNo ) ); - G_LogPrintf( "Vote: Vote passed (%d-%d)\n", level.voteYes, level.voteNo ); - level.voteExecuteTime = level.time + 3000; - } + if( voting ) + level.voteYes[ team ]++; else - { - // same behavior as a timeout - trap_SendServerCommand( -1, va("print \"Vote failed (%d - %d)\n\"", - level.voteYes, level.voteNo ) ); - G_LogPrintf( "Vote: Vote failed (%d - %d)\n", level.voteYes, level.voteNo ); - } + level.voteYes[ team ]--; + + trap_SetConfigstring( CS_VOTE_YES + team, + va( "%d", level.voteYes[ team ] ) ); } else { - if( level.voteYes > (int)( (double) level.numConnectedClients * - ( (double) votePassThreshold/100.0 ) ) ) - { - // execute the command, then remove the vote - trap_SendServerCommand( -1, va("print \"Vote passed (%d - %d)\n\"", - level.voteYes, level.voteNo ) ); - G_LogPrintf( "Vote: Vote passed (%d - %d)\n", level.voteYes, level.voteNo ); - level.voteExecuteTime = level.time + 3000; - } - else if( level.voteNo > (int)( (double) level.numConnectedClients * - ( (double) ( 100.0-votePassThreshold )/ 100.0 ) ) ) - { - // same behavior as a timeout - trap_SendServerCommand( -1, va("print \"Vote failed (%d - %d)\n\"", - level.voteYes, level.voteNo ) ); - G_LogPrintf("Vote failed\n"); - } + if( voting ) + level.voteNo[ team ]++; else - { - // still waiting for a majority - return; - } - } + level.voteNo[ team ]--; - level.voteTime = 0; - trap_SetConfigstring( CS_VOTE_TIME, "" ); - trap_SetConfigstring( CS_VOTE_STRING, "" ); + trap_SetConfigstring( CS_VOTE_NO + team, + va( "%d", level.voteNo[ team ] ) ); + } } /* -================== -CheckTeamVote -================== +======================================================================== + +FUNCTIONS CALLED EVERY FRAME + +======================================================================== */ -void CheckTeamVote( int team ) -{ - int cs_offset; - if ( team == PTE_HUMANS ) - cs_offset = 0; - else if ( team == PTE_ALIENS ) - cs_offset = 1; - else - return; - if( !level.teamVoteTime[ cs_offset ] ) - return; +void G_ExecuteVote( team_t team ) +{ + level.voteExecuteTime[ team ] = 0; - if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME ) + if( !Q_stricmpn( level.voteString[ team ], "map_restart", 11 ) ) { - if( level.teamVoteYes[ cs_offset ] > level.teamVoteNo[ cs_offset ] && level.teamVoteYes[ cs_offset ] >= 2 ) - { - // execute the command, then remove the vote - trap_SendServerCommand( -1, va("print \"Team vote passed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) ); - trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); - } - else - { - trap_SendServerCommand( -1, va("print \"Team vote failed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) ); - G_LogPrintf( "Teamvote: Team vote failed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ); - } + char map[ MAX_QPATH ]; + trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); + G_MapConfigs( map ); } - else + else if( !Q_stricmpn( level.voteString[ team ], "map", 3 ) ) { - if( level.teamVoteYes[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 ) - { - // execute the command, then remove the vote - trap_SendServerCommand( -1, va("print \"Team vote passed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) ); - G_LogPrintf( "Teamvote: Team vote passed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ); - // - trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); - } - else if( level.teamVoteNo[ cs_offset ] >= level.numteamVotingClients[ cs_offset ] / 2 ) - { - // same behavior as a timeout - trap_SendServerCommand( -1, va("print \"Team vote failed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) ); - G_LogPrintf( "Teamvote: Team vote failed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ); - } - else - { - // still waiting for a majority - return; - } + char map[ MAX_QPATH ]; + char *p; + Q_strncpyz( map, strchr( level.voteString[ team ], '"' ) + 1, sizeof( map ) ); + if( ( p = strchr( map, '"' ) ) ) + *p = '\0'; + G_MapConfigs( map ); } - level.teamVoteTime[ cs_offset ] = 0; - trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" ); - trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, "" ); + trap_SendConsoleCommand( EXEC_APPEND, va( "%s%s\n", + ( !Q_stricmp( level.voteString[ team ], "map" ) && g_cheats.integer ? "dev" : "" ), + level.voteString[ team ] ) ); + + if( !Q_stricmpn( level.voteString[ team ], "map", 3 ) ) + level.restarted = qtrue; } /* ================== -CheckMsgTimer +G_CheckVote ================== */ -void CheckMsgTimer( void ) +void G_CheckVote( team_t team ) { - static int LastTime = 0; + float votePassThreshold = (float)level.voteThreshold[ team ] / 100.0f; + qboolean pass = qfalse; + const char *msg; + int i; - if( level.time - LastTime < 1000 ) - return; + if( level.voteExecuteTime[ team ] && + level.voteExecuteTime[ team ] < level.time ) + { + G_ExecuteVote( team ); + } - LastTime = level.time; + if( !level.voteTime[ team ] ) + return; - if( level.mapRotationVoteTime ) + if( ( level.time - level.voteTime[ team ] >= VOTE_TIME ) || + ( level.voteYes[ team ] + level.voteNo[ team ] == level.numVotingClients[ team ] ) ) { - G_IntermissionMapVoteMessageAll( ); - return; + pass = ( level.voteYes[ team ] && + (float)level.voteYes[ team ] / ( (float)level.voteYes[ team ] + (float)level.voteNo[ team ] ) > votePassThreshold ); } - - if( g_welcomeMsgTime.integer && g_welcomeMsg.string[ 0 ] ) + else { - char buffer[ MAX_STRING_CHARS ]; - int wt; - int i; - - buffer[ 0 ] = '\0'; - wt = g_welcomeMsgTime.integer * 1000; - for( i = 0; i < level.maxclients; i++ ) + if( (float)level.voteYes[ team ] > + (float)level.numVotingClients[ team ] * votePassThreshold ) { - if( level.clients[ i ].pers.connected != CON_CONNECTED ) - continue; - - if( level.time - level.clients[ i ].pers.enterTime < wt ) - { - if( buffer[ 0 ] == '\0' ) - { - Q_strncpyz( buffer, g_welcomeMsg.string, sizeof( buffer ) ); - G_ParseEscapedString( buffer ); - } - trap_SendServerCommand( i, va( "cp \"%s\"", buffer ) ); - } + pass = qtrue; + } + else if( (float)level.voteNo[ team ] <= + (float)level.numVotingClients[ team ] * ( 1.0f - votePassThreshold ) ) + { + return; } } - if( !g_msgTime.integer ) - return; + if( pass ) + level.voteExecuteTime[ team ] = level.time + level.voteDelay[ team ]; - if( level.time - level.lastMsgTime < abs( g_msgTime.integer ) * 60000 ) - return; + G_LogPrintf( "EndVote: %s %s %d %d %d\n", + team == TEAM_NONE ? "global" : BG_TeamName( team ), + pass ? "pass" : "fail", + level.voteYes[ team ], level.voteNo[ team ], level.numVotingClients[ team ] ); - // negative settings only print once per map - if( ( level.lastMsgTime ) && g_msgTime.integer < 0 ) - return; + msg = va( "print \"%sote %sed (%d - %d)\n\"", + team == TEAM_NONE ? "V" : "Team v", pass ? "pass" : "fail", + level.voteYes[ team ], level.voteNo[ team ] ); - level.lastMsgTime = level.time; - - if( g_msg.string[0] ) - { - char buffer[ MAX_STRING_CHARS ]; - - Q_strncpyz( buffer, g_msg.string, sizeof( buffer ) ); - G_ParseEscapedString( buffer ); - trap_SendServerCommand( -1, va( "cp \"%s\"", buffer ) ); - trap_SendServerCommand( -1, va( "print \"%s\n\"", buffer ) ); - } -} - -/* -================== -CheckCountdown -================== -*/ -void CheckCountdown( void ) -{ - static int lastmsg = 0; - int timeleft = g_warmup.integer - ( level.time - level.startTime ) / 1000; + if( team == TEAM_NONE ) + trap_SendServerCommand( -1, msg ); + else + G_TeamCommand( team, msg ); - if( !g_doWarmup.integer || timeleft < 0 ) - return; + level.voteTime[ team ] = 0; + level.voteYes[ team ] = 0; + level.voteNo[ team ] = 0; - if( level.time - lastmsg < 1000 ) - return; + for( i = 0; i < level.maxclients; i++ ) + level.clients[ i ].pers.voted &= ~( 1 << team ); - lastmsg = level.time; - if( timeleft > 0 ) - trap_SendServerCommand( -1, va( "cp \"^1Warmup Time:^7\n^%i----- ^7%i ^%i-----\"", timeleft % 7, timeleft, timeleft % 7 ) ); - else if( timeleft == 0 ) - trap_SendServerCommand( -1, "cp \"^2----- GO! -----^7\"" ); + trap_SetConfigstring( CS_VOTE_TIME + team, "" ); + trap_SetConfigstring( CS_VOTE_STRING + team, "" ); + trap_SetConfigstring( CS_VOTE_YES + team, "0" ); + trap_SetConfigstring( CS_VOTE_NO + team, "0" ); } @@ -2758,6 +2180,7 @@ void CheckCvars( void ) static int lastPasswordModCount = -1; static int lastMarkDeconModCount = -1; static int lastSDTimeModCount = -1; + static int lastNumZones = 0; if( g_password.modificationCount != lastPasswordModCount ) { @@ -2773,29 +2196,36 @@ void CheckCvars( void ) // the server setting is changed if( g_markDeconstruct.modificationCount != lastMarkDeconModCount ) { - int i; - gentity_t *ent; - lastMarkDeconModCount = g_markDeconstruct.modificationCount; - - for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ ) - { - if( !ent->inuse ) - continue; - - if( ent->s.eType != ET_BUILDABLE ) - continue; - - ent->deconstruct = qfalse; - } + G_ClearDeconMarks( ); } + // If we change g_suddenDeathTime during a map, we need to update + // when sd will begin if( g_suddenDeathTime.modificationCount != lastSDTimeModCount ) { lastSDTimeModCount = g_suddenDeathTime.modificationCount; level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000; } + // If the number of zones changes, we need a new array + if( g_humanRepeaterMaxZones.integer != lastNumZones ) + { + buildPointZone_t *newZones; + size_t newsize = g_humanRepeaterMaxZones.integer * sizeof( buildPointZone_t ); + size_t oldsize = lastNumZones * sizeof( buildPointZone_t ); + + newZones = BG_Alloc( newsize ); + if( level.buildPointZones ) + { + Com_Memcpy( newZones, level.buildPointZones, MIN( oldsize, newsize ) ); + BG_Free( level.buildPointZones ); + } + + level.buildPointZones = newZones; + lastNumZones = g_humanRepeaterMaxZones.integer; + } + level.frameMsec = trap_Milliseconds( ); } @@ -2855,47 +2285,62 @@ Advances the non-player objects in the world */ void G_RunFrame( int levelTime ) { - int i; - gentity_t *ent; - int msec; - int start, end; + int i; + gentity_t *ent; + int msec; + static int ptime3000 = 0; // if we are waiting for the level to restart, do nothing if( level.restarted ) return; - - if( level.paused ) + + if( level.pausedTime ) { - if( ( levelTime % 6000 ) == 0) - trap_SendServerCommand( -1, "cp \"^3Game is paused.\"" ); + msec = levelTime - level.time - level.pausedTime; + level.pausedTime = levelTime - level.time; + + ptime3000 += msec; + while( ptime3000 > 3000 ) + { + ptime3000 -= 3000; + trap_SendServerCommand( -1, "cp \"The game has been paused. Please wait.\"" ); - level.startTime += levelTime - level.time; - trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime ) ); + if( level.pausedTime >= 110000 && level.pausedTime <= 119000 ) + trap_SendServerCommand( -1, va( "print \"Server: Game will auto-unpause in %d seconds\n\"", + (int) ( (float) ( 120000 - level.pausedTime ) / 1000.0f ) ) ); + } - if( levelTime - level.pauseTime > 3 * 60000 ) + // Prevents clients from getting lagged-out messages + for( i = 0; i < level.maxclients; i++ ) { - trap_SendConsoleCommand( EXEC_APPEND, "!unpause" ); + if( level.clients[ i ].pers.connected == CON_CONNECTED ) + level.clients[ i ].ps.commandTime = levelTime; } + + if( level.pausedTime > 120000 ) + { + trap_SendServerCommand( -1, "print \"Server: The game has been unpaused automatically (2 minute max)\n\"" ); + trap_SendServerCommand( -1, "cp \"The game has been unpaused!\"" ); + level.pausedTime = 0; + } + + return; } - CheckMsgTimer( ); - CheckCountdown( ); - level.framenum++; level.previousTime = level.time; level.time = levelTime; msec = level.time - level.previousTime; - //TA: seed the rng - srand( level.framenum ); - // get any cvar changes G_UpdateCvars( ); + CheckCvars( ); + // now we are done spawning + level.spawning = qfalse; // // go through all allocated objects // - start = trap_Milliseconds( ); ent = &g_entities[ 0 ]; for( i = 0; i < level.num_entities; i++, ent++ ) @@ -2935,7 +2380,7 @@ void G_RunFrame( int levelTime ) if( ent->freeAfterEvent ) continue; - //TA: calculate the acceleration of this entity + // calculate the acceleration of this entity if( ent->evaluateAcceleration ) G_EvaluateAcceleration( ent, msec ); @@ -2948,6 +2393,12 @@ void G_RunFrame( int levelTime ) continue; } + if ( ent->s.eType == ET_WEAPON_DROP ) + { + G_RunWeaponDrop( ent ); + continue; + } + if( ent->s.eType == ET_BUILDABLE ) { G_BuildableThink( ent, msec ); @@ -2974,9 +2425,6 @@ void G_RunFrame( int levelTime ) G_RunThink( ent ); } - end = trap_Milliseconds(); - - start = trap_Milliseconds(); // perform final fixups on the players ent = &g_entities[ 0 ]; @@ -2987,19 +2435,20 @@ void G_RunFrame( int levelTime ) ClientEndFrame( ent ); } - // save position information for all active clients + // save position information for all active clients G_UnlaggedStore( ); - end = trap_Milliseconds(); - - //TA: G_CountSpawns( ); - G_CalculateBuildPoints( ); - G_CalculateStages( ); - G_SpawnClients( PTE_ALIENS ); - G_SpawnClients( PTE_HUMANS ); - G_CalculateAvgPlayers( ); - G_UpdateZaps( msec ); + if( !g_doWarmup.integer || level.warmupTime <= level.time ) + { + G_CalculateBuildPoints( ); + G_CalculateStages( ); + G_SpawnClients( TEAM_ALIENS ); + G_SpawnClients( TEAM_HUMANS ); + G_CalculateAvgPlayers( ); + G_UpdateZaps( msec ); + } + G_UpdateBuildableRangeMarkers( ); // see if it is time to end the level CheckExitRules( ); @@ -3008,23 +2457,8 @@ void G_RunFrame( int levelTime ) CheckTeamStatus( ); // cancel vote if timed out - CheckVote( ); - - // check team votes - CheckTeamVote( PTE_HUMANS ); - CheckTeamVote( PTE_ALIENS ); + for( i = 0; i < NUM_TEAMS; i++ ) + G_CheckVote( i ); - G_admin_schachtmeisterFrame(); - - // for tracking changes - CheckCvars( ); - - if( g_listEntity.integer ) - { - for( i = 0; i < MAX_GENTITIES; i++ ) - G_Printf( "%4i: %s\n", i, g_entities[ i ].classname ); - - trap_Cvar_Set( "g_listEntity", "0" ); - } + level.frameMsec = trap_Milliseconds(); } - |