diff --git a/src/game/g_admin.c b/src/game/g_admin.c index e47f65c..286df38 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -111,7 +111,7 @@ g_admin_cmd_t g_admin_cmds[ ] = {"drop", G_admin_drop, "o", "kick a client from the server without log", - "[^3name|slot#^7]" + "[^3name|slot#^7] [^3message^7]" }, {"drug", G_admin_drug, "Q", @@ -242,7 +242,7 @@ g_admin_cmd_t g_admin_cmds[ ] = {"register", G_admin_register, "R", "Registers your name to protect it from being used by others or updates your admin name to your current name.", - "" + "[^3level^7] [^3password^7]" }, {"rename", G_admin_rename, "N", @@ -3488,17 +3488,59 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg ) qboolean G_admin_register(gentity_t *ent, int skiparg ){ int level = 0; + int max = 0; + char buffer [ 64 ]; - level = G_admin_level(ent); - if( !ent ) { ADMP( "!register: sorry, but console isn't allowed to register.\n"); return qfalse; } - - if( level == 0 ) - level = 1; + + level = G_admin_level(ent); + + if( G_SayArgc() > 1 + skiparg ) + { + G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) ); + max = atoi( buffer ); + + if( G_SayArgc() > 2 + skiparg ) + { + G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); + if( g_adminRegisterAdminPass.string[ 0 ] && Q_stricmp( g_adminRegisterAdminPass.string, "none" ) && + Q_stricmp( g_adminRegisterAdminPass.string, buffer) ) + { + ADMP( "Invalid password.\n" ); + return qfalse; + } + if (max > g_adminRegisterAdminLevel.integer) + max = g_adminRegisterAdminLevel.integer; + if( level <= g_adminRegisterAdminLevel.integer ) + level = max; + } + else + { + if( max > g_adminRegisterLevel.integer ) + max = g_adminRegisterLevel.integer; + + if( level > 0 && + level <= g_adminRegisterLevel.integer ) + { + level = max; + } + } + + if( max < -1 ) + max = -1; + } + else + { + if( max == 0 ) + max = 1; + } + + if( level < max ) + level = max; if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ) { @@ -5166,12 +5208,13 @@ qboolean G_admin_drop( gentity_t *ent, int skiparg ) int pids[ MAX_CLIENTS ]; char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; int minargc; + char msg[ MAX_STRING_CHARS ]; minargc = 2 + skiparg; if( G_SayArgc() < minargc ) { - ADMP( "^3!drop: ^7usage: !drop [name|slot#]\n" ); + ADMP( "^3!drop: ^7usage: !drop [name|slot#] [message]\n" ); return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); @@ -5189,11 +5232,22 @@ qboolean G_admin_drop( gentity_t *ent, int skiparg ) " level than you\n" ); return qfalse; } + + minargc = 3 + skiparg; + G_SayArgv( 2 + skiparg, msg, sizeof( msg ) ); //what they get -trap_SendServerCommand( pids[ 0 ], va( "disconnect" ) ); +if( G_SayArgc() < minargc ) + { + trap_SendServerCommand( pids[ 0 ], va( "disconnected" ) ); + } +else + { + trap_SendServerCommand( pids[ 0 ], va( "disconnected \"Disconnected.\n\n%s^7\n\"", msg ) ); + } + //what people get -trap_DropClient( pids[ 0 ], va( "disconnect" ) ); +trap_DropClient( pids[ 0 ], va( "disconnected" ) ); diff --git a/src/game/g_client.c b/src/game/g_client.c index be63923..6cdc9fb 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -79,6 +79,97 @@ void SP_info_human_intermission( gentity_t *ent ) { } +/* +=============== +G_OverflowCredits +=============== +*/ +void G_OverflowCredits( gclient_t *doner, int credits ) +{ + int i; + int maxCredits; + int clientNum; + + if( !g_creditOverflow.integer ) + return; + + if( doner->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + { + maxCredits = ALIEN_MAX_KILLS; + clientNum = level.lastCreditedAlien; + } + else if( doner->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + { + maxCredits = HUMAN_MAX_CREDITS; + clientNum = level.lastCreditedHuman; + } + else + { + return; + } + + if( g_creditOverflow.integer == 1 ) + { + // distribute to everyone on team + gentity_t *vic; + + i = 0; + while( credits > 0 && i < level.maxclients ) + { + i++; + clientNum++; + if( clientNum >= level.maxclients ) + clientNum = 0; + + vic = &g_entities[ clientNum ]; + if( vic->client->ps.stats[ STAT_PTEAM ] != doner->ps.stats[ STAT_PTEAM ] || + vic->client->ps.persistant[ PERS_CREDIT ] >= maxCredits ) + continue; + + if( vic->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + level.lastCreditedAlien = clientNum; + else + level.lastCreditedHuman = clientNum; + + if( vic->client->ps.persistant[ PERS_CREDIT ] + credits > maxCredits ) + { + credits -= maxCredits - vic->client->ps.persistant[ PERS_CREDIT ]; + vic->client->ps.persistant[ PERS_CREDIT ] = maxCredits; + } + else + { + vic->client->ps.persistant[ PERS_CREDIT ] += credits; + return; + } + } + } + else if( g_creditOverflow.integer == 2 ) + { + // distribute by team rank + gclient_t *cl; + + for( i = 0; i < level.numPlayingClients && credits > 0; i++ ) + { + // get the client list sorted by rank + cl = &level.clients[ level.sortedClients[ i ] ]; + if( cl->ps.stats[ STAT_PTEAM ] != doner->ps.stats[ STAT_PTEAM ] || + cl->ps.persistant[ PERS_CREDIT ] >= maxCredits ) + continue; + + if( cl->ps.persistant[ PERS_CREDIT ] + credits > maxCredits ) + { + credits -= maxCredits - cl->ps.persistant[ PERS_CREDIT ]; + cl->ps.persistant[ PERS_CREDIT ] = maxCredits; + } + else + { + cl->ps.persistant[ PERS_CREDIT ] += credits; + return; + } + } + } +} + /* =============== G_AddCreditToClient @@ -96,13 +187,19 @@ void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap ) { if( client->ps.persistant[ PERS_CREDIT ] >= ALIEN_MAX_KILLS && credit > 0 ) + { + G_OverflowCredits( client, credit ); return; + } } else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { if( client->ps.persistant[ PERS_CREDIT ] >= HUMAN_MAX_CREDITS && credit > 0 ) + { + G_OverflowCredits( client, credit ); return; + } } } @@ -113,12 +210,18 @@ void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap ) if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { if( client->ps.persistant[ PERS_CREDIT ] > ALIEN_MAX_KILLS ) + { + G_OverflowCredits( client, client->ps.persistant[ PERS_CREDIT ] - ALIEN_MAX_KILLS ); client->ps.persistant[ PERS_CREDIT ] = ALIEN_MAX_KILLS; + } } else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { if( client->ps.persistant[ PERS_CREDIT ] > HUMAN_MAX_CREDITS ) + { + G_OverflowCredits( client, client->ps.persistant[ PERS_CREDIT ] - HUMAN_MAX_CREDITS ); client->ps.persistant[ PERS_CREDIT ] = HUMAN_MAX_CREDITS; + } } } diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 074c676..1041b42 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1711,15 +1711,20 @@ void Cmd_CallVote_f( gentity_t *ent ) ent->client->pers.voteCount++; - // start the voting, the caller autoamtically votes yes + // start the voting level.voteTime = level.time; - level.voteYes = 1; + level.voteYes = 0; level.voteNo = 0; for( i = 0 ; i < level.maxclients ; i++ ) level.clients[i].ps.eFlags &= ~EF_VOTED; - ent->client->ps.eFlags |= EF_VOTED; + // the caller always votes yes, except for polls + if( Q_stricmp( arg1, "poll" ) ) + { + level.voteYes++; + ent->client->ps.eFlags |= EF_VOTED; + } trap_SetConfigstring( CS_VOTE_TIME, va( "%i", level.voteTime ) ); trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString ); @@ -1861,6 +1866,8 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) trap_SendServerCommand( ent-g_entities, "print \"Invalid team vote string\n\"" ); return; } + + level.teamVotePercentToPass[ cs_offset ] = 50; // detect clientNum for partial name match votes if( !Q_stricmp( arg1, "kick" ) || @@ -2034,6 +2041,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) } else if( !Q_stricmp( arg1, "admitdefeat" ) ) { + if( !g_admitDefeatVotePercent.integer ) + { + trap_SendServerCommand( ent-g_entities, "print \"admitdefeat votes are disabled\n\"" ); + return; + } + level.teamVotePercentToPass[ cs_offset ] = g_admitDefeatVotePercent.integer; Com_sprintf( level.teamVoteString[ cs_offset ], sizeof( level.teamVoteString[ cs_offset ] ), "admitdefeat %i", team ); Com_sprintf( level.teamVoteDisplayString[ cs_offset ], @@ -2056,6 +2069,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) } ent->client->pers.voteCount++; + if( level.teamVotePercentToPass[ cs_offset ] != 50 ) + { + Q_strcat( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ), + va( " (Needs %d percent)", level.teamVotePercentToPass[ cs_offset ] ) ); + } + for( i = 0 ; i < level.maxclients ; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) @@ -2066,9 +2085,9 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) "called a team vote: %s \n\"", ent->client->pers.netname, level.teamVoteDisplayString[ cs_offset ] ) ); } - // start the voting, the caller autoamtically votes yes + // start the voting level.teamVoteTime[ cs_offset ] = level.time; - level.teamVoteYes[ cs_offset ] = 1; + level.teamVoteYes[ cs_offset ] = 0; level.teamVoteNo[ cs_offset ] = 0; for( i = 0 ; i < level.maxclients ; i++ ) @@ -2077,7 +2096,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) level.clients[ i ].ps.eFlags &= ~EF_TEAMVOTED; } - ent->client->ps.eFlags |= EF_TEAMVOTED; + // the caller always votes yes, except for polls + if( Q_stricmp( arg1, "poll" ) ) + { + level.teamVoteYes[ cs_offset ]++; + ent->client->ps.eFlags |= EF_TEAMVOTED; + } trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va( "%i", level.teamVoteTime[ cs_offset ] ) ); trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteDisplayString[ cs_offset ] ); diff --git a/src/game/g_local.h b/src/game/g_local.h index 3bfa9cc..076964a 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -640,6 +640,7 @@ typedef struct // team voting state char teamVoteString[ 2 ][ MAX_STRING_CHARS ]; char teamVoteDisplayString[ 2 ][ MAX_STRING_CHARS ]; + int teamVotePercentToPass[ 2 ]; int teamVoteTime[ 2 ]; // level.time vote was called int teamVoteYes[ 2 ]; int teamVoteNo[ 2 ]; @@ -727,6 +728,9 @@ typedef struct int pause_ff; int pause_ffb; + int lastCreditedAlien; + int lastCreditedHuman; + int unlaggedIndex; int unlaggedTimes[ MAX_UNLAGGED_MARKERS ]; @@ -1250,6 +1254,7 @@ extern vmCvar_t g_extendVotesTime; extern vmCvar_t g_extendVotesCount; extern vmCvar_t g_mapVotesPercent; extern vmCvar_t g_designateVotes; +extern vmCvar_t g_admitDefeatVotePercent; extern vmCvar_t g_teamAutoJoin; extern vmCvar_t g_teamForceBalance; extern vmCvar_t g_banIPs; @@ -1260,6 +1265,7 @@ extern vmCvar_t pmove_fixed; extern vmCvar_t pmove_msec; extern vmCvar_t g_rankings; extern vmCvar_t g_allowShare; +extern vmCvar_t g_creditOverflow; extern vmCvar_t g_enableDust; extern vmCvar_t g_enableBreath; extern vmCvar_t g_singlePlayer; @@ -1309,6 +1315,9 @@ extern vmCvar_t g_adminSayFilter; extern vmCvar_t g_adminNameProtect; extern vmCvar_t g_adminTempBan; extern vmCvar_t g_adminMapLog; +extern vmCvar_t g_adminRegisterLevel; +extern vmCvar_t g_adminRegisterAdminPass; +extern vmCvar_t g_adminRegisterAdminLevel; extern vmCvar_t g_minLevelToJoinTeam; extern vmCvar_t g_minLevelToSpecMM1; diff --git a/src/game/g_main.c b/src/game/g_main.c index 59d52aa..d70a215 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -88,6 +88,7 @@ vmCvar_t g_extendVotesTime; vmCvar_t g_extendVotesCount; vmCvar_t g_mapVotesPercent; vmCvar_t g_designateVotes; +vmCvar_t g_admitDefeatVotePercent; vmCvar_t g_teamAutoJoin; vmCvar_t g_teamForceBalance; vmCvar_t g_banIPs; @@ -149,6 +150,9 @@ vmCvar_t g_adminSayFilter; vmCvar_t g_adminNameProtect; vmCvar_t g_adminTempBan; vmCvar_t g_adminMapLog; +vmCvar_t g_adminRegisterLevel; +vmCvar_t g_adminRegisterAdminPass; +vmCvar_t g_adminRegisterAdminLevel; vmCvar_t g_minLevelToJoinTeam; vmCvar_t g_privateMessages; @@ -173,6 +177,7 @@ vmCvar_t g_allowActions; vmCvar_t g_actionPrefix; vmCvar_t g_allowShare; +vmCvar_t g_creditOverflow; vmCvar_t g_devmapNoGod; vmCvar_t g_devmapNoStructDmg; @@ -270,6 +275,7 @@ static cvarTable_t gameCvarTable[ ] = { &g_extendVotesCount, "g_extendVotesCount", "3", CVAR_ARCHIVE, 0, qfalse }, { &g_mapVotesPercent, "g_mapVotesPercent", "50", CVAR_ARCHIVE, 0, qfalse }, { &g_designateVotes, "g_designateVotes", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_admitDefeatVotePercent, "g_admitDefeatVotePercent", "50", CVAR_ARCHIVE, 0, qfalse }, { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse }, { &g_minCommandPeriod, "g_minCommandPeriod", "500", 0, 0, qfalse}, @@ -330,6 +336,9 @@ static cvarTable_t gameCvarTable[ ] = { &g_adminTempBan, "g_adminTempBan", "120", CVAR_ARCHIVE, 0, qfalse }, { &g_minLevelToJoinTeam, "g_minLevelToJoinTeam", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_adminMapLog, "g_adminMapLog", "", CVAR_ROM, 0, qfalse }, + { &g_adminRegisterLevel, "g_adminRegisterLevel", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_adminRegisterAdminPass, "g_adminRegisterAdminPass", "", CVAR_ARCHIVE, 0, qfalse }, + { &g_adminRegisterAdminLevel, "g_adminRegisterAdminLevel", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_radiationDamage, "g_radiationDamage", "1", CVAR_ARCHIVE, 0, qfalse }, @@ -357,6 +366,7 @@ static cvarTable_t gameCvarTable[ ] = { &g_rankings, "g_rankings", "0", 0, 0, qfalse }, { &g_allowShare, "g_allowShare", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse}, + { &g_creditOverflow, "g_creditOverflow", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse}, { &g_allowActions, "g_allowActions", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_actionPrefix, "g_actionPrefix", "***", CVAR_ARCHIVE, 0, qfalse }, { &g_banNotice, "g_banNotice", "", CVAR_ARCHIVE, 0, qtrue }, @@ -2187,7 +2197,8 @@ CheckVote void CheckVote( void ) { int votePercentToPass=level.votePercentToPass; - int voteYesPercent; + int voteYesPercent = 0; + char *result = ""; if( level.voteExecuteTime && level.voteExecuteTime < level.time ) { @@ -2213,19 +2224,21 @@ void CheckVote( void ) if( !level.voteTime ) return; - voteYesPercent = (int)(100* (level.voteYes)/(level.voteYes + level.voteNo)); + if( level.voteYes || level.voteNo ) + { + voteYesPercent = (int)(100* (level.voteYes)/(level.voteYes + level.voteNo)); + } if( level.time - level.voteTime >= VOTE_TIME || ( level.voteYes + level.voteNo == level.numConnectedClients ) ) { if( voteYesPercent> votePercentToPass || level.voteNo == 0 ) { // execute the command, then remove the vote - trap_SendServerCommand(-1, va("print \"Vote ^2passed^7 (^2Y:%d^7-^1N:%d^7) [%s]\n\"", level.voteYes, level.voteNo, level.voteDisplayString )); + result = "^2passed"; level.voteExecuteTime = level.time + 3000; } else { - // same behavior as a timeout - trap_SendServerCommand(-1, va("print \"Vote ^1failed^7 (^4Y:%d^7-^1N:%d^7) [%s]\n\"", level.voteYes, level.voteNo, level.voteDisplayString )); + result = "^1failed"; } } else @@ -2233,13 +2246,13 @@ void CheckVote( void ) if( level.voteYes > (int)((double)level.numConnectedClients * ((double)votePercentToPass/100.0)) ) { // execute the command, then remove the vote - trap_SendServerCommand(-1, va("print \"Vote ^2passed^7 (^2Y:%d^7-^1N:%d^7) [%s]\n\"", level.voteYes, level.voteNo, level.voteDisplayString )); + result = "^2passed"; level.voteExecuteTime = level.time + 3000; } else if( level.voteNo > (int)((double)level.numConnectedClients * ((double)(100.0-votePercentToPass)/100.0)) ) { // same behavior as a timeout - trap_SendServerCommand(-1, va("print \"Vote ^1failed^7 (^2Y:%d^7-^1N:%d^7) [%s]\n\"", level.voteYes, level.voteNo, level.voteDisplayString )); + result = "^1failed"; } else { @@ -2248,6 +2261,9 @@ void CheckVote( void ) } } + trap_SendServerCommand(-1, va("print \"Vote %s^7 (^2Y:%d^7-^1N:%d^7) [%s]\n\"", + result, level.voteYes, level.voteNo, level.voteDisplayString ) ); + level.voteTime = 0; trap_SetConfigstring( CS_VOTE_TIME, "" ); } @@ -2261,6 +2277,9 @@ CheckTeamVote void CheckTeamVote( int team ) { int cs_offset; + int votePercentToPass; + int voteYesPercent = 0; + char *result = ""; if ( team == PTE_HUMANS ) cs_offset = 0; @@ -2272,32 +2291,39 @@ void CheckTeamVote( int team ) if( !level.teamVoteTime[ cs_offset ] ) return; - if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME ) + votePercentToPass = level.teamVotePercentToPass[ cs_offset ]; + if( level.teamVoteYes[ cs_offset ] || level.teamVoteNo[ cs_offset ] ) { - if( level.teamVoteYes[ cs_offset ] > level.teamVoteNo[ cs_offset ] && level.teamVoteYes[ cs_offset ] >= 2 ) + voteYesPercent = (int)(100* (level.teamVoteYes[ cs_offset ])/(level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ])); + } + + if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME || + level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ] == level.numteamVotingClients[ cs_offset ] ) + { + if( voteYesPercent > votePercentToPass && + level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 ) { // execute the command, then remove the vote - trap_SendServerCommand( -1, "print \"Team vote passed\n\"" ); + result = "^2passed"; trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); } else - { - trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + { + result = "^1failed"; } } else { - if( level.teamVoteYes[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 ) + if( level.teamVoteYes[ cs_offset ] > (int)((double)level.numteamVotingClients[ cs_offset ] * ((double)votePercentToPass/100.0)) ) { // execute the command, then remove the vote - trap_SendServerCommand( -1, "print \"Team vote passed\n\"" ); - // + result = "^2passed"; trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); } - else if( level.teamVoteNo[ cs_offset ] >= level.numteamVotingClients[ cs_offset ] / 2 ) + else if( level.teamVoteNo[ cs_offset ] > (int)((double)level.numteamVotingClients[ cs_offset ] * ((double)(100.0-votePercentToPass)/100.0)) ) { // same behavior as a timeout - trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + result = "^1Failed"; } else { @@ -2306,6 +2332,9 @@ void CheckTeamVote( int team ) } } + trap_SendServerCommand( -1, va( "print \"Team vote %s^7 (^2Y:%d^7-^1N:%d^7)\n\"", + result, level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) ); + level.teamVoteTime[ cs_offset ] = 0; trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" ); } diff --git a/src/game/g_utils.c b/src/game/g_utils.c index 2206341..96055f3 100644 --- a/src/game/g_utils.c +++ b/src/game/g_utils.c @@ -806,7 +806,7 @@ gentity_t *G_ClosestEnt( vec3_t origin, gentity_t **entities, int numEntities ) { gentity_t *ent = entities[ i ]; - if( ( nd = Distance( origin, ent->s.origin ) ) < d ) + if( ( nd = Distance( origin, ent->s.origin ) ) < d || !closestEnt ) { d = nd; closestEnt = ent;