diff options
| -rw-r--r-- | src/game/bg_lib.c | 8 | ||||
| -rw-r--r-- | src/game/bg_misc.c | 134 | ||||
| -rw-r--r-- | src/game/bg_pmove.c | 10 | ||||
| -rw-r--r-- | src/game/bg_public.h | 21 | ||||
| -rw-r--r-- | src/game/g_active.c | 201 | ||||
| -rw-r--r-- | src/game/g_admin.c | 2907 | ||||
| -rw-r--r-- | src/game/g_admin.h | 60 | ||||
| -rw-r--r-- | src/game/g_buildable.c | 1038 | ||||
| -rw-r--r-- | src/game/g_client.c | 144 | ||||
| -rw-r--r-- | src/game/g_cmds.c | 1040 | ||||
| -rw-r--r-- | src/game/g_combat.c | 238 | ||||
| -rw-r--r-- | src/game/g_local.h | 156 | ||||
| -rw-r--r-- | src/game/g_main.c | 708 | ||||
| -rw-r--r-- | src/game/g_maprotation.c | 171 | ||||
| -rw-r--r-- | src/game/g_mem.c | 2 | ||||
| -rw-r--r-- | src/game/g_missile.c | 31 | ||||
| -rw-r--r-- | src/game/g_session.c | 2 | ||||
| -rw-r--r-- | src/game/g_weapon.c | 17 | ||||
| -rw-r--r-- | src/game/tremulous.h | 3 | 
19 files changed, 4958 insertions, 1933 deletions
diff --git a/src/game/bg_lib.c b/src/game/bg_lib.c index 69bca48..b9c8762 100644 --- a/src/game/bg_lib.c +++ b/src/game/bg_lib.c @@ -67,7 +67,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";  #endif  static const char rcsid[] = -  "$Id: bg_lib.c 965 2007-08-09 13:54:12Z msk $"; +  "$Id: bg_lib.c,v 1.1.1.1.4.1 2009/02/17 02:38:26 johne Exp $";  #endif /* LIBC_SCCS and not lint */  // bk001127 - needed for DLL's @@ -1663,7 +1663,6 @@ unsigned int _hextoi( const char **stringPtr )  #define SHORTINT  0x00000040    /* short integer */  #define ZEROPAD   0x00000080    /* zero (as opposed to blank) pad */  #define FPT       0x00000100    /* floating point number */ -#define UNSIGNED  0x00000200    /* unsigned integer */      #define to_digit(c)   ((c) - '0')  #define is_digit(c)   ((unsigned)to_digit(c) <= 9) @@ -1677,9 +1676,6 @@ void AddInt( char **buf_p, int val, int width, int flags )    digits = 0; -  if( flags & UNSIGNED ) -    val = (unsigned) val; -    if( flags & HEX )    {      char c; @@ -1944,8 +1940,6 @@ reswitch:          arg++;          break; -      case 'u': -        flags |= UNSIGNED;        case 'd':        case 'i':          AddInt( &buf_p, *arg, width, flags ); diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index 79a8e2a..1a55ed0 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -32,6 +32,8 @@ void trap_FS_Write( const void *buffer, int len, fileHandle_t f );  void trap_FS_FCloseFile( fileHandle_t f );  void trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ); // fsOrigin_t +modExtremeType_t modEntry[ MOD_BG_COUNT ]; +  buildableAttributes_t bg_buildableList[ ] =  {    { @@ -5730,4 +5732,136 @@ void BG_ClientListParse( clientList_t *list, const char *s )    sscanf( s, "%x%x", &list->hi, &list->lo );  } +void BG_MOD_set( modExtremeType_t entry, int value ) +{ +  if( entry >= 0 && entry < MOD_BG_COUNT ) +  { +    if( value < 0 ) +      value = 0; + +    modEntry[ entry ] = value; +  } +} + +int BG_MOD_get( modExtremeType_t entry) +{ +  if( entry >= 0 && entry < MOD_BG_COUNT ) +    return modEntry[ entry ]; + +  return 0; +} + +void BG_MOD_update( void ) +{ +  static qboolean updated = qfalse; +  int i; + +  if( updated ) return; +  updated = qtrue; + +  for( i = 0; i < bg_numBuildables; i++ ) +  { +    if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] ) +    { +      bg_buildableList[ i ].health = +        bg_buildableList[ i ].health * modEntry[ MOD_BG_BUILDABLE_HEALTH ] / 100; +    } + +    if( modEntry[ MOD_BG_BUILDABLE_SPEED ] ) +    { +      bg_buildableList[ i ].turretFireSpeed = +        bg_buildableList[ i ].turretFireSpeed * 100 / modEntry[ MOD_BG_BUILDABLE_SPEED ]; +    } + +    if( modEntry[ MOD_BG_TURRET_ANGLE ] && +      ( bg_buildableList[ i ].buildNum == BA_H_MGTURRET || +        bg_buildableList[ i ].buildNum == BA_H_TESLAGEN ) ) +    { +      bg_buildableList[ i ].minNormal = 0.0f; +    } +  } + +  for( i = 0; i < bg_numPclasses; i++ ) +  { +    if( bg_classList[ i ].classNum == PCL_HUMAN || +        bg_classList[ i ].classNum == PCL_HUMAN_BSUIT ) +    { +      if( modEntry[ MOD_BG_HUMAN_HEALTH ] ) +      { +        bg_classList[ i ].health = +          bg_classList[ i ].health * modEntry[ MOD_BG_HUMAN_HEALTH ] / 100; +      } +    } +    else +    { +      if( modEntry[ MOD_BG_ALIEN_HEALTH ] ) +        bg_classList[ i ].health = +          bg_classList[ i ].health * modEntry[ MOD_BG_ALIEN_HEALTH ] / 100; +    } +  } + +  for( i = 0; i < bg_numWeapons; i++ ) +  { +    if( modEntry[ MOD_BG_WEAPON_AMMO ] && +        bg_weapons[ i ].weaponNum != WP_ALEVEL3_UPG ) +    { +      bg_weapons[ i ].maxAmmo = +        bg_weapons[ i ].maxAmmo * modEntry[ MOD_BG_WEAPON_AMMO ] / 100; +    } + +    if( modEntry[ MOD_BG_ALIEN_RATE ] && +        bg_weapons[ i ].weaponNum >= WP_ALEVEL0 && +        bg_weapons[ i ].weaponNum <= WP_ALEVEL4 ) +    { +      bg_weapons[ i ].repeatRate1 = +        bg_weapons[ i ].repeatRate1 * 100 / modEntry[ MOD_BG_ALIEN_RATE ]; +      bg_weapons[ i ].repeatRate2 = +        bg_weapons[ i ].repeatRate2 * 100 / modEntry[ MOD_BG_ALIEN_RATE ]; +      bg_weapons[ i ].repeatRate3 = +        bg_weapons[ i ].repeatRate3 * 100 / modEntry[ MOD_BG_ALIEN_RATE ]; +    } + +    if( modEntry[ MOD_BG_HUMAN_RATE ] && +        bg_weapons[ i ].weaponNum >= WP_BLASTER && +        bg_weapons[ i ].weaponNum <= WP_LUCIFER_CANNON ) +    { +      bg_weapons[ i ].repeatRate1 = +        bg_weapons[ i ].repeatRate1 * 100 / modEntry[ MOD_BG_HUMAN_RATE ]; +      bg_weapons[ i ].repeatRate2 = +        bg_weapons[ i ].repeatRate2 * 100 / modEntry[ MOD_BG_HUMAN_RATE ]; +      bg_weapons[ i ].repeatRate3 = +        bg_weapons[ i ].repeatRate3 * 100 / modEntry[ MOD_BG_HUMAN_RATE ]; +    } + +    if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] && +        modEntry[ MOD_BG_HUMAN_RATE ] && +      ( bg_weapons[ i ].weaponNum == WP_ABUILD || bg_weapons[ i ].weaponNum == WP_ABUILD2 ) ) +    { +      int avg; + +      avg = ( modEntry[ MOD_BG_BUILDABLE_HEALTH ] + MOD_BG_HUMAN_RATE ) / 2; +      bg_weapons[ i ].repeatRate1 = bg_weapons[ i ].repeatRate1 * 100 / avg; +      bg_weapons[ i ].repeatRate2 = bg_weapons[ i ].repeatRate2 * 100 / avg; +      bg_weapons[ i ].repeatRate3 = bg_weapons[ i ].repeatRate3 * 100 / avg; +    } + +    if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] && +        modEntry[ MOD_BG_ALIEN_RATE ] && +      ( bg_weapons[ i ].weaponNum == WP_HBUILD || bg_weapons[ i ].weaponNum == WP_HBUILD2 ) ) +    { +      int avg; + +      avg = ( modEntry[ MOD_BG_BUILDABLE_HEALTH ] + MOD_BG_ALIEN_RATE ) / 2; +      bg_weapons[ i ].repeatRate1 = bg_weapons[ i ].repeatRate1 * 100 / avg; +      bg_weapons[ i ].repeatRate2 = bg_weapons[ i ].repeatRate2 * 100 / avg; +      bg_weapons[ i ].repeatRate3 = bg_weapons[ i ].repeatRate3 * 100 / avg; +    } + +    if( modEntry[ MOD_BG_WEAPON_RELOAD ] ) +    { +      bg_weapons[ i ].reloadTime = +        bg_weapons[ i ].reloadTime * 100 / modEntry[ MOD_BG_WEAPON_RELOAD ]; +    } +  } +} diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c index b0e5c96..88707bd 100644 --- a/src/game/bg_pmove.c +++ b/src/game/bg_pmove.c @@ -714,7 +714,15 @@ static qboolean PM_CheckJump( void )    //TA: take some stamina off    if( pm->ps->stats[ STAT_PTEAM ] == PTE_HUMANS ) -    pm->ps->stats[ STAT_STAMINA ] -= 500; +  { +    int rate; + +    rate = BG_MOD_get( MOD_BG_HUMAN_STAMINA ); +    if( rate ) +      pm->ps->stats[ STAT_STAMINA ] -= 500 * 100 / rate; +    else +      pm->ps->stats[ STAT_STAMINA ] -= 500; +  }    pm->ps->groundEntityNum = ENTITYNUM_NONE; diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 6642d91..e0e6233 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -1330,3 +1330,24 @@ void BG_ClientListParse( clientList_t *list, const char *s );  #define FFF_ALIENS         2  #define FFF_BUILDABLES     4 +// value mods equal to g_mod cvars on server +typedef enum +{ +  MOD_BG_BUILDABLE_HEALTH,  // Buildable health +  MOD_BG_BUILDABLE_SPEED,   // Buildable fire rate +  MOD_BG_HUMAN_STAMINA,     // Human stamina +  MOD_BG_HUMAN_HEALTH,      // Human health +  MOD_BG_ALIEN_HEALTH,      // Alien health +  MOD_BG_HUMAN_RATE,        // Human fire rate +  MOD_BG_ALIEN_RATE,        // Alien fire rate +  MOD_BG_WEAPON_AMMO,       // Weapon ammo per clip +  MOD_BG_WEAPON_RELOAD,     // Weapon reload time +  MOD_BG_TURRET_ANGLE,      // Allow high turret build angles +  MOD_BG_STAGE3_STRENGTH,   // alter stage 3 buildables +  MOD_BG_COUNT              // number of entries +} modExtremeType_t; + +void BG_MOD_set( modExtremeType_t entry, int value ); +int  BG_MOD_get( modExtremeType_t entry); +void BG_MOD_update( void ); + diff --git a/src/game/g_active.c b/src/game/g_active.c index 57f58fe..2c18f00 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -428,7 +428,7 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )       if( ( client->pers.teamSelection == PTE_ALIENS &&             G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) ||           ( client->pers.teamSelection == PTE_HUMANS && -           G_SearchSpawnQueue( &level.humanSpawnQueue, ent-g_entities ) ) ) +           G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) )       {         client->ps.pm_flags |= PMF_QUEUED;       } @@ -521,8 +521,12 @@ ClientInactivityTimer  Returns qfalse if the client is dropped  =================  */ -qboolean ClientInactivityTimer( gclient_t *client ) +qboolean ClientInactivityTimer( gentity_t *ent )  { +  gclient_t *client; + +  client = ent->client; +    if( ! g_inactivity.integer )    {      // give everyone some time, so if the operator sets g_inactivity during @@ -542,6 +546,22 @@ qboolean ClientInactivityTimer( gclient_t *client )    {      if( level.time > client->inactivityTime )      { +      if( g_inactivityMode.integer == 1 ) +      { +        char buf[ MAX_STRING_CHARS ]; + +        Com_sprintf( buf, sizeof( buf ), +          "%s^7 moved from %s to spectators due to inactivity\n", +          client->pers.netname, +          ( client->pers.teamSelection == PTE_ALIENS ) ? "aliens" : "humans" ); +        trap_SendServerCommand( -1, va( "print \"%s\"", buf ) ); +        G_LogOnlyPrintf( "Inactivity: %s", buf ); + +        G_ChangeTeam( ent, PTE_NONE ); +        client->inactivityTime = level.time + g_inactivity.integer * 1000; +        client->inactivityWarning = qfalse; +        return qtrue; +      }        trap_DropClient( client - level.clients, "Dropped due to inactivity" );        return qfalse;      } @@ -549,13 +569,77 @@ qboolean ClientInactivityTimer( gclient_t *client )      if( level.time > client->inactivityTime - 10000 && !client->inactivityWarning )      {        client->inactivityWarning = qtrue; -      trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); +      if( g_inactivityMode.integer == 1 ) +        trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity spectate!\n\"" ); +      else +        trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );      }    }    return qtrue;  } +static void ClientSpreeDecay( gentity_t *ent, int msec ) +{ +  gclient_t *client; + +  client = ent->client; +  client->pers.spreeTime1000 += msec; + +  while( client->pers.spreeTime1000 >= 1000 ) +  { +    client->pers.spreeTime1000 -= 1000; + +    // spree decay +    if( ent->client->pers.statscounters.spreefeeds ) +    { +      ent->client->pers.statscounters.spreefeeds -= SPREE_FEED_FADE; +      if( ent->client->pers.statscounters.spreefeeds < 0 ) +        ent->client->pers.statscounters.spreefeeds = 0; +    } +    if( ent->client->pers.statscounters.spreekills > 1 ) +    { +      ent->client->pers.statscounters.spreekills -= 2; +    } +    if( ent->client->pers.statscounters.spreebleeds || +        ent->client->pers.bleeder ) +    { +      if( ent->client->pers.bleeder ) +        ent->client->pers.statscounters.spreebleeds -= 10; +      else +        ent->client->pers.statscounters.spreebleeds -= 4; +      if( ent->client->pers.statscounters.spreebleeds < 0 ) +        ent->client->pers.statscounters.spreebleeds = 0; +      if( ent->client->pers.bleeder ) +      { +        if( !ent->client->pers.statscounters.spreebleeds ) +        { +          ent->client->pers.bleeder = qfalse; +          trap_SendServerCommand( -1, +            va( "print \"%s^7 has been ^2forgiven ^7by their base\n\"", ent->client->pers.netname ) ); +          if( client->sess.sessionTeam != TEAM_SPECTATOR || +              client->sess.spectatorState != SPECTATOR_SCOREBOARD ) +            trap_SendServerCommand( ent-g_entities, "cp \"^2Your base has forgiven you\"" ); +          if( level.bleeders ) +            level.bleeders--; +        } +        else if( client->sess.sessionTeam != TEAM_SPECTATOR && +                 ent->client->ps.stats[ STAT_HEALTH ] <= 0 ) +        { +          int spreeLength; + +          spreeLength = ent->client->pers.statscounters.spreebleeds / 10; +          trap_SendServerCommand( ent-g_entities, +            va( "cp \"^3Bleeding made you an enemy of your own base!\n\n^7for %d:%02d\n\n^1Respawn delayed %d seconds\"", +              spreeLength / 60, spreeLength % 60, +              ( ent->client->respawnTime - level.time) / 1000 ) ); +        } +      } +    } +    +  } +} +  /*  ==================  ClientTimerActions @@ -568,9 +652,9 @@ void ClientTimerActions( gentity_t *ent, int msec )    gclient_t *client;    usercmd_t *ucmd;    int       aForward, aRight; -  qboolean  walking = qfalse, stopped = qfalse, -            crouched = qfalse, jumping = qfalse, -            strafing = qfalse; +  int staminaLarmour = STAMINA_LARMOUR_TAKE; +  int staminaSprint = STAMINA_SPRINT_TAKE; +  int timestep10000;    ucmd = &ent->client->pers.cmd; @@ -582,43 +666,38 @@ void ClientTimerActions( gentity_t *ent, int msec )    client->time1000 += msec;    client->time10000 += msec; -  if( aForward == 0 && aRight == 0 ) -    stopped = qtrue; -  else if( aForward <= 64 && aRight <= 64 ) -    walking = qtrue; - -  if( aRight > 0 ) -    strafing = qtrue; - -  if( ucmd->upmove > 0 ) -    jumping = qtrue; -  else if( ent->client->ps.pm_flags & PMF_DUCKED ) -    crouched = qtrue; +  if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && +    g_modHumanStamina.integer > 0 ) +    { +    staminaLarmour = staminaLarmour * 100 / g_modHumanStamina.integer; +    staminaSprint = staminaSprint * 100 / g_modHumanStamina.integer; +    }    while ( client->time100 >= 100 )    {      client->time100 -= 100;      //if not trying to run then not trying to sprint -    if( walking || stopped ) +    if( aForward <= 64 )        client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;      if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )        client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; -    if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && !crouched ) +    if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && +       !( client->ps.stats[ STAT_STATE ] & PMF_DUCKED ) )      {        //subtract stamina        if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) -        client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE; +        client->ps.stats[ STAT_STAMINA ] -= staminaLarmour;        else -        client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE; +        client->ps.stats[ STAT_STAMINA ] -= staminaSprint;        if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )          client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;      } -    if( walking || crouched ) +    if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) )      {        //restore stamina        client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE; @@ -626,7 +705,7 @@ void ClientTimerActions( gentity_t *ent, int msec )        if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )          client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;      } -    else if( stopped ) +    else if( aForward <= 5 && aRight <= 5 )      {        //restore stamina faster        client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE; @@ -769,6 +848,13 @@ void ClientTimerActions( gentity_t *ent, int msec )      {        int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime ); +      if( level.vampireDeath ) +      { +        ent->client->medKitHealthToRestore = 0; +        trap_SendServerCommand( ent-g_entities, +          "print \"Medkit is disabled during Vampire Sudden Death\n\"" ); +      } +        if( remainingStartupTime < 0 )        {          if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] && @@ -830,7 +916,7 @@ void ClientTimerActions( gentity_t *ent, int msec )      //replenish alien health      if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && -      level.surrenderTeam != PTE_ALIENS ) +      level.surrenderTeam != PTE_ALIENS && !level.vampireDeath )      {        int       entityList[ MAX_GENTITIES ];        vec3_t    range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE }; @@ -866,7 +952,32 @@ void ClientTimerActions( gentity_t *ent, int msec )            !level.paused &&            ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )        { -        ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier; +        float dist_mod = 1.0f; +        float hadd; + +        if( g_modAlienRegenRange.integer ) +        { +          float om; + +          om = G_BuildableRangeClosest( client->ps.origin, CREEP_BASESIZE * 2, BA_A_OVERMIND ); +          if( om > 0.0 ) +          { +            dist_mod = ( ( CREEP_BASESIZE * 2 ) - om ) / ( CREEP_BASESIZE * 2 ) + 0.25; +            if( dist_mod < 0.25 ) +              dist_mod = 0.25; +            if( dist_mod > 1.0 ) +              dist_mod = 1.0; +          } +          else +          { +            dist_mod = 0.10f; +          } +        } + +        hadd = (float)(BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier * dist_mod); +        if( hadd < 1.0 && hadd >= 0.5 ) +          hadd = 1.0; +        ent->health += hadd;          // if completely healed, cancel retribution          if( ent->health >= client->ps.stats[ STAT_MAX_HEALTH ] ) @@ -884,10 +995,12 @@ void ClientTimerActions( gentity_t *ent, int msec )      if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )      {        ent->client->pers.statscounters.timealive++; +      ent->client->pers.karma += 1;        level.alienStatsCounters.timealive++;        if( G_BuildableRange( ent->client->ps.origin, 900, BA_A_OVERMIND ) )        {          ent->client->pers.statscounters.timeinbase++; +        ent->client->pers.karma -= 1;          level.alienStatsCounters.timeinbase++;        }        if( BG_ClassHasAbility( ent->client->ps.stats[ STAT_PCLASS ], SCA_WALLCLIMBER )  ) @@ -904,10 +1017,12 @@ void ClientTimerActions( gentity_t *ent, int msec )      else if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )      {        ent->client->pers.statscounters.timealive++; +      ent->client->pers.karma += 1;        level.humanStatsCounters.timealive++;        if( G_BuildableRange( ent->client->ps.origin, 900, BA_H_REACTOR ) )        {          ent->client->pers.statscounters.timeinbase++; +        ent->client->pers.karma -= 1;          level.humanStatsCounters.timeinbase++;        }        if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) ) @@ -943,7 +1058,7 @@ void ClientTimerActions( gentity_t *ent, int msec )            BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );          } else if( client->jetpackfuel < 10.0f && client->jetpackfuel > 0.0f) {            client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value; -          trap_SendServerCommand( client - level.clients, "cp \"^3Fuel ^1Low!!!!!^7\nLand now.\"" ); +          trap_SendServerCommand( client - level.clients, "cp \"^3Fuel ^1Low!^7\nLand now.\"" );          } else {            client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value;          } @@ -961,9 +1076,23 @@ void ClientTimerActions( gentity_t *ent, int msec )      }    } -  while( client->time10000 >= 10000 ) +  timestep10000 = 10000; +  if( g_modAlienRate.integer > 0 ) +  { +    timestep10000 = timestep10000 * 100 / g_modAlienRate.integer; +    if( timestep10000 < 1000 ) +      timestep10000 = 1000; +  } +  if( g_modBuildableHealth.integer > 0 ) +  { +    timestep10000 = timestep10000 * 100 / g_modBuildableHealth.integer; +    if( timestep10000 < 1000 ) +      timestep10000 = 1000; +  } + +  while( client->time10000 >= timestep10000 )    { -    client->time10000 -= 10000; +    client->time10000 -= timestep10000;      if( client->ps.weapon == WP_ALEVEL3_UPG )      { @@ -1500,6 +1629,9 @@ void ClientThink_real( gentity_t *ent )      return;    } +  //spree decay +  ClientSpreeDecay( ent, msec ); +    if( client->pers.teamSelection != PTE_NONE && client->pers.joinedATeam )      G_UpdatePTRConnection( client ); @@ -1514,7 +1646,7 @@ void ClientThink_real( gentity_t *ent )    }    // check for inactivity timer, but never drop the local client of a non-dedicated server -  if( !ClientInactivityTimer( client ) ) +  if( !ClientInactivityTimer( ent ) )      return;    // calculate where ent is currently seeing all the other active clients  @@ -1567,6 +1699,15 @@ void ClientThink_real( gentity_t *ent )    client->ps.gravity = g_gravity.value; +  if( client->pers.bubbleTime && client->pers.bubbleTime < level.time ) +  { +    gentity_t *bubble; + +    client->pers.bubbleTime = level.time + 500; +    bubble = G_TempEntity( client->ps.origin, EV_PLAYER_TELEPORT_OUT ); +    bubble->s.clientNum = ent->s.clientNum; +  } +    if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) &&        BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) )    { diff --git a/src/game/g_admin.c b/src/game/g_admin.c index f9c5f84..de59a47 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -80,6 +80,16 @@ g_admin_cmd_t g_admin_cmds[ ] =        "[^3name|slot#|IP^7] (^5time^7) (^5reason^7)"      }, +    {"bring", G_admin_bring, "bring", +      "This will bring you to a players location.", +      "[^3name|slot#^7]" +    }, + +    {"bubble", G_admin_bubble, "bubble", +      "draw attention to a player with bubbles", +      "[^3name|slot#^7]" +    }, +      {"buildlog", G_admin_buildlog, "buildlog",        "display a list of recent builds and deconstructs, optionally specifying"        " a team", @@ -108,26 +118,31 @@ g_admin_cmd_t g_admin_cmds[ ] =        "[^3name|slot#^7]"      }, +    {"denyweapon", G_admin_denyweapon, "denyweapon", +      "take away a player's ability to use a weapon or class", +      "[^3name|slot#^7] [^3class|weapon^7]" +    }, +      {"designate", G_admin_designate, "designate",        "give the player designated builder privileges",        "[^3name|slot#^7]"      }, - +          {"devmap", G_admin_devmap, "devmap",        "load a map with cheats (and optionally force layout)",        "[^3mapname^7] (^5layout^7)"      }, -    {"denyweapon", G_admin_denyweapon, "denyweapon", -      "take away a player's ability to use a weapon or class", -      "[^3name|slot#^7] [^3class|weapon^7]" -    }, -      {"drop", G_admin_drop, "drop",        "kick a client from the server without log",        "[^3name|slot#^7] [^3message^7]"      }, +    {"expire", G_admin_expire, "expire", +      "Expires up to 5 level 1 admins with !seen times older than g_adminExpireTime", +      "[^5confirm^7]" +    }, +      {"flag", G_admin_flag, "flag",        "add an admin flag to a player, prefix flag with '-' to disallow the flag. "        "console can use this command on admin levels by prefacing a '*' to the admin level value.", @@ -138,7 +153,7 @@ g_admin_cmd_t g_admin_cmds[ ] =        "list all flags understood by this server",        ""      }, - +          {"help", G_admin_help, "help",        "display commands available to you or help on a specific command",        "(^5command^7)" @@ -148,12 +163,17 @@ g_admin_cmd_t g_admin_cmds[ ] =        "display the contents of server info files",        "(^5subject^7)"      }, -     + +    {"immunity", G_admin_immunity, "immunity", +      "give a player ban immunity", +      "[^3+|-^7](^5slot#^7)" +    }, +      {"invisible", G_admin_invisible, "invisible",        "hides a player so they cannot be seen in playerlists",        ""      }, -     +      {"kick", G_admin_kick, "kick",        "kick a player with an optional reason",        "[^3name|slot#^7] (^5reason^7)" @@ -224,9 +244,11 @@ g_admin_cmd_t g_admin_cmds[ ] =        ""      }, -    {"nobuild", G_admin_nobuild, "nobuild", -      "set nobuild markers to prevent players from building in an area", -      "(^5area^7) (^5height^7)" +    {"outlaw", G_admin_outlaw, "outlaw", +      "adjust a player's bleed counter, usually to make their base turn on them." +      " bleed value can be '?' to query their current value, a number to add and activate bleed status," +      " +num or -num will silently adjust their current bleed value, 0 will pardon them", +      "(^5name|slot^7) (^5bleed value)"      },      {"passvote", G_admin_passvote, "passvote", @@ -240,6 +262,12 @@ g_admin_cmd_t g_admin_cmds[ ] =        "(^5name|slot|*^7)"      }, +    {"practice", G_admin_practice, "practice", +      "set practice mode for player names with 'clan tag', " +      "these players will be allowed to join any team regardless of balance. " +      "'map count' is number of maps to maintain practice mode", +      "[^3clan tag|off^7] [^3map count^7])" +    },      {"putteam", G_admin_putteam, "putteam",        "move a player to a specified team", @@ -253,7 +281,7 @@ g_admin_cmd_t g_admin_cmds[ ] =      {"register", G_admin_register, "register",        "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, "rename", @@ -299,12 +327,13 @@ g_admin_cmd_t g_admin_cmds[ ] =      {"spec999", G_admin_spec999, "spec999",        "move 999 pingers to the spectator team", -      "" -    }, - +      ""}, +       +     //kev: a bit of a hack, but there is no real point to +     //creating a new admin flag for this, so i stole it from !help      {"specme", G_admin_putmespec, "specme", -      "moves you to the spectators", -      "" +      "moves you to the spectators (can be done silently with the 's' argument)", +      "(^5s^7)"      },      {"subnetban", G_admin_subnetban, "subnetban", @@ -316,7 +345,7 @@ g_admin_cmd_t g_admin_cmds[ ] =        "\n ^1WARNING:^7 Use of this command may make your admin.dat incompatible with other game.qvms"      }, -    {"suspendban", G_admin_suspendban, "ban", +    {"suspendban", G_admin_suspendban, "suspendban",        "suspend a ban for a length of time. time is specified as numbers "        "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes),"        " or seconds if no units are specified", @@ -325,7 +354,11 @@ g_admin_cmd_t g_admin_cmds[ ] =      {"time", G_admin_time, "time",        "show the current local server time", -      "" +      ""}, + +    {"tklog", G_admin_tklog, "tklog", +      "list recent teamkill activity", +      "(^5start id#|name|-skip#^7) (^5search skip#^7)"      },      {"unban", G_admin_unban, "ban", @@ -337,13 +370,13 @@ g_admin_cmd_t g_admin_cmds[ ] =        "revoke designated builder privileges",        "[^3name|slot#^7]"      }, -     +      {"unflag", G_admin_flag, "flag",        "clears an admin flag from a player. "        "console can use this command on admin levels by prefacing a '*' to the admin level value.",        "[^3name|slot#|admin#|*adminlevel^7] (^5+^7|^5-^7)[^3flag^7]"      }, - +          {"unlock", G_admin_unlock, "lock",        "unlock a locked team",        "[^3a|h^7]" @@ -354,6 +387,11 @@ g_admin_cmd_t g_admin_cmds[ ] =        "[^3name|slot#^7]"      }, +    {"nobuild", G_admin_nobuild, "nobuild", +      "Enable and control nobuild mode.", +      "[^3on|off|save|add|del|list|mode|zone|+|-|go^7]" +    }, +      {"unpause", G_admin_pause, "pause",        "allow a player to interact with the game."        "  * will unpause all players, using no argument will unpause game clock", @@ -376,6 +414,12 @@ g_admin_ban_t *g_admin_bans[ MAX_ADMIN_BANS ];  g_admin_command_t *g_admin_commands[ MAX_ADMIN_COMMANDS ];  g_admin_namelog_t *g_admin_namelog[ MAX_ADMIN_NAMELOGS ]; +static int admin_adminlog_index = 0; +g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ]; + +static int admin_tklog_index = 0; +g_admin_tklog_t *g_admin_tklog[ MAX_ADMIN_TKLOGS ]; +  // match a certain flag within these flags  // return state of whether flag was found or not,   // set *perm to indicate whether found flag was + or - @@ -404,16 +448,12 @@ static qboolean admin_permission( char *flags, const char *flag, qboolean *perm      *perm = base_perm;      return qtrue;    } -    return qfalse;  } -static int admin_adminlog_index = 0; -g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ]; -  // This function should only be used directly when the client is connecting and thus has no GUID.  // Else, use G_admin_permission()  -qboolean G_admin_permission_guid( char *guid, const char* flag ) +qboolean G_admin_permission_guid( char *guid, const char *flag )  {    int i;    int l = 0; @@ -436,8 +476,7 @@ qboolean G_admin_permission_guid( char *guid, const char* flag )    for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )    {      if( g_admin_levels[ i ]->level == l ) -      return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && -        perm; +      return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && perm;    }    return qfalse;  } @@ -542,18 +581,13 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid )  {    int i;    int alevel = 0; -  int alevel2 = 0; +  qboolean perm = qfalse;    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )    {      if( !Q_stricmp( admin_guid, g_admin_admins[ i ]->guid ) )      {        alevel = g_admin_admins[ i ]->level; - -      // Novelty Levels should be equivelant to level 1 -      if( alevel > 9 ) -        alevel = 1; -        break;      }    } @@ -561,17 +595,9 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid )    {      if( !Q_stricmp( victim_guid, g_admin_admins[ i ]->guid ) )      { -      alevel2 = g_admin_admins[ i ]->level; - -      // Novelty Levels should be equivelant to level 1 -      if( alevel2 > 9 ) -        alevel2 = 1; - -      if( alevel < alevel2 ) -        return qfalse; - -      if( strstr( g_admin_admins[ i ]->flags, va( "%s", ADMF_IMMUTABLE ) ) ) +      if( alevel < g_admin_admins[ i ]->level )          return qfalse; +      return ( !admin_permission( g_admin_admins[ i ]->flags, ADMF_IMMUTABLE, &perm ) || !perm );      }    }    return qtrue; @@ -619,7 +645,7 @@ static void admin_writeconfig( void )  {    fileHandle_t f;    int len, i; -  int t, expiretime; +  int t;    char levels[ MAX_STRING_CHARS ] = {""};    if( !g_admin.string[ 0 ] ) @@ -650,19 +676,9 @@ static void admin_writeconfig( void )    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )    {      // don't write level 0 users -    if( g_admin_admins[ i ]->level < 1 ) +    if( g_admin_admins[ i ]->level == 0 )        continue; -    //if set dont write admins that havent been seen in a while -    expiretime = G_admin_parse_time( g_adminExpireTime.string ); -    if( expiretime > 0 ) { -      //only expire level 1 people -      if( t - expiretime > g_admin_admins[ i ]->seen && g_admin_admins[ i ]->level == 1 ) { -        G_Printf("Admin %s has been expired.\n", g_admin_admins[ i ]->name ); -        continue; -      } -    } -          trap_FS_Write( "[admin]\n", 8, f );      trap_FS_Write( "name    = ", 10, f );      admin_writeconfig_string( g_admin_admins[ i ]->name, f ); @@ -672,8 +688,6 @@ static void admin_writeconfig( void )      admin_writeconfig_int( g_admin_admins[ i ]->level, f );      trap_FS_Write( "flags   = ", 10, f );      admin_writeconfig_string( g_admin_admins[ i ]->flags, f ); -    trap_FS_Write( "seen    = ", 10, f ); -    admin_writeconfig_int( g_admin_admins[ i ]->seen, f );      trap_FS_Write( "\n", 1, f );    }    for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) @@ -697,7 +711,8 @@ static void admin_writeconfig( void )      admin_writeconfig_string( g_admin_bans[ i ]->made, f );      trap_FS_Write( "expires = ", 10, f );      admin_writeconfig_int( g_admin_bans[ i ]->expires, f ); -    if( g_admin_bans[ i ]->suspend > t ) { +    if( g_admin_bans[ i ]->suspend > t ) +    {        trap_FS_Write( "suspend = ", 10, f );        admin_writeconfig_int( g_admin_bans[ i ]->suspend, f );      } @@ -778,6 +793,253 @@ static void admin_readconfig_int( char **cnf, int *v )    *v = atoi( t );  } +void G_admin_chat_writeconfig( void ) +{ +  fileHandle_t f; +  int len; +  char keybuf[ 16 ]; +  int i, j; +  qboolean found; + +  G_admin_karma_sync( ); + +  if( !g_chat.string[ 0 ] ) +  { +    G_Printf( "WARNING: g_chat is not set. " +      " channel subscriptions will not be saved to a file.\n" ); +    return; +  } + +  // check that there is something to save +  found = qfalse; +  for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && !found; i++ ) +  { +    if( g_admin_admins[ i ]->level == 0 ) +      continue; +    for( j = 0; j < CHAT_MAXCHAN; j++ ) +    { +      if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) +        found = qtrue; +    } +    if(g_admin_admins[ i ]->seen ) +      found = qtrue; +  } +  if( !found ) +  { +    G_Printf( "Cowardly refusing to create an empty chat file.\n" ); +    return; +  } + +  len = trap_FS_FOpenFile( g_chat.string, &f, FS_WRITE ); +  if( len < 0 ) +  { +    G_Printf( "chat_writeconfig: could not open g_chat file \"%s\"\n", +              g_chat.string ); +    return; +  } +  for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) +  { +    // don't write level 0 users +    if( g_admin_admins[ i ]->level == 0 ) +      continue; + +    // don't write users not joined to a channel +    found = qfalse; +    for( j = 0; j < CHAT_MAXCHAN; j++ ) +    { +      if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) +        found = qtrue; +    } +    if( !found && !g_admin_admins[ i ]->seen ) +      continue; + +    trap_FS_Write( "[chat]\n", 7, f ); +    trap_FS_Write( "guid    = ", 10, f ); +    admin_writeconfig_string( g_admin_admins[ i ]->guid, f ); +    trap_FS_Write( "seen    = ", 10, f ); +    admin_writeconfig_int( g_admin_admins[ i ]->seen, f ); +    trap_FS_Write( "karma   = ", 10, f ); +    admin_writeconfig_int( g_admin_admins[ i ]->karma, f ); + +    for( j = 0 ; j < CHAT_MAXCHAN; j++ ) +    { +      if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) +      { +        Com_sprintf( keybuf, sizeof( keybuf ), "%d       = ", j ); +        trap_FS_Write( keybuf, 10, f ); +        admin_writeconfig_string( g_admin_admins[ i ]->chat[ j ], f ); +      } +    } + +    trap_FS_Write( "\n", 1, f ); +  } +  trap_FS_FCloseFile( f ); +} + +qboolean G_admin_chat_readconfig( gentity_t *ent ) +{ +  g_admin_admin_t *a = NULL; +  fileHandle_t f; +  int len; +  char *cnf, *cnf2; +  char *t; +  int uc = 0, cc = 0; +  qboolean chat_open; +  char guid[ 33 ]; +  int i; + +  if( !g_chat.string[ 0 ] ) +  { +    ADMP( "chat_readconfig: g_chat is not set, not loading channel subscriptions " +      "from a file\n" ); +    return qfalse; +  } + +  len = trap_FS_FOpenFile( g_chat.string, &f, FS_READ ) ; +  if( len < 0 ) +  { +    ADMP( va( "chat_readconfig: could not open chat config file %s\n", +      g_chat.string ) ); +    return qfalse; +  } +  cnf = G_Alloc( len + 1 ); +  cnf2 = cnf; +  trap_FS_Read( cnf, len, f ); +  *( cnf + len ) = '\0'; +  trap_FS_FCloseFile( f ); + +  t = COM_Parse( &cnf ); +  chat_open = qfalse; +  while( *t ) +  { +    if( !Q_stricmp( t, "[chat]" ) ) +    { +      chat_open = qtrue; +      a = NULL; +    } +    else if( chat_open ) +    { +      int chan; + +      if( !Q_stricmp( t, "guid" ) ) +      { +        admin_readconfig_string( &cnf, guid, sizeof( guid ) ); +        for( i = 0 ; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) +        { +          if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) +          { +            a = g_admin_admins[ i ]; +            memset( a->chat, 0, sizeof( a->chat) ); +            uc++; +            break; +          } +        } +      } +      else if( a == NULL ) +      { +        // no guid match, ignored +      } +      else if( !Q_stricmp( t, "seen" ) ) +      { +        admin_readconfig_int( &cnf, &a->seen ); +      } +      else if( !Q_stricmp( t, "karma" ) ) +      { +        admin_readconfig_int( &cnf, &a->karma ); +      } +      else if( *t >= '0' && *t <= '9' ) +      { +        chan = atoi( t ); +        if( chan >= 0 && chan < CHAT_MAXCHAN && a ) +        { +          admin_readconfig_string( &cnf, a->chat[ chan ], sizeof( a->chat[ chan ] ) ); +          cc++; +        } +      } +      else +      { +        ADMP( va( "chat_readconfig: [chat] parse error near %s on line %d\n", +          t, COM_GetCurrentParseLine() ) ); +      } +    } + +    t = COM_Parse( &cnf ); +  } + +  G_Free( cnf2 ); +  ADMP( va( "chat_readconfig: loaded %d users with %d channels\n", uc, cc ) ); +  return qtrue; +} + +void G_admin_chat_sync( gentity_t *ent ) +{ +  gentity_t *target; +  int i, j; +  qboolean rejoin = qfalse; + +  if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) +    return; + +  for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) +  { +    if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) +    { +      ent->client->pers.karma = g_admin_admins[ i ]->karma; +      for( j = 0; j < CHAT_MAXCHAN; j++ ) +      { +        Q_strncpyz( ent->client->pers.chat[ j ], +          g_admin_admins[ i ]->chat[ j ], +        sizeof( g_admin_admins[ i ]->chat[ j ] ) ); +      } +      rejoin = qtrue; +      break; +    } +  } + +  if( !rejoin ) +    return; + +  for( j = 0; j < CHAT_MAXCHAN; j++ ) +  { +    if( !ent->client->pers.chat[ j ][ 0 ] ) +      continue; + +    for( i = 0; i < level.maxclients; i++ ) +    { +      target = &g_entities[ i ]; +      if( target && target->client && +          target->client->pers.connected == CON_CONNECTED && +          !Q_stricmp( target->client->pers.chat[ j ], ent->client->pers.chat[ j ] ) ) +      { +        trap_SendServerCommand( i, va( "print \"join: %s^7 has rejoined channel #%d\n\"", +          ent->client->pers.netname, j ) ); +      } +    } +  } +} + +void G_admin_chat_update( gentity_t *ent, int chan ) +{ +  int i; + +  if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) +    return; + +  if( chan < 0 || chan > CHAT_MAXCHAN - 1 ) +    return; + +  for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) +  { +    if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) +    { +      Q_strncpyz( g_admin_admins[ i ]->chat[ chan ], +        ent->client->pers.chat[ chan ], +        sizeof( g_admin_admins[ i ]->chat[ chan ] ) ); +      return; +    } +  } +} +  // if we can't parse any levels from readconfig, set up default  // ones to make new installs easier for admins  static void admin_default_levels( void ) @@ -828,7 +1090,7 @@ static void admin_default_levels( void )      sizeof( l->name ) );    Q_strncpyz( g_admin_levels[ 4 ]->flags,       "listplayers admintest help specme time putteam spec999 kick mute showbans " -    "ban namelog warn denybuild ADMINCHAT SEESFULLLISTPLAYERS", +    "ban namelog warn denybuild invisible ADMINCHAT SEESFULLLISTPLAYERS",      sizeof( l->flags ) );    Q_strncpyz( g_admin_levels[ 5 ]->name, "^1Server Operator", @@ -897,7 +1159,7 @@ void G_admin_set_adminname( gentity_t *ent )    }  } -//  Get an admin's registered name +//  get an admin's realname  const char *G_admin_get_adminname( gentity_t *ent )  {    int i; @@ -914,8 +1176,6 @@ const char *G_admin_get_adminname( gentity_t *ent )    return ent->client->pers.netname;  } -// Get an admin's name to print as the user of various commands,  -// obeying admin stealth settings when necessary  char* G_admin_adminPrintName( gentity_t *ent )  {    char *out; @@ -1110,26 +1370,27 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev          break;        }      } -    ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", +    ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n",        i,        l,        lname,        guid_stub, +      ( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) ) ? "I" : "",        vic->client->pers.netname ) );      drawn++;    }    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]      && drawn < MAX_ADMIN_LISTITEMS; i++ ) -   if( g_admin_admins[ i ]->level >= minlevel )  +   if( ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) || +       ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) || +       ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) )     { -       if( start )       {         start--;         continue;       } -       if( search[ 0 ] )       {         G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) ); @@ -1174,11 +1435,12 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev           break;         }       } -     ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", +     ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n",         ( i + MAX_CLIENTS ),         g_admin_admins[ i ]->level,         lname,         guid_stub, +       ( G_admin_permission_guid( g_admin_admins[ i ]->guid, ADMF_BAN_IMMUNITY ) ) ? "I" : "",         g_admin_admins[ i ]->name ) );       drawn++;     } @@ -1223,6 +1485,7 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )    int t;    char notice[51];    qboolean ignoreIP = qfalse; +  qboolean banned = qfalse;    trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); @@ -1252,17 +1515,15 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )      if(!IP[k]) continue;      userIP |= IP[k] << 8*(k-1);    } -  ignoreIP = G_admin_permission_guid( guid , ADMF_BAN_IMMUNITY ); +  ignoreIP = G_admin_permission_guid( guid, ADMF_BAN_IMMUNITY );    for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )    {      // 0 is for perm ban      if( g_admin_bans[ i ]->expires != 0 &&           ( g_admin_bans[ i ]->expires - t ) < 1 )        continue; -    //if suspend time is over continue      if( g_admin_bans[ i ]->suspend >= t )        continue; -      if( !ignoreIP )      {        tempIP = userIP; @@ -1295,47 +1556,38 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )        if( intIP == tempIP || mask == 0 )        { -        char duration[ 32 ]; -        G_admin_duration( ( g_admin_bans[ i ]->expires - t ), -          duration, sizeof( duration ) ); - -        // flood protected -        if( t - lastConnectTime >= 300 || -            Q_stricmp( lastConnectIP, ip ) ) -        { -          lastConnectTime = t; -          Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); - -          G_WarningsPrintf( -            "ban", -            "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n", -            Info_ValueForKey( userinfo, "name" ), -            g_admin_bans[ i ]->name, -            i+1, -            ip,  -            g_admin_bans[ i ]->banner, -            duration, -            g_admin_bans[ i ]->reason ); -        } -             -        Com_sprintf( -          reason, -          rlen, -          "You have been banned by %s^7 reason: %s^7 expires: %s       %s", -          g_admin_bans[ i ]->banner, -          g_admin_bans[ i ]->reason, -          duration, -          notice -          );          G_LogPrintf("Banned player tried to connect from IP %s\n", ip); -        return qtrue; +        banned = qtrue;        }      } -    if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) +    if( !banned && *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) +    { +      G_LogPrintf("Banned player tried to connect with GUID %s\n", guid); +      banned = qtrue; +    } +    if( banned )      {        char duration[ 32 ];        G_admin_duration( ( g_admin_bans[ i ]->expires - t ),          duration, sizeof( duration ) ); + +      // flood protected +      if( t - lastConnectTime >= 300 || +          Q_stricmp( lastConnectIP, ip ) ) +      { +        lastConnectTime = t; +        Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); + +        G_AdminsPrintf( +          "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n", +          Info_ValueForKey( userinfo, "name" ), +          g_admin_bans[ i ]->name, +          i+1, +          ip,  +          g_admin_bans[ i ]->banner, +          duration, +          g_admin_bans[ i ]->reason ); +      }        Com_sprintf(          reason,          rlen, @@ -1344,7 +1596,6 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )          g_admin_bans[ i ]->reason,          duration        ); -      G_Printf("Banned player tried to connect with GUID %s\n", guid);        return qtrue;      }    } @@ -1469,10 +1720,7 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )    char n2[ MAX_NAME_LENGTH ];    int clientNum = ( client - level.clients ); -  if ( client->sess.invisible == qfalse ) -  { -    G_admin_seen_update( client->pers.guid ); -  } +  G_admin_seen_update( client, disconnect );    G_SanitiseString( client->pers.netname, n1, sizeof( n1 ) );    for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) @@ -1539,11 +1787,6 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )            G_AdminsPrintf( "^7%s^7's Putteam spectator has been restored\n", client->pers.netname );            g_admin_namelog[ i ]->specExpires = 0;          } -        if( g_admin_namelog[ i ]->voteCount > 0 ) -        { -          client->pers.voteCount = g_admin_namelog[ i ]->voteCount; -          g_admin_namelog[ i ]->voteCount = 0; -        }        }        else        { @@ -1573,12 +1816,9 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )          {            g_admin_namelog[ i ]->specExpires = client->pers.specExpires;          } -        if( client->pers.voteCount > 0 ) -        { -          g_admin_namelog[ i ]->voteCount = client->pers.voteCount; -        }        } +        return;      }    } @@ -1698,10 +1938,6 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg )        {          admin_readconfig_string( &cnf, a->flags, sizeof( a->flags ) );        } -      else if( !Q_stricmp( t, "seen" ) ) -      { -        admin_readconfig_int( &cnf, &a->seen ); -      }        else        {          ADMP( va( "^3!readconfig: ^7[admin] parse error near %s on line %d\n", @@ -1863,6 +2099,9 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg )    for( i = 0; i < level.maxclients; i++ )      if( level.clients[ i ].pers.connected != CON_DISCONNECTED )        level.clients[ i ].pers.adminLevel = G_admin_level( &g_entities[ i ] ); + +  G_admin_chat_readconfig( ent ); +    return qtrue;  } @@ -1874,7 +2113,6 @@ qboolean G_admin_time( gentity_t *ent, int skiparg )    t = trap_RealTime( &qt );    ADMP( va( "^3!time: ^7local time is %02i:%02i:%02i\n",      qt.tm_hour, qt.tm_min, qt.tm_sec ) ); -        return qtrue;  } @@ -2138,379 +2376,6 @@ qboolean G_admin_setlevel( gentity_t *ent, int skiparg )    return qtrue;  } -static int SortFlags( const void *pa, const void *pb ) -{ -  char *a = (char *)pa; -  char *b = (char *)pb; - -  if( *a == '-' || *a == '+' ) -    a++; -  if( *b == '-' || *b == '+' ) -    b++; -  return strcmp(a, b); -} - -#define MAX_USER_FLAGS 200 -const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear, -                               char *newflags, int size ) -{ -  char *token, *token_p; -  char *key; -  char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; -  char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; -  char allflag[ MAX_ADMIN_FLAG_LEN ]; -  char newflag[ MAX_ADMIN_FLAG_LEN ]; -  int head_count = 0; -  int tail_count = 0; -  qboolean flagset = qfalse; -  int i; - -  if( !flag[ 0 ] ) -  { -    return "invalid admin flag"; -  } - -  allflag[ 0 ] = '\0'; -  token_p = oldflags; -  while( *( token = COM_Parse( &token_p ) ) ) -  { -    key = token; -    if( *key == '-' || *key == '+' ) -      key++; - -    if( !strcmp( key, flag ) ) -    { -      if( flagset ) -        continue; -      flagset = qtrue; -      if( clear ) -      { -        // clearing ALLFLAGS will result in clearing any following flags -        if( !strcmp( key, ADMF_ALLFLAGS ) ) -          break; -        else -          continue; -      } -      Com_sprintf( newflag, sizeof( newflag ), "%s%s", -        ( add ) ? "+" : "-", key ); -    } -    else -    { -      Q_strncpyz( newflag, token, sizeof( newflag ) ); -    } - -    if( !strcmp( key, ADMF_ALLFLAGS ) ) -    { -      if( !allflag[ 0 ] ) -        Q_strncpyz( allflag, newflag, sizeof( allflag ) ); -      continue; -    } - -    if( !allflag[ 0 ] ) -    { -      if( head_count < MAX_USER_FLAGS ) -      { -        Q_strncpyz( head_flags[ head_count ], newflag, -                    sizeof( head_flags[ head_count ] ) ); -        head_count++; -      } -    } -    else -    { -      if( tail_count < MAX_USER_FLAGS ) -      { -        Q_strncpyz( tail_flags[ tail_count ], newflag, -                    sizeof( tail_flags[ tail_count ] ) ); -        tail_count++; -      } -    } -  } - -  if( !flagset && !clear ) -  { -    if( !strcmp( flag, ADMF_ALLFLAGS ) ) -    { -      Com_sprintf( allflag, sizeof( allflag ), "%s%s", -        ( add ) ? "" : "-", ADMF_ALLFLAGS ); -    } -    else if( !allflag[ 0 ] ) -    { -      if( head_count < MAX_USER_FLAGS ) -      { -        Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ), -          "%s%s", ( add ) ? "" : "-", flag ); -        head_count++; -      } -    } -    else -    { -      if( tail_count < MAX_USER_FLAGS ) -      { -        Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ), -          "%s%s", ( add ) ? "+" : "-", flag ); -        tail_count++; -      } -    } -  } - -  qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags ); -  qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags ); - -  // rebuild flags -  newflags[ 0 ] = '\0'; -  for( i = 0; i < head_count; i++ ) -  { -    Q_strcat( newflags, size, -              va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) ); -  } -  if( allflag[ 0 ] ) -  { -    Q_strcat( newflags, size, -      va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) ); - -    for( i = 0; i < tail_count; i++ ) -    { -      Q_strcat( newflags, size, -                va( " %s", tail_flags[ i ] ) ); -    } -  } - -  return NULL; -} - -typedef struct { -  char *flag; -  char *description; -} AdminFlagListEntry_t; -static AdminFlagListEntry_t adminFlagList[] = -{ -  { ADMF_ACTIVITY,             "inactivity rules do not apply" }, -  { ADMF_ADMINCHAT,            "can see and use admin chat" }, -  { ADMF_ALLFLAGS,             "has all flags and can use any command" }, -  { ADMF_BAN_IMMUNITY,         "immune from IP bans" }, -  { ADMF_CAN_PERM_BAN,         "can permanently ban players" }, -  { ADMF_DBUILDER,             "permanent designated builder" }, -  { ADMF_FORCETEAMCHANGE,      "team balance rules do not apply" }, -  { ADMF_INCOGNITO,            "does not show as admin in !listplayers" }, -  { ADMF_IMMUNITY,             "cannot be vote kicked or muted" }, -  { ADMF_IMMUTABLE,            "admin commands cannot be used on them" }, -  { ADMF_NOCENSORFLOOD,        "no flood protection" }, -  { ADMF_NO_VOTE_LIMIT,        "vote limitations do not apply" }, -  { ADMF_SEESFULLLISTPLAYERS,  "sees all info in !listplayers" }, -  { ADMF_SPEC_ALLCHAT,         "can see team chat as spectator" }, -  { ADMF_ADMINSTEALTH,         "uses admin stealth" }, -  { ADMF_TEAMCHANGEFREE,       "keeps credits on team switch" }, -  { ADMF_TEAMCHAT_CMD,         "can run commands from team chat" }, -  { ADMF_UNACCOUNTABLE,        "does not need to specify reason for kick/ban" }, -  { ADMF_NO_CHAT,              "can not talk" }, -  { ADMF_NO_VOTE,              "can not call votes" } -}; -static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] ); - -#define MAX_LISTCOMMANDS 128 -qboolean G_admin_flaglist( gentity_t *ent, int skiparg ) -{ -  qboolean shown[ MAX_LISTCOMMANDS ]; -  int i, j; -  int count = 0; - -  ADMBP_begin(); - -  ADMBP( "^3Ability flags:\n" ); - -  for( i = 0; i < adminNumFlags; i++ ) -  { -    ADMBP( va( "  %s%-20s ^7%s\n", -      ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1", -      adminFlagList[ i ].flag, -      adminFlagList[ i ].description ) ); -  } - -  ADMBP( "^3Command flags:\n" ); - -  memset( shown, 0, sizeof( shown ) ); -  for( i = 0; i < adminNumCmds; i++ ) -  { -    if( i < MAX_LISTCOMMANDS && shown[ i ] ) -      continue; -    ADMBP( va( "  ^5%-20s^7", g_admin_cmds[ i ].flag ) ); -    for( j = i; j < adminNumCmds; j++ ) -    { -      if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) ) -      { -        ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) ); -        if( j < MAX_LISTCOMMANDS ) -          shown[ j ] = qtrue; -      } -    } -    ADMBP( "\n" ); -    count++; -  } - -  ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n", -    adminNumFlags, count ) ); - -  ADMBP_end(); - -  return qtrue; -} - -qboolean G_admin_flag( gentity_t *ent, int skiparg ) -{ -  char command[ MAX_ADMIN_CMD_LEN ], *cmd; -  char name[ MAX_NAME_LENGTH ]; -  char flagbuf[ MAX_ADMIN_FLAG_LEN ]; -  char *flag; -  int id; -  char adminname[ MAX_NAME_LENGTH ] = {""}; -  const char *result; -  qboolean add = qtrue; -  qboolean clear = qfalse; -  int admin_level = -1; -  int i, level; - -  G_SayArgv( skiparg, command, sizeof( command ) ); -  cmd = command; -  if( *cmd == '!' ) -    cmd++; - -  if( G_SayArgc() < 2 + skiparg ) -  { -    ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); -    return qfalse; -  } - -  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); -  if( name[ 0 ] == '*' ) -  { -    if( ent ) -    { -      ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) ); -      return qfalse; -    } -    id = atoi( name + 1 ); -    for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) -    { -      if( g_admin_levels[ i ]->level == id ) -      { -        admin_level = i; -        break; -      } -    } -    if( admin_level < 0 ) -    { -      ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) ); -      return qfalse; -    } -    Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id ); -  } -  else -  { -    id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) ); -    if( id < 0 ) -      return qfalse; - -    if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) -    { -      ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) ); -      return qfalse; -    } -  } - -  if( G_SayArgc() < 3 + skiparg ) -  { -    flag = ""; -    level = 0; -    if( admin_level < 0 ) -    { -      for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) -      { -        if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level ) -        { -          flag = g_admin_levels[ i ]->flags; -          level = g_admin_admins[ id ]->level; -          break; -        } -      } -      ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", -        cmd, adminname, g_admin_admins[ id ]->flags) ); -    } -    else -    { -      flag = g_admin_levels[ admin_level ]->flags; -      level = g_admin_levels[ admin_level ]->level; -    } -    ADMP( va( "^3%s:^7  level %d flags are '%s'\n", -      cmd, level, flag ) ); - -    return qtrue; -  } - -  G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); -  flag = flagbuf; -  if( flag[ 0 ] == '-' || flag[ 0 ] == '+' ) -  { -    add = ( flag[ 0 ] == '+' ); -    flag++; -  } -  if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) -  { -    ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); -    return qfalse; -  } -  if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) ) -  { -    ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); -    return qfalse; -  } - -  if( !Q_stricmp( cmd, "unflag" ) ) -  { -    clear = qtrue; -  } - -  if( admin_level < 0 ) -  { -    result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear, -                                g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); -  } -  else -  { -    result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear, -                                g_admin_levels[ admin_level ]->flags, -                                sizeof( g_admin_levels[ admin_level ]->flags ) ); -  } -  if( result ) -  { -    ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n", -      flag, result ) ); -    return qfalse; -  } - -  if( !Q_stricmp( cmd, "flag" ) ) -  { -    G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n", -      cmd, adminname, -      ( add ) ? "given" : "denied", -      flag, -      ( ent ) ? ent->client->pers.netname : "console" ); -  } -  else -  { -    G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n", -      cmd, flag, adminname, -      ( ent ) ? ent->client->pers.netname : "console" ); -  } - -  if( !g_admin.string[ 0 ] ) -    ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " -      "to a file\n", cmd ) ); -  else -    admin_writeconfig(); - -  return qtrue; -} -  int G_admin_parse_time( const char *time )  {    int seconds = 0, num = 0; @@ -2544,6 +2409,49 @@ int G_admin_parse_time( const char *time )    return seconds;  } +static void admin_check_duplicate_ban( int ban ) +{ +  qtime_t qt; +  int t; +  int i, j; +  qboolean immune; + +  if ( ban < 0 || ban >= MAX_ADMIN_BANS || !g_admin_bans[ ban ] ) +    return; + +  t = trap_RealTime( &qt ); +  for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) +  { +    if( g_admin_bans[ i ]->expires != 0 +      && ( g_admin_bans[ i ]->expires - t ) < 1 ) +    { +      continue; +    } + +    if( i != ban && +      strstr( g_admin_bans[ ban ]->ip, g_admin_bans[ i ]->ip ) == g_admin_bans[ ban ]->ip ) +    { +      immune = qfalse; + +      for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) +      { +        if( !Q_stricmp( g_admin_bans[ ban ]->guid, g_admin_admins[ j ]->guid ) && +            G_admin_permission_guid( g_admin_admins[ j ]->guid, ADMF_BAN_IMMUNITY ) ) +        { +          immune = qtrue; +        } +      } + +      G_AdminsPrintf( "new ban #%d duplicates %sban #%d (%s^7)%s.\n", +        ban + 1, +        ( g_admin_bans[ i ]->suspend > t ) ? "SUSPENDED " : "", +        i + 1, +        g_admin_bans[ i ]->name, +        ( immune ) ? ", player has immunity" : "" ); +    } +  } +} +  static qboolean admin_create_ban( gentity_t *ent,    char *netname,    char *guid, @@ -2597,6 +2505,82 @@ static qboolean admin_create_ban( gentity_t *ent,      return qfalse;    }    g_admin_bans[ i ] = b; + +  admin_check_duplicate_ban( i ); + +  return qtrue; +} + +static qboolean admin_create_ban_check_repeats( gentity_t *ent, +  char *netname, +  char *guid, +  char *ip, +  int seconds, +  char *reason )  +{ +  qboolean repeatBan = qfalse; +  qboolean isKick = qfalse; +  qtime_t qt; +  int t; +  int i; + +  t = trap_RealTime( &qt ); + +  if( seconds >= G_admin_parse_time( g_adminTempBan.string ) - 60 && +      seconds <= G_admin_parse_time( g_adminTempBan.string ) + 60 ) +    isKick = qtrue; + +  if( g_adminBanRepeatKicks.integer && isKick ) +  { +    for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ] && !repeatBan; i++ ) +    { +      if( g_admin_bans[ i ]->expires != 0 && +          g_admin_bans[ i ]->expires - t  < 1 ) +        continue; + +      if( !Q_stricmp( ip, g_admin_bans[ i ]->ip ) || +        ( guid[0] != 'X' && !Q_stricmp( guid, g_admin_bans[ i ]->guid) )) +      { +        char duration[ 32 ]; + +        g_admin_bans[ i ]->suspend = 0; +        g_admin_bans[ i ]->expires += g_adminBanRepeatKicks.integer * 60 * 60; +        repeatBan = qtrue; + +        G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) ); +        trap_SendServerCommand( -1, +          va( "print \"^3autoban: ^7%s^7 has been auto-banned. duration: %s, reason: repeated kicks\n\"", +            netname, duration ) ); + +        return qtrue; +      } +    } +  } + +  if( !admin_create_ban( ent, netname, guid, ip, seconds, reason ) ) +    return qfalse; + +  if( g_adminBanRepeatKicks.integer && seconds > 0 && isKick ) +  { +    int newestBan = -1; +    int length; + +    length = g_adminBanRepeatKicks.integer * 60 * 60; +    if( admin_create_ban( ent, netname, guid, ip, length, reason ) ) +    { +      for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) +      { +        newestBan = i; +      } +      if( newestBan >= 0 && +          !Q_stricmp( ip, g_admin_bans[ newestBan ]->ip ) ) +      { +        g_admin_bans[ newestBan ]->suspend = t + length; +        Q_strncpyz( g_admin_bans[ newestBan ]->banner, "^3auto-banner", sizeof( g_admin_bans[ newestBan ]->banner ) ); +      } +    } +  } +    return qtrue;  } @@ -2634,7 +2618,8 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg )      return qfalse;    }    vic = &g_entities[ pids[ 0 ] ]; -  admin_create_ban( ent, +  G_admin_autorevert( vic ); +  admin_create_ban_check_repeats( ent,      vic->client->pers.netname,      vic->client->pers.guid,      vic->client->pers.ip, G_admin_parse_time( g_adminTempBan.string ), @@ -2642,10 +2627,20 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg )    if( g_admin.string[ 0 ] )      admin_writeconfig(); +  vic->client->pers.karma -= 5000; +   trap_SendServerCommand( pids[ 0 ],    va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\n%s\"",      ( ent ) ? va( "admin:\n%s", G_admin_adminPrintName( ent ) ) : "admin\nconsole",      ( *reason ) ? reason : "kicked by admin", notice ) ); + +  G_LogPrintf( "kick: %i %i [%s] (%s) %s^7 %s^7\n", +    vic->client->ps.clientNum, +    G_admin_parse_time( g_adminTempBan.string ), +    vic->client->pers.ip, +    vic->client->pers.guid, +    vic->client->pers.netname, +    ( *reason ) ? reason : "automatic temp ban created by kick" );    trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s",      ( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console", @@ -2669,6 +2664,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )    char s2[ MAX_NAME_LENGTH ];    char guid_stub[ 9 ];    char notice[51]; +  gentity_t *vic;    trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); @@ -2823,7 +2819,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )      return qfalse;    } -  admin_create_ban( ent, +  admin_create_ban_check_repeats( ent,      g_admin_namelog[ logmatch ]->name[ 0 ],      g_admin_namelog[ logmatch ]->guid,      g_admin_namelog[ logmatch ]->ip, @@ -2836,6 +2832,13 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )    else      admin_writeconfig(); +  G_LogPrintf( "ban: %i %i [%s] (%s) %s^7 %s^7\n", +    g_admin_namelog[ logmatch ]->slot, seconds, +    g_admin_namelog[ logmatch ]->ip, +    g_admin_namelog[ logmatch ]->guid, +    g_admin_namelog[ logmatch ]->name[ 0 ], +    ( *reason ) ? reason : "banned by admin" ); +    if( g_admin_namelog[ logmatch ]->slot == -1 )     {      // client is already disconnected so stop here @@ -2847,6 +2850,11 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )        ( *reason ) ? reason : "banned by admin" ) );      return qtrue;    } +  vic = &g_entities[ g_admin_namelog[ logmatch ]->slot ]; +  G_admin_autorevert( vic ); + +  if( g_karma.integer ) +    vic->client->pers.karma -= 5000;    trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot,      va( "disconnect \"You have been banned.\n" @@ -3022,6 +3030,7 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )          ADMP( "^3!subnetban: ^7Only console may ban such a large network. Regular admins may only ban >=16.\n" );          return qfalse;        } +        if( strcmp(exl, "!") )        {          ADMP( "^3!subnetban: ^1WARNING:^7 you are about to ban a large network, use !subnetban [ban] [mask] ! to force^7\n" ); @@ -3065,12 +3074,12 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )    {      Q_strncpyz(         cIPRlow,  -      va("%u.%u.%u.%u", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255),  +      va("%i.%i.%i.%i", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255),        sizeof( cIPRlow )       );      Q_strncpyz(         cIPRhigh,  -      va("%u.%u.%u.%u", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255),  +      va("%i.%i.%i.%i", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255),         sizeof( cIPRhigh )       );    } @@ -3097,16 +3106,15 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )    return qtrue;  } +  qboolean G_admin_suspendban( gentity_t *ent, int skiparg )  {    int bnum;    int length; -  int timenow = 0;    int expires = 0;    char *arg;    char bs[ 5 ];    char duration[ 32 ]; -  qtime_t qt;    if( G_SayArgc() < 3 + skiparg )    { @@ -3127,9 +3135,7 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg )    }    arg = G_SayConcatArgs( 2 + skiparg ); -  timenow = trap_RealTime( &qt );    length = G_admin_parse_time( arg ); -    if( length < 0 )    {      ADMP( "^3!suspendban: ^7invalid length\n" ); @@ -3140,16 +3146,13 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg )      length = MAX_ADMIN_BANSUSPEND_DAYS * 24 * 60 * 60;      ADMP( va( "^3!suspendban: ^7maximum ban suspension is %d days\n",        MAX_ADMIN_BANSUSPEND_DAYS ) ); -  } else if( g_admin_bans[ bnum - 1 ]->expires > 0 && length + timenow > g_admin_bans[ bnum - 1 ]->expires ) { -    length = g_admin_bans[ bnum - 1 ]->expires - timenow; -    G_admin_duration( length , duration, sizeof( duration ) ); -    ADMP( va( "^3!suspendban: ^7Suspension Duration trimmed to Ban duration: %s\n", -          duration ) );    }    if ( length > 0 )    { -    expires = timenow + length; +    qtime_t qt; + +    expires = trap_RealTime( &qt ) + length;    }    if( g_admin_bans[ bnum - 1 ]->suspend == expires )    { @@ -3225,93 +3228,6 @@ qboolean G_admin_unban( gentity_t *ent, int skiparg )    return qtrue;  } -qboolean G_admin_putteam( gentity_t *ent, int skiparg ) -{ -  int pids[ MAX_CLIENTS ]; -  char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; -  gentity_t *vic; -  pTeam_t teamnum = PTE_NONE; -  char teamdesc[ 32 ] = {"spectators"}; -  char secs[ 7 ]; -  int seconds = 0; -  qboolean useDuration = qfalse; - -  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); -  G_SayArgv( 2 + skiparg, team, sizeof( team ) ); -  if( G_SayArgc() < 3 + skiparg ) -  { -    ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" ); -    return qfalse; -  } - -  if( G_ClientNumbersFromString( name, pids ) != 1 ) -  { -    G_MatchOnePlayer( pids, err, sizeof( err ) ); -    ADMP( va( "^3!putteam: ^7%s\n", err ) ); -    return qfalse; -  } -  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) -  { -    ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " -        " admin level than you\n" ); -    return qfalse; -  } -  vic = &g_entities[ pids[ 0 ] ]; - -  if ( vic->client->sess.invisible == qtrue ) -  { -    ADMP( "^3!putteam: ^7invisible players cannot join a team\n" ); -    return qfalse; -  } - -  switch( team[ 0 ] ) -  { -  case 'a': -    teamnum = PTE_ALIENS; -    Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) ); -    break; -  case 'h': -    teamnum = PTE_HUMANS; -    Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) ); -    break; -  case 's': -    teamnum = PTE_NONE; -    break; -  default: -    ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) ); -    return qfalse; -  } -  //duration code -  if( G_SayArgc() > 3 + skiparg ) { -    //can only lock players in spectator -    if ( teamnum != PTE_NONE ) -    { -      ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" ); -      return qfalse; -    } -    G_SayArgv( 3 + skiparg, secs, sizeof( secs ) ); -    seconds = G_admin_parse_time( secs ); -    useDuration = qtrue; -  } - -  if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE ) -  { -    ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) ); -    return qfalse; -  } - -  if( useDuration == qtrue && seconds > 0 ) { -    vic->client->pers.specExpires = level.time + ( seconds * 1000 ); -  } -  G_ChangeTeam( vic, teamnum ); - -  AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"", -          ( ent ) ? G_admin_adminPrintName( ent ) : "console", -          vic->client->pers.netname, teamdesc, -          ( seconds ) ? va( " for %i seconds", seconds ) : "" ) ); -  return qtrue; -} -  qboolean G_admin_seen(gentity_t *ent, int skiparg )  {    char name[ MAX_NAME_LENGTH ]; @@ -3362,11 +3278,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )      if( i == id || (search[ 0 ] && strstr( name, search ) ) )      { -      if ( vic->client->sess.invisible == qfalse ) -      { -        ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", i, vic->client->pers.netname ) ); -        count++; -      } +      ADMBP( va( "%4d %s^7 is currently playing\n", i, vic->client->pers.netname ) ); +      count++;      }    }    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && count < 10; i++ ) @@ -3384,11 +3297,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )          if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid )            && strstr( name, search ) )          { -          if ( vic->client->sess.invisible == qfalse ) -          { -            ison = qtrue; -            break; -          } +          ison = qtrue; +          break;          }        } @@ -3396,7 +3306,7 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )        {          if( id == -1 )            continue; -        ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", +        ADMBP( va( "%4d %s^7 is currently playing\n",            i + MAX_CLIENTS, g_admin_admins[ i ]->name ) );        }        else @@ -3422,10 +3332,42 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )    return qtrue;  } -void G_admin_seen_update( char *guid ) +void G_admin_karma_sync( void )  { +  gclient_t *p; +  int i, j; + +  if( !g_karma.integer ) +    return; + +  for( i = 0; i < level.maxclients; i++ ) +  { +    p = &level.clients[ i ]; + +    if( p->pers.connected != CON_CONNECTED && +        p->pers.connected != CON_CONNECTING ) +      continue; +    if( p->pers.adminLevel < 1 ) +      continue; + +    for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) +    { +      if( !Q_stricmp( g_admin_admins[ j ]->guid, p->pers.guid ) ) +      { +        if( p->pers.karma ) +          g_admin_admins[ j ]->karma = p->pers.karma; +        break; +      } +    } +  } +} + +void G_admin_seen_update( gclient_t *client, qboolean disconnect ) +{ +  char *guid;    int i; +  guid = client->pers.guid;    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ )    {      if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) @@ -3433,11 +3375,198 @@ void G_admin_seen_update( char *guid )        qtime_t qt;        g_admin_admins[ i ]->seen = trap_RealTime( &qt ); +      if ( disconnect && client->pers.karma ) +        g_admin_admins[ i ]->karma = client->pers.karma;        return;      }    }  } +typedef struct +{ +  g_admin_admin_t *admin; +  int id; +} +g_admin_sort_t; + +static int SortSeenTimes( const void *av, const void *bv) +{ +  const g_admin_sort_t *a = av; +  const g_admin_sort_t *b = bv; + +  if( !a->admin || !b->admin ) +    return 0; + +  if( a->admin->seen > b->admin->seen ) +    return 1; +  if( a->admin->seen < b->admin->seen ) +    return -1; + +  return 0; +} + +qboolean G_admin_expire( gentity_t *ent, int skiparg ) +{ +  g_admin_sort_t sort_list[ MAX_ADMIN_ADMINS ]; +  char arg[ MAX_ADMIN_CMD_LEN ]; +  qboolean confirm = qfalse; +  qtime_t qt; +  int t; +  int count = 0; +  int max = 5; +  int i; + +  if( g_adminExpireTime.integer < 1 ) +  { +    ADMP( "^3!expire: ^7expire is disabled\n" ); +    return qfalse; +  } + +  if( G_SayArgc() > 1 + skiparg ) +  { +    G_SayArgv( skiparg + 1, arg, sizeof( arg ) ); +    if( !Q_stricmp( arg, "confirm" ) ) +      confirm = qtrue; +  } + +  t = trap_RealTime( &qt ); + +  memset( sort_list, 0, sizeof( sort_list ) ); +  for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) +  { +    sort_list[ i ].admin = g_admin_admins[ i ]; +    sort_list[ i ].id = MAX_CLIENTS + i; +    count++; +  } + +  qsort( sort_list, count, sizeof( sort_list[ 0 ] ), SortSeenTimes ); + +  ADMBP_begin(); +  for( i = 0; i < count && max > 0 ; i++ ) +  { +    if( !sort_list[ i ].admin ) +      continue; +    if( sort_list[ i ].admin->level != 1 ) +      continue; +    if( t - sort_list[ i ].admin->seen > g_adminExpireTime.integer * 86400 ) +    { +      char sduration[ 32 ]; + +      max--; +      G_admin_duration( t - sort_list[ i ].admin->seen, sduration, sizeof( sduration ) ); + +      if( confirm ) +      { +        trap_SendConsoleCommand( EXEC_APPEND, va( "!setlevel %d 0;", sort_list[ i ].id ) ); +      } +      else +      { +        ADMBP( va( "  ^7%d %s^7 last seen %s%s\n", +          sort_list[ i ].id, +          sort_list[ i ].admin->name, +          (sort_list[ i ].admin->seen ) ? sduration : "", +          (sort_list[ i ].admin->seen ) ? " ago" : "time is unknown" ) ); +      } +    } +  } + +  ADMBP( va( "^3!expire: ^7%s %d level 1 admin(s) older than %d days\n", +    ( confirm ) ? "expired" : "listed", +    5 - max, g_adminExpireTime.integer ) ); +  if ( !confirm ) +    ADMBP( "^3!expire: ^7to make this permanent use '^2!expire confirm^7'\n" ); + +  ADMBP_end(); +  return qtrue; +} + +qboolean G_admin_putteam( gentity_t *ent, int skiparg ) +{ +  int pids[ MAX_CLIENTS ]; +  char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; +  gentity_t *vic; +  pTeam_t teamnum = PTE_NONE; +  char teamdesc[ 32 ] = {"spectators"}; +  char secs[ 7 ]; +  int seconds = 0; +  qboolean useDuration = qfalse; + +  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); +  G_SayArgv( 2 + skiparg, team, sizeof( team ) ); +  if( G_SayArgc() < 3 + skiparg ) +  { +    ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" ); +    return qfalse; +  } + +  if( G_ClientNumbersFromString( name, pids ) != 1 ) +  { +    G_MatchOnePlayer( pids, err, sizeof( err ) ); +    ADMP( va( "^3!putteam: ^7%s\n", err ) ); +    return qfalse; +  } +  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) +  { +    ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " +        " admin level than you\n" ); +    return qfalse; +  } +  vic = &g_entities[ pids[ 0 ] ]; + +  if ( vic->client->sess.invisible == qtrue ) +  { +    ADMP( "^3!putteam: ^7invisible players cannot join a team\n" ); +    return qfalse; +  } + +  switch( team[ 0 ] ) +  { +  case 'a': +    teamnum = PTE_ALIENS; +    Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) ); +    break; +  case 'h': +    teamnum = PTE_HUMANS; +    Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) ); +    break; +  case 's': +    teamnum = PTE_NONE; +    break; +  default: +    ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) ); +    return qfalse; +  } +  //duration code +  if( G_SayArgc() > 3 + skiparg ) { +    //can only lock players in spectator +    if ( teamnum != PTE_NONE ) +    { +      ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" ); +      return qfalse; +    } +    G_SayArgv( 3 + skiparg, secs, sizeof( secs ) ); +    seconds = G_admin_parse_time( secs ); +    useDuration = qtrue; +  } + +  if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE ) +  { +    ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) ); +    return qfalse; +  } + +  if( useDuration == qtrue && seconds > 0 ) { +    vic->client->pers.specExpires = level.time + ( seconds * 1000 ); +  } +  G_ChangeTeam( vic, teamnum ); + +  AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"", +          ( ent ) ? G_admin_adminPrintName( ent ) : "console", +          vic->client->pers.netname, teamdesc, +          ( seconds ) ? va( " for %i seconds", seconds ) : "" ) ); +  return qtrue; +} +  void G_admin_adminlog_cleanup( void )  {    int i; @@ -3469,7 +3598,6 @@ void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skipar        !Q_stricmp( command, "listplayers" ) ||        !Q_stricmp( command, "namelog" ) ||        !Q_stricmp( command, "showbans" ) || -      !Q_stricmp( command, "seen" ) ||        !Q_stricmp( command, "time" ) )      return; @@ -3722,6 +3850,261 @@ qboolean G_admin_adminlog( gentity_t *ent, int skiparg )    return qtrue;  } +void G_admin_tklog_cleanup( void ) +{ +  int i; + +  for( i = 0; i < MAX_ADMIN_TKLOGS && g_admin_tklog[ i ]; i++ ) +  { +    G_Free( g_admin_tklog[ i ] ); +    g_admin_tklog[ i ] = NULL; +  } + +  admin_tklog_index = 0; +} + +void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath ) +{ +  g_admin_tklog_t *tklog; +  int previous; +  int count = 1; + +  if( !attacker ) +    return; + +  previous = admin_tklog_index - 1; +  if( previous < 0 ) +    previous = MAX_ADMIN_TKLOGS - 1; + +  if( g_admin_tklog[ previous ] ) +    count = g_admin_tklog[ previous ]->id + 1; + +  if( g_admin_tklog[ admin_tklog_index ] ) +    tklog = g_admin_tklog[ admin_tklog_index ]; +  else +    tklog = G_Alloc( sizeof( g_admin_tklog_t ) ); + +  memset( tklog, 0, sizeof( g_admin_tklog_t ) ); +  tklog->id = count; +  tklog->time = level.time - level.startTime; +  Q_strncpyz( tklog->name, attacker->client->pers.netname, sizeof( tklog->name ) ); + +  if( victim ) +  { +    Q_strncpyz( tklog->victim, victim->client->pers.netname, sizeof( tklog->victim ) ); +    tklog->damage = victim->client->tkcredits[ attacker->s.number ]; +    tklog->value = victim->client->ps.stats[ STAT_MAX_HEALTH ]; +  } +  else +  { +    Q_strncpyz( tklog->victim, "^3BLEEDING", sizeof( tklog->victim ) ); +    tklog->damage = attacker->client->pers.statscounters.spreebleeds; +    tklog->value = g_bleedingSpree.integer * 100; +  } + +  tklog->team = attacker->client->ps.stats[ STAT_PTEAM ]; +  if( meansOfDeath == MOD_GRENADE ) +    tklog->weapon = WP_GRENADE; +  else if( tklog->team == PTE_HUMANS ) +    tklog->weapon = attacker->s.weapon; +  else +    tklog->weapon = attacker->client->ps.stats[ STAT_PCLASS ]; + +  g_admin_tklog[ admin_tklog_index ] = tklog; +  admin_tklog_index++; +  if( admin_tklog_index >= MAX_ADMIN_TKLOGS ) +    admin_tklog_index = 0; +} + +qboolean G_admin_tklog( gentity_t *ent, int skiparg ) +{ +  g_admin_tklog_t *results[ 10 ]; +  int result_index = 0; +  char *search_name = NULL; +  int index; +  int skip = 0; +  int skipped = 0; +  int checked = 0; +  char n1[ MAX_NAME_LENGTH ]; +  char fmt_name[ 16 ]; +  char argbuf[ 32 ]; +  char *weaponName; +  int name_length = 10; +  int max_id = 0; +  int i; +  qboolean match; + +  memset( results, 0, sizeof( results ) ); + +  index = admin_tklog_index; +  for( i = 0; i < 10; i++ ) +  { +    int prev; + +    prev = index - 1; +    if( prev < 0 ) +      prev = MAX_ADMIN_TKLOGS - 1; +    if( !g_admin_tklog[ prev ] ) +      break; +    if( g_admin_tklog[ prev ]->id > max_id ) +      max_id = g_admin_tklog[ prev ]->id; +    index = prev; +  } + +  if( G_SayArgc() > 1 + skiparg ) +  { +    G_SayArgv( 1 + skiparg, argbuf, sizeof( argbuf ) ); +    if( ( *argbuf >= '0' && *argbuf <= '9' ) || *argbuf == '-' ) +    { +      int id; + +      id = atoi( argbuf ); +      if( id < 0 ) +        id += ( max_id - 9 ); +      else if( id <= max_id - MAX_ADMIN_TKLOGS ) +        id = max_id - MAX_ADMIN_TKLOGS + 1; + +      if( id + 9 >= max_id ) +        id = max_id - 9; +      if( id < 1 ) +        id = 1; +      for( i = 0; i < MAX_ADMIN_TKLOGS; i++ ) +      { +        if( g_admin_tklog[ i ]->id == id ) +        { +          index = i; +          break; +        } +      } +    } +    else +    { +      search_name = argbuf; +    } + +    if( G_SayArgc() > 2 + skiparg && ( search_name ) ) +    { +      char skipbuf[ 4 ]; +      G_SayArgv( 2 + skiparg, skipbuf, sizeof( skipbuf ) ); +      skip = atoi( skipbuf ); +    } +  } + +  if( search_name ) +  { +    g_admin_tklog_t *result_swap[ 10 ]; + +    memset( &result_swap, 0, sizeof( result_swap ) ); + +    index = admin_tklog_index - 1; +    if( index < 0 ) +      index = MAX_ADMIN_TKLOGS - 1; + +    while( g_admin_tklog[ index ] && +      checked < MAX_ADMIN_TKLOGS && +      result_index < 10 ) +    { +      match = qfalse; + +      G_SanitiseString( g_admin_tklog[ index ]->name, n1, sizeof( n1 ) ); +      if( strstr( n1, search_name ) ) +        match = qtrue; + +      if( match && skip > 0 ) +      { +        match = qfalse; +        skip--; +        skipped++; +      } +      if( match ) +      { +        result_swap[ result_index ] = g_admin_tklog[ index ]; +        result_index++; +      } + +      checked++; +      index--; +      if( index < 0 ) +        index = MAX_ADMIN_TKLOGS - 1; +    } +    // search runs backwards, turn it around +    for( i = 0; i < result_index; i++ ) +      results[ i ] = result_swap[ result_index - i - 1 ]; +  } +  else +  { +    while( g_admin_tklog[ index ] && result_index < 10 ) +    { +      results[ result_index ] = g_admin_tklog[ index ]; +      result_index++; +      index++; +      if( index >= MAX_ADMIN_TKLOGS ) +        index = 0; +    } +  } + +  for( i = 0; results[ i ] && i < 10; i++ ) +  { +    int l; + +    G_DecolorString( results[ i ]->name, n1 ); +    l = strlen( n1 ); +    if( l > name_length ) +      name_length = l; +  } +  ADMBP_begin( ); +  for( i = 0; results[ i ] && i < 10; i++ ) +  { +    int t; + +    t = results[ i ]->time / 1000; + +    G_DecolorString( results[ i ]->name, n1 ); +    Com_sprintf( fmt_name, sizeof( fmt_name ), "%%%ds",  +      ( name_length + strlen( results[ i ]->name ) - strlen( n1 ) ) ); +    Com_sprintf( n1, sizeof( n1 ), fmt_name, results[ i ]->name ); + +    if( results[ i ]->team == PTE_HUMANS ) +      weaponName = BG_FindNameForWeapon( results[ i ]->weapon ); +    else +      weaponName = BG_FindNameForClassNum( results[ i ]->weapon ); + +    ADMBP( va( "^7%3d %3d:%02d %s^7 %3d / %3d %10s %s^7\n", +      results[ i ]->id, +      t / 60, t % 60, +      n1, +      results[ i ]->damage, +      results[ i ]->value, +      weaponName, +      results[ i ]->victim ) ); +  } +  if( search_name ) +  { +    ADMBP( va( "^3!tklog:^7 Showing %d matches for '%s^7'.", +      result_index, +      argbuf ) ); +    if( checked < MAX_ADMIN_TKLOGS && g_admin_tklog[ checked ] ) +      ADMBP( va( " run '!tklog %s^7 %d' to see more", +       argbuf, +       skipped + result_index ) ); +    ADMBP( "\n" ); +  } +  else if ( results[ 0 ] ) +  { +    ADMBP( va( "^3!tklog:^7 Showing %d - %d of %d.\n", +      results[ 0 ]->id, +      results[ result_index - 1 ]->id, +      max_id ) ); +  } +  else +  { +    ADMBP( "^3!tklog:^7 log is empty.\n" ); +  } +  ADMBP_end( ); + +  return qtrue; +} +  qboolean G_admin_map( gentity_t *ent, int skiparg )  {    char map[ MAX_QPATH ]; @@ -3947,6 +4330,9 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg )           case 'M':             result = "^6admin changed map";             break; +         case 'l': +           result = "^2layout vote"; +           break;           case 'D':             result = "^6admin loaded devmap";             break; @@ -3986,6 +4372,10 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg )      ptr = end;      count++;    } + +  if( g_nextMap.string[ 0 ] ) +    ADMBP( va( "^5NextMap override: %s\n", g_nextMap.string ) ); +    ADMBP_end( );    return qtrue; @@ -4013,7 +4403,7 @@ qboolean G_admin_demo( gentity_t *ent, int skiparg )  {    if( !ent )    { -    ADMP( "!demo: console can not use demo.\n" ); +    ADMP( "!demo: console can not use demo.\n");      return qfalse;    } @@ -4415,29 +4805,34 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg )    int start = 0;    qboolean numeric = qtrue;    int drawn = 0; -  int minlevel = 1; +  int minlevel = 0; +  char *direction = "(all levels)";    if( G_SayArgc() == 3 + skiparg )    {      G_SayArgv( 2 + skiparg, s, sizeof( s ) ); -    if( s[ 0 ] >= '0' && s[ 0] <= '9' ) +    if( ( s[ 0 ] >= '0' && s[ 0] <= '9' ) || s[ 0 ] == '-' )      {        minlevel = atoi( s ); -      if( minlevel < 1 )  -       minlevel = 1; +      if( minlevel > 0 ) +        direction = "or greater"; +      else if( minlevel < 0) +        direction = "or lesser";      }    }    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )    { -    if( g_admin_admins[ i ]->level >= minlevel ) +    if( ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) || +        ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) || +        ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) )        found++;    }    if( !found )    { -    if( minlevel > 1 ) +    if( minlevel != 0 )      { -      ADMP( va( "^3!listadmins: ^7no admins level %i or greater found\n", minlevel ) ); +      ADMP( va( "^3!listadmins: ^7no admins level %i %s found\n", minlevel, direction ) );      }      else      { @@ -4476,27 +4871,28 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg )    {      if( drawn <= 20 )      { -      ADMP( va( "^3!listadmins:^7 found %d admins level %i or greater matching '%s^7'\n", -        drawn, minlevel, search ) ); +      ADMP( va( "^3!listadmins:^7 found %d admins level %i %s matching '%s^7'\n", +        drawn, minlevel, direction, search ) );      }      else      { -      ADMP( va( "^3!listadmins:^7 found >20 admins level %i or greater matching '%s^7. Try a more narrow search.'\n", -        minlevel, search ) ); +      ADMP( va( "^3!listadmins:^7 found >20 admins level %i %s matching '%s^7. Try a more narrow search.'\n", +        minlevel, direction, search ) );      }    }    else    {      ADMBP_begin(); -    ADMBP( va( "^3!listadmins:^7 showing admins level %i or greater %d - %d of %d.  ", +    ADMBP( va( "^3!listadmins:^7 showing admins level %i %s %d - %d of %d.  ",        minlevel, +      direction,        ( found ) ? ( start + 1 ) : 0,        ( ( start + MAX_ADMIN_LISTITEMS ) > found ) ?         found : ( start + MAX_ADMIN_LISTITEMS ),        found ) );      if( ( start + MAX_ADMIN_LISTITEMS ) < found )      { -      if( minlevel > 1) +      if( minlevel  != 0 )        {          ADMBP( va( "run '!listadmins %d %d' to see more",            ( start + MAX_ADMIN_LISTITEMS + 1 ), minlevel ) ); @@ -4564,27 +4960,29 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )    char lname[ MAX_NAME_LENGTH ];    char lname2[ MAX_NAME_LENGTH ];    char guid_stub[ 9 ]; -  char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], misc[ 2 ]; +  char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], immune[ 2 ];    int l;    char lname_fmt[ 5 ]; +  char karma[ 8 ]; +  //get amount of invisible players    for( i = 0; i < level.maxclients; i++ ) {      p = &level.clients[ i ];      if ( p->sess.invisible == qtrue )        invisiblePlayers++;    } -   +    ADMBP_begin(); -  ADMBP( va( "^3!listplayers^7: %i players connected:\n", +  ADMBP( va( "^3!listplayers^7: %d players connected:\n",      level.numConnectedClients - invisiblePlayers ) );    for( i = 0; i < level.maxclients; i++ )    {      p = &level.clients[ i ]; -     +      // Ignore invisible players      if ( p->sess.invisible == qtrue )        continue; -     +      Q_strncpyz( t, "S", sizeof( t ) );      Q_strncpyz( c, S_COLOR_YELLOW, sizeof( c ) );      if( p->pers.teamSelection == PTE_HUMANS ) @@ -4613,6 +5011,10 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )      guid_stub[ j ] = '\0';      muted[ 0 ] = '\0'; +    if( G_admin_permission( &g_entities[ i ], ADMF_NO_VOTE ) ) +    { +      Q_strncpyz( muted, "V", sizeof( muted ) ); +    }      if( p->pers.muted )      {        Q_strncpyz( muted, "M", sizeof( muted ) ); @@ -4641,15 +5043,16 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )          Q_strncpyz( dbuilder, "D", sizeof( dbuilder ) );        }      } - -    misc[ 0 ] = '\0'; +    immune[ 0 ] = '\0';      if( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) )      { -      // use Misc slot for Immunity player status -      Q_strncpyz( misc, "I", sizeof( misc ) ); -    } else if( p->pers.paused ) { -      // use Misc slot for paused player status -      Q_strncpyz( misc, "P", sizeof( misc ) ); +      Q_strncpyz( immune, "I", sizeof( immune ) ); +    } + +    if( p->pers.paused ) +    { +      // use immunity slot for paused player status +      Q_strncpyz( immune, "L", sizeof( immune ) );      }      l = 0; @@ -4693,19 +5096,25 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )      } +    if( g_karma.integer ) +      Com_sprintf( karma, sizeof( karma ), " %4i", p->pers.karma / 1000 ); +    else +      karma[ 0 ] = '\0'; +       if( G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) { -      ADMBP( va( "%2i %s%s^7 %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n", +      ADMBP( va( "%2i %s%s^7%s %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n",                 i,                 c,                 t, +               karma,                 l,                 ( *lname ) ? lname2 : "",                  guid_stub, +               immune,                 muted,                 dbuilder,                 denied, -               misc,                 p->pers.netname,                 ( *n ) ? "(a.k.a. " : "",                 n, @@ -4714,10 +5123,11 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )       }       else       { -      ADMBP( va( "%2i %s%s^7 ^1%1s%1s%1s^7 %s^7\n", +      ADMBP( va( "%2i %s%s^7%s ^1%1s%1s%1s^7 %s^7\n",                 i,                 c,                 t, +               karma,                 muted,                 dbuilder,                 denied, @@ -4870,52 +5280,6 @@ qboolean G_admin_listrotation( gentity_t *ent, int skiparg )              statusColor = 7;              status = "vote";            } -        } else if( !Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*" ) ) -        { -          char slotMap[ 64 ]; -          int lineLen = 0; -          int k; - -          trap_Cvar_VariableStringBuffer( "mapname", slotMap, sizeof( slotMap ) ); -          mapnames[ 0 ] = '\0'; -          for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) -          { -            char *thisMap; -            int mc = 7; - -            if( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].lhs != MCV_SELECTEDRANDOM ) -              continue; - -            thisMap = mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest; -            lineLen += strlen( thisMap ) + 1; - -            if( currentMap == j && !Q_stricmp( thisMap, slotMap ) ) -              mc = 3; -            Q_strcat( mapnames, sizeof( mapnames ), va( "^7%s%s^%i%s", -                      ( k ) ? ", " : "", -                        ( lineLen > 50 ) ? "\n                  " : "", -                          mc, thisMap ) ); -            if( lineLen > 50 ) -              lineLen = strlen( thisMap ) + 2; -            else -              lineLen++; -          } - -          if( currentMap == j ) -          { -            statusColor = 3; -            status = "current slot"; -          } -          else if( !k ) -          { -            statusColor = 1; -            status = "empty Random"; -          } -          else -          { -            statusColor = 7; -            status = "Random"; -          }          }          else if ( currentMap == j )          { @@ -4980,16 +5344,13 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )    char name_match[ MAX_NAME_LENGTH ] = {""};    int show_count = 0;    qboolean subnetfilter = qfalse; +  int line_color; +  int dummy;    t = trap_RealTime( NULL );    for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )    { -    if( g_admin_bans[ i ]->expires != 0 -      && ( g_admin_bans[ i ]->expires - t ) < 1 ) -    { -      continue; -    }      found++;    } @@ -5067,7 +5428,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )          else          {            int mask = -1; -          int dummy;            int scanflen = 0;            scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask );            if( scanflen == 5 && mask < 32 ) @@ -5114,7 +5474,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )        else        {          int mask = -1; -        int dummy;          int scanflen = 0;          scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask );          if( scanflen != 5 || mask >= 32 ) @@ -5141,42 +5500,54 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )        made++;      } -    if( g_admin_bans[ i ]->expires != 0 -        && ( g_admin_bans[ i ]->expires - t ) < 1 ) +    if( g_admin_bans[ i ]->expires == 0 || +        g_admin_bans[ i ]->expires > t )      { -      Com_sprintf( duration, sizeof( duration ), "^1*EXPIRED*^7" ); -    } else {        secs = ( g_admin_bans[ i ]->expires - t );        G_admin_duration( secs, duration, sizeof( duration ) ); +      if( sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &dummy ) != 4 ) +        line_color = 1; +      else if( g_admin_bans[ i ]->suspend > t ) +        line_color = 5; +      else +        line_color = 7; +    } +    else +    { +      Q_strncpyz( duration, "expired", sizeof( duration ) ); +      line_color = 5;      }      suspended[ 0 ] = '\0';      if( g_admin_bans[ i ]->suspend > t )      {        G_admin_duration( g_admin_bans[ i ]->suspend - t, sduration, sizeof( sduration ) ); -      Com_sprintf( suspended, sizeof( suspended ), "^3*SUSPENDED*^7 for %s^7", +      Com_sprintf( suspended, sizeof( suspended ), "^5     | - SUSPENDED for %s\n",          sduration );      }      G_DecolorString( g_admin_bans[ i ]->name, n1 ); -    Com_sprintf( name_fmt, sizeof( name_fmt ), "%%%is", +    Com_sprintf( name_fmt, sizeof( name_fmt ), "%%-%is",        ( max_name + strlen( g_admin_bans[ i ]->name ) - strlen( n1 ) ) );      Com_sprintf( n1, sizeof( n1 ), name_fmt, g_admin_bans[ i ]->name );       G_DecolorString( g_admin_bans[ i ]->banner, n2 ); -    Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%%is", +    Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%-%is",        ( max_banner + strlen( g_admin_bans[ i ]->banner ) - strlen( n2 ) ) ); -    Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner ); +    Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner );       bannerslevel = g_admin_bans[ i ]->bannerlevel; -    ADMBP( va( "%4i %s^7 %-15s %-8s %-10s\n     |  %-15s^7 Level:%2i\n     |  %s\n     \\__ %s\n", +    ADMBP( va( "^%i%4i ^7%s^%i %-15s %-8s ^7%s^7 %2i^%i %-10s\n%s^5     \\__ %s\n", +             line_color,               ( i + 1 ),               n1, +             line_color,               g_admin_bans[ i ]->ip,               date, -             duration,               n2,               bannerslevel, +             line_color, +             duration,               suspended,               g_admin_bans[ i ]->reason ) ); @@ -5209,8 +5580,9 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )    if( ( start + MAX_ADMIN_SHOWBANS ) < found )    { -    ADMBP( va( "run !showbans %d %s to see more", +    ADMBP( va( "run !showbans %d%s%s to see more",               ( start + MAX_ADMIN_SHOWBANS + 1 ), +             (filter[0]) ? " " : "",               (filter[0]) ? filter : "" ) );    }    ADMBP( "\n" ); @@ -5221,8 +5593,7 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )  qboolean G_admin_help( gentity_t *ent, int skiparg )  {    int i; -  int count = 0; -  int commandsPerLine = 6; +  char additional[ MAX_STRING_CHARS ] = "";    if( G_SayArgc() < 2 + skiparg )    { @@ -5259,102 +5630,39 @@ qboolean G_admin_help( gentity_t *ent, int skiparg )          j = 0;        }      } - +     +    if( ent && g_markDeconstruct.integer == 2 ) +      strcat( additional, " /mark" ); +    if( ent ) +      strcat( additional, " /builder /say_area" ); +    if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) +      strcat( additional, " /a /say_admins" ); +    if( g_privateMessages.integer ) +      strcat( additional, " /m" ); +    if( ent && g_actionPrefix.string[0] ) +      strcat( additional, " /me /mt /me_team" ); +    if( ent && g_myStats.integer ) +      strcat( additional, " /mystats" ); +    if( ent && g_teamStatus.integer ) +      strcat( additional, " /teamstatus" ); +    if( ent && ent->client ) +    { +      if( ent->client->pers.designatedBuilder ) +      { +        strcat( additional, " /protect /resign" ); +      } +    } +    if( ent && g_allowShare.integer ) +      strcat( additional, " /share /donate" ); +          if( count )        ADMBP( "\n" );      ADMBP( va( "^3!help: ^7%i available commands\n", count ) );      ADMBP( "run !help [^3command^7] for help with a specific command.\n" ); -    ADMBP( "The following non-standard /commands may also be available to you: \n" ); -    count = 1; - -    if( ent && g_AllStats.integer ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "allstats" ) ); -      count++; -    } -    if ( ent ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "builder" ) ); -      count++; -    } -    if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "callvote" ) ); -      count++; -    } -    if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "callteamvote" ) ); -      count++; -    } -    if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "donate" ) ); -      count++; -    } -    if( g_privateMessages.integer ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "m" ) ); -      count++; -    } -    if( ent && g_markDeconstruct.integer == 2 && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "mark" ) ); -      count++; -    } -    if( ent && g_actionPrefix.string[0] ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "me" ) ); -      count++; -    } -    if( ent && g_actionPrefix.string[0] ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "me_team" ) ); -      count++; -    } -    if( ent && g_actionPrefix.string[0] && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "mt" ) ); -      count++; -    } -    if( ent && g_myStats.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "mystats" ) ); -      count++; -    } -    if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "protect" ) ); -      count++; -    } -    if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "resign" ) ); -      count++; -    } -    if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "say_admins" ) ); -      count++; -    } -    if( ent && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "say_area" ) ); -      count++; -    } -    if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "share" ) ); -      count++; -    } -    if( ent && g_teamStatus.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { -      if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); -      ADMBP( va( "^5/%-12s", "teamstatus" ) ); -      count++; -    } -    ADMBP( "\n" ); +    ADMBP( va( "\nThe following non-standard /commands may also be available to you: \n^3%s\n", +      additional ) );      ADMBP_end(); - +          return qtrue;    }    else @@ -5490,6 +5798,10 @@ qboolean G_admin_cancelvote( gentity_t *ent, int skiparg )      ADMP( "^3!cancelvote^7: no vote in progress\n" );      return qfalse;    } +  if( !Q_strncmp( level.voteDisplayString, "Extend", 6 ) && +      level.extend_vote_count > 0 ) +    level.extend_vote_count--; +    level.voteNo = level.numConnectedClients;    level.voteYes = 0;    CheckVote( ); @@ -5679,11 +5991,81 @@ qboolean G_admin_pause( gentity_t *ent, int skiparg )              vic->client->pers.netname,              ( ent ) ? ent->client->pers.netname : "console" ) );      } -    ClientUserinfoChanged( pids[ i ], qfalse );    }    return qtrue;  } +qboolean G_admin_practice( gentity_t *ent, int skiparg ) +{ +  char clantag[ MAX_NAME_LENGTH ]; +  char mapsarg[ 8 ]; +  int maps; + +  if( G_SayArgc() < 2 + skiparg ) +  { +    if( g_practiceCount.integer ) +    { +      AP( va( "print \"^3practice:^7 practice mode is in effect for the next %d maps\n\"", +        g_practiceCount.integer ) ); +      ADMP( va( "^3!practice: ^7practice mode set to %s^7 for next %d maps\n", +        g_practiceText.string, g_practiceCount.integer ) ); +    } +    else +    { +      ADMP( "^3!practice: ^7practice mode is off\n" ); +    } +    return qfalse; +  } + +  G_SayArgv( 1 + skiparg, clantag, sizeof( clantag ) ); + +  if( G_SayArgc() < 3 + skiparg ) +  { +    if( !Q_stricmp( clantag, "off" ) ) +    { +      if( g_practiceCount.integer ) +      { +        trap_Cvar_Set( "g_practiceCount", "0" ); +        AP( va ("print \"^3!practice: ^7 practice mode turned off by %s^7\n\"", +          ( ent ) ? ent->client->pers.netname : "console" ) ); +        ADMP( "^3!practice: ^7practice mode set to off\n" ); +        return qtrue; +      } +      else +      { +        ADMP( "^3!practice: ^7practice mode already off\n" ); +      } +    } +    else +    { +      ADMP( "^3!practice: ^7usage: practice [clan tag] [map count]\n" ); +    } +    return qfalse; +  } +  G_SayArgv( 2 + skiparg, mapsarg, sizeof( mapsarg ) ); +  maps = atoi( mapsarg ); + +  if( !clantag[ 0 ] ) +  { +    ADMP( "^3!practice: ^7no clan tag specified\n" ); +    return qfalse; +  } +  if( maps < 1 || maps > 8  ) +  { +    ADMP( "^3!practice: ^7map count must be between 1 and 8\n" ); +    return qfalse; +  } + +  trap_Cvar_Set( "g_practiceText", clantag ); +  trap_Cvar_Set( "g_practiceCount", va( "%d", maps ) ); + +  AP( va( "print \"^3practice:^7 %s^7 has activated practice mode for the next %d maps\n\"", +      ( ent ) ? ent->client->pers.netname : "console", +      maps ) ); + +  return qtrue; +} +  qboolean G_admin_spec999( gentity_t *ent, int skiparg )  {    int i; @@ -5711,13 +6093,47 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg )  qboolean G_admin_register(gentity_t *ent, int skiparg ){    int level = 0; +  int max; +  char buffer [ 64 ];    if( !ent ) return qtrue;    level = G_admin_level(ent); -  if( level == 0 ) -   level = 1; +  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( max >= 0 && level >= 0 && level <= g_adminRegisterLevel.integer ) +        level = max; +    } +  } +  else +  { +    max = 1; +  } + +  if( level >= 0 && level < max ) +    level = max;    if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) )    { @@ -5886,140 +6302,6 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg )    return qtrue;  } -qboolean G_admin_nobuild( gentity_t *ent, int skiparg ) -{ -  char buffer[ MAX_STRING_CHARS ]; -  int  i, tmp; -   -  if( G_SayArgc() < 2 + skiparg ) -  { -    ADMP( "^3!nobuild: ^7usage: !nobuild (^5enable / disable / log / remove / save^7)\n" ); -    return qfalse; -  } -   -  G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) ); -   -  if( !Q_stricmp( buffer, "enable" ) || !Q_stricmp( buffer, "Enable" ) ) -  { -  	if( G_SayArgc() < 4 + skiparg ) -  	{ -		ADMP( "^3!nobuild: ^7usage: !nobuild enable (^5area^7) (^5height^7)\n" ); -		return qfalse; -  	} -	 -  	if( !level.noBuilding ) -  	{ -	 -  		level.noBuilding = qtrue; -		 -		// Grab and set the area -		G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); -		tmp = atoi( buffer ); -		level.nbArea = tmp; -		 -		// Grab and set the height -		G_SayArgv( 3 + skiparg, buffer, sizeof( buffer ) ); -		tmp = atoi( buffer ); -		level.nbHeight = tmp; -		 -		ADMP( "^3!nobuild: ^7nobuild is now enabled, please place a buildable to spawn a marker\n" ); -		return qtrue; -  	} -	else -	{ -		ADMP( "^3!nobuild: ^7nobuild is already enabled. type !nobuild disable to disable nobuild mode.\n" ); -		return qfalse; -	} -  } -   -  if( !Q_stricmp( buffer, "disable" ) || !Q_stricmp( buffer, "Disable" ) ) -  { -  	if( level.noBuilding ) -  	{ -  		level.noBuilding = qfalse; -		level.nbArea = 0.0f; -		level.nbHeight = 0.0f; -		ADMP( "^3!nobuild: ^7nobuild is now disabled\n" ); -		return qtrue; -  	} -	else -	{ -		ADMP( "^3!nobuild: ^7nobuild is disabled. type !nobuild enable (^5area^7) (^5height^7) to enable nobuild mode.\n" ); -		return qfalse; -	} -  } -   -  if( !Q_stricmp( buffer, "log" ) || !Q_stricmp( buffer, "Log" ) ) -  { - 	ADMBP_begin(); -	 -	tmp = 0; -  	for( i = 0; i < MAX_GENTITIES; i++ ) -  	{ -  		if( level.nbMarkers[ i ].Marker == NULL ) -  		continue; -		 -		// This will display a start at 1, and not 0. This is to stop confusion by server ops -		ADMBP( va( "^7#%i at origin (^5%f %f %f^7)^7\n", i + 1, level.nbMarkers[ i ].Origin[0], level.nbMarkers[ i ].Origin[1], level.nbMarkers[ i ].Origin[2] ) ); -		tmp++; - 	} -	ADMBP( va( "^3!nobuild:^7 displaying %i marker(s)\n", tmp ) ); -	 -  	ADMBP_end(); -	return qtrue; -  } -   -  if( !Q_stricmp( buffer, "remove" ) || !Q_stricmp( buffer, "Remove" ) ) -  { - 	if( G_SayArgc() < 3 + skiparg ) -  	{ -		ADMP( "^3!nobuild: ^7usage: !nobuild remove (^5marker #^7)\n" ); -		return qfalse; -  	} -	 -	G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); -	tmp = atoi( buffer ) - 1; -	// ^ that was to work with the display number... -	 -	if( level.nbMarkers[ tmp ].Marker == NULL ) -	{ -		ADMP( "^3!nobuild: ^7that is not a valid marker number\n" ); -		return qfalse; -	} -	// Donno why im doing this, but lets clear this... -	level.nbMarkers[ tmp ].Marker->noBuild.isNB = qfalse; -	level.nbMarkers[ tmp ].Marker->noBuild.Area = 0.0f; -	level.nbMarkers[ tmp ].Marker->noBuild.Height = 0.0f; -	 -	// Free the entitiy and null it out... -	G_FreeEntity( level.nbMarkers[ tmp ].Marker ); -	level.nbMarkers[ tmp ].Marker = NULL; -	 -	// That is to work with the display number... -	ADMP( va( "^3!nobuild:^7 marker %i has been removed\n", tmp + 1 ) ); -	return qtrue; -  } -   -  if( !Q_stricmp( buffer, "save" ) || !Q_stricmp( buffer, "Save" ) ) -  { -  	int  i, tmp; -	 - 	G_NobuildSave( ); -	 -	tmp = 0; -  	for( i = 0; i < MAX_GENTITIES; i++ ) -  	{ -  		if( level.nbMarkers[ i ].Marker == NULL ) -  		continue; -		 -		tmp++; - 	} -	ADMP( va( "^3!nobuild:^7 %i nobuild markers have been saved\n", tmp ) ); -	return qtrue; -  } -  return qfalse; -} -  qboolean G_admin_nextmap( gentity_t *ent, int skiparg )  {    AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"", @@ -6244,6 +6526,441 @@ qboolean G_admin_designate( gentity_t *ent, int skiparg )    return qtrue;  } +static int SortFlags( const void *pa, const void *pb ) +{ +  char *a = (char *)pa; +  char *b = (char *)pb; + +  if( *a == '-' || *a == '+' ) +    a++; +  if( *b == '-' || *b == '+' ) +    b++; +  return strcmp(a, b); +} + +#define MAX_USER_FLAGS 200 +const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear, +                               char *newflags, int size ) +{ +  char *token, *token_p; +  char *key; +  char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; +  char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; +  char allflag[ MAX_ADMIN_FLAG_LEN ]; +  char newflag[ MAX_ADMIN_FLAG_LEN ]; +  int head_count = 0; +  int tail_count = 0; +  qboolean flagset = qfalse; +  int i; + +  if( !flag[ 0 ] ) +  { +    return "invalid admin flag"; +  } + +  allflag[ 0 ] = '\0'; +  token_p = oldflags; +  while( *( token = COM_Parse( &token_p ) ) ) +  { +    key = token; +    if( *key == '-' || *key == '+' ) +      key++; + +    if( !strcmp( key, flag ) ) +    { +      if( flagset ) +        continue; +      flagset = qtrue; +      if( clear ) +      { +        // clearing ALLFlAGS will result in clearing any following flags +        if( !strcmp( key, ADMF_ALLFLAGS ) ) +          break; +        else +          continue; +      } +      Com_sprintf( newflag, sizeof( newflag ), "%s%s", +        ( add ) ? "+" : "-", key ); +    } +    else +    { +      Q_strncpyz( newflag, token, sizeof( newflag ) ); +    } + +    if( !strcmp( key, ADMF_ALLFLAGS ) ) +    { +      if( !allflag[ 0 ] ) +        Q_strncpyz( allflag, newflag, sizeof( allflag ) ); +      continue; +    } + +    if( !allflag[ 0 ] ) +    { +      if( head_count < MAX_USER_FLAGS ) +      { +        Q_strncpyz( head_flags[ head_count ], newflag, +                    sizeof( head_flags[ head_count ] ) ); +        head_count++; +      } +    } +    else +    { +      if( tail_count < MAX_USER_FLAGS ) +      { +        Q_strncpyz( tail_flags[ tail_count ], newflag, +                    sizeof( tail_flags[ tail_count ] ) ); +        tail_count++; +      } +    } +  } + +  if( !flagset && !clear ) +  { +    if( !strcmp( flag, ADMF_ALLFLAGS ) ) +    { +      Com_sprintf( allflag, sizeof( allflag ), "%s%s", +        ( add ) ? "" : "-", ADMF_ALLFLAGS ); +    } +    else if( !allflag[ 0 ] ) +    { +      if( head_count < MAX_USER_FLAGS ) +      { +        Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ), +          "%s%s", ( add ) ? "" : "-", flag ); +        head_count++; +      } +    } +    else +    { +      if( tail_count < MAX_USER_FLAGS ) +      { +        Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ), +          "%s%s", ( add ) ? "+" : "-", flag ); +        tail_count++; +      } +    } +  } + +  qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags ); +  qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags ); + +  // rebuild flags +  newflags[ 0 ] = '\0'; +  for( i = 0; i < head_count; i++ ) +  { +    Q_strcat( newflags, size, +              va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) ); +  } +  if( allflag[ 0 ] ) +  { +    Q_strcat( newflags, size, +      va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) ); + +    for( i = 0; i < tail_count; i++ ) +    { +      Q_strcat( newflags, size, +                va( " %s", tail_flags[ i ] ) ); +    } +  } + +  return NULL; +} + + +typedef struct { +  char *flag; +  char *description; +} AdminFlagListEntry_t; +static AdminFlagListEntry_t adminFlagList[] = +{ +  { ADMF_ACTIVITY,             "inactivity rules do not apply" }, +  { ADMF_ADMINCHAT,            "can see and use admin chat" }, +  { ADMF_ALLFLAGS,             "has all flags and can use any command" }, +  { ADMF_BAN_IMMUNITY,         "immune from IP bans" }, +  { ADMF_CAN_PERM_BAN,         "can permanently ban players" }, +  { ADMF_DBUILDER,             "permanent designated builder" }, +  { ADMF_FORCETEAMCHANGE,      "team balance rules do not apply" }, +  { ADMF_INCOGNITO,            "does not show as admin in !listplayers" }, +  { ADMF_IMMUNITY,             "cannot be vote kicked or muted" }, +  { ADMF_IMMUTABLE,            "admin commands cannot be used on them" }, +  { ADMF_NOCENSORFLOOD,        "no flood protection" }, +  { ADMF_NO_VOTE_LIMIT,        "vote limitations do not apply" }, +  { ADMF_SEESFULLLISTPLAYERS,  "sees all info in !listplayers" }, +  { ADMF_SPEC_ALLCHAT,         "can see team chat as spectator" }, +  { ADMF_ADMINSTEALTH,         "uses admin stealth" }, +  { ADMF_TEAMCHANGEFREE,       "keeps credits on team switch" }, +  { ADMF_TEAMCHAT_CMD,         "can run commands from team chat" }, +  { ADMF_UNACCOUNTABLE,        "does not need to specify reason for kick/ban" }, +  { ADMF_NO_CHAT,              "can not talk" }, +  { ADMF_NO_VOTE,              "can not call votes" } +}; +static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] ); + +#define MAX_LISTCOMMANDS 128 +qboolean G_admin_flaglist( gentity_t *ent, int skiparg ) +{ +  qboolean shown[ MAX_LISTCOMMANDS ]; +  int i, j; +  int count = 0; + +  ADMBP_begin(); + +  ADMBP( "^3Ability flags:\n" ); + +  for( i = 0; i < adminNumFlags; i++ ) +  { +    ADMBP( va( "  %s%-20s ^7%s\n", +      ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1", +      adminFlagList[ i ].flag, +      adminFlagList[ i ].description ) ); +  } + +  ADMBP( "^3Command flags:\n" ); + +  memset( shown, 0, sizeof( shown ) ); +  for( i = 0; i < adminNumCmds; i++ ) +  { +    if( i < MAX_LISTCOMMANDS && shown[ i ] ) +      continue; +    ADMBP( va( "  ^5%-20s^7", g_admin_cmds[ i ].flag ) ); +    for( j = i; j < adminNumCmds; j++ ) +    { +      if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) ) +      { +        ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) ); +        if( j < MAX_LISTCOMMANDS ) +          shown[ j ] = qtrue; +      } +    } +    ADMBP( "\n" ); +    count++; +  } + +  ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n", +    adminNumFlags, count ) ); + +  ADMBP_end(); + +  return qtrue; +} + +qboolean G_admin_flag( gentity_t *ent, int skiparg ) +{ +  char command[ MAX_ADMIN_CMD_LEN ], *cmd; +  char name[ MAX_NAME_LENGTH ]; +  char flagbuf[ MAX_ADMIN_FLAG_LEN ]; +  char *flag; +  int id; +  char adminname[ MAX_NAME_LENGTH ] = {""}; +  const char *result; +  qboolean add = qtrue; +  qboolean clear = qfalse; +  int admin_level = -1; +  int i, level; + +  G_SayArgv( skiparg, command, sizeof( command ) ); +  cmd = command; +  if( *cmd == '!' ) +    cmd++; + +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); +    return qfalse; +  } + +  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); +  if( name[ 0 ] == '*' ) +  { +    if( ent ) +    { +      ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) ); +      return qfalse; +    } +    id = atoi( name + 1 ); +    for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) +    { +      if( g_admin_levels[ i ]->level == id ) +      { +        admin_level = i; +        break; +      } +    } +    if( admin_level < 0 ) +    { +      ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) ); +      return qfalse; +    } +    Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id ); +  } +  else +  { +    id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) ); +    if( id < 0 ) +      return qfalse; + +    if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) +    { +      ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) ); +      return qfalse; +    } +  } + +  if( G_SayArgc() < 3 + skiparg ) +  { +    flag = ""; +    level = 0; +    if( admin_level < 0 ) +    { +      for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) +      { +        if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level ) +        { +          flag = g_admin_levels[ i ]->flags; +          level = g_admin_admins[ id ]->level; +          break; +        } +      } +      ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", +        cmd, adminname, g_admin_admins[ id ]->flags) ); +    } +    else +    { +      flag = g_admin_levels[ admin_level ]->flags; +      level = g_admin_levels[ admin_level ]->level; +    } +    ADMP( va( "^3%s:^7  level %d flags are '%s'\n", +      cmd, level, flag ) ); + +    return qtrue; +  } + +  G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); +  flag = flagbuf; +  if( flag[ 0 ] == '-' || flag[ 0 ] == '+' ) +  { +    add = ( flag[ 0 ] == '+' ); +    flag++; +  } +  if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) +  { +    ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); +    return qfalse; +  } +  if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) ) +  { +    ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); +    return qfalse; +  } + +  if( !Q_stricmp( cmd, "unflag" ) ) +  { +    clear = qtrue; +  } + +  if( admin_level < 0 ) +  { +    result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear, +                                g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); +  } +  else +  { +    result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear, +                                g_admin_levels[ admin_level ]->flags, +                                sizeof( g_admin_levels[ admin_level ]->flags ) ); +  } +  if( result ) +  { +    ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n", +      flag, result ) ); +    return qfalse; +  } + +  if( !Q_stricmp( cmd, "flag" ) ) +  { +    G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n", +      cmd, adminname, +      ( add ) ? "given" : "denied", +      flag, +      ( ent ) ? ent->client->pers.netname : "console" ); +  } +  else +  { +    G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n", +      cmd, flag, adminname, +      ( ent ) ? ent->client->pers.netname : "console" ); +  } + +  if( !g_admin.string[ 0 ] ) +    ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " +      "to a file\n", cmd ) ); +  else +    admin_writeconfig(); + +  return qtrue; +} + +qboolean G_admin_immunity( gentity_t *ent, int skiparg ) +{ +  char command[ MAX_ADMIN_CMD_LEN ]; +  char *cmd, *action; +  int id; +  char adminname[ MAX_NAME_LENGTH ] = {""}; +  const char *result; + +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( "^3!immunity: ^7usage: immunity [+|-]slot#\n" ); +    return qfalse; +  } +  G_SayArgv( 1 + skiparg, command, sizeof( command ) ); +  cmd = command; +  action = command; +  if( *cmd == '+' || *cmd == '-' ) cmd++; + +  id = G_admin_find_admin_slot( ent, cmd, "immunity", adminname, sizeof( adminname ) ); +  if( id < 0 ) +    return qfalse; + +  if( *action != '+' && *action != '-' ) +  { +    ADMP( va( "^3immunity:^7 ban immunity for %s^7 is %s, prepend + or - to the slot number to change.\n", +      adminname, +      ( G_admin_permission_guid( g_admin_admins[ id ]->guid, ADMF_BAN_IMMUNITY ) ) ? "on" : "off" ) ); +    return qfalse; +  } + +  result = G_admin_user_flag( g_admin_admins[ id ]->flags, +                              ADMF_BAN_IMMUNITY, qtrue, ( *action != '+' ), +                              g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); +  if( result ) +  { +    ADMP( va( "^3!immunity: ^7an error occured setting flag, %s\n", result ) ); +    return qfalse; +  } + +  if( *action == '+' ) +  { +    AP( va( +      "print \"^3!immunity: ^7%s^7 was given ban immunity by %s\n\"", +      adminname, ( ent ) ? ent->client->pers.netname : "console" ) ); +  } +  else +  { +    AP( va( +      "print \"^3!immunity: ^7ban immunity for %s^7 removed by %s\n\"", +      adminname, ( ent ) ? ent->client->pers.netname : "console" ) ); +  } + +  if( !g_admin.string[ 0 ] ) +    ADMP( "^3!immunity: ^7WARNING g_admin not set, not saving admin record " +      "to a file\n" ); +  else +    admin_writeconfig(); + +  return qtrue; +} +   //!Warn by Gate (Daniel Evans)   qboolean G_admin_warn( gentity_t *ent, int skiparg )  {//mostly copy and paste with the proper lines altered from !mute and !kick @@ -6284,9 +7001,47 @@ qboolean G_admin_warn( gentity_t *ent, int skiparg )              ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );//console announcement    return qtrue;  } + +qboolean G_admin_bring( gentity_t *ent, int skiparg ) +{ +  int pids[ MAX_CLIENTS ]; +  char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; +  gentity_t *vic; + +  if( !ent ) +  { +    ADMP( "^3!bring: ^7console cannot use this command\n" ); +    return qfalse; +  } +  if( ent->client->pers.teamSelection != PTE_NONE ) +  { +    ADMP( "^3!bring: ^7you can only use this command from spectator\n" ); +    return qfalse; +  } +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( "^3!bring: ^7usage: !bring [name|slot#]\n" ); +    return qfalse; +  } + +  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); +  if( G_ClientNumbersFromString( name, pids ) != 1 ) +  { +    G_MatchOnePlayer( pids, err, sizeof( err ) ); +    ADMP( va( "^3!bring: ^7%s\n", err ) ); +    return qfalse; +  } + +  vic = &g_entities[ pids[ 0 ] ]; +  VectorCopy( vic->client->ps.origin, ent->client->ps.origin ); + +  return qtrue; +}  qboolean G_admin_putmespec( gentity_t *ent, int skiparg )  { +  int cs_offset; +    if( !ent )    {      ADMP( "!specme: sorry, but console isn't allowed on the spectators team\n"); @@ -6302,6 +7057,15 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg )    if(ent->client->pers.teamSelection == PTE_NONE)      return qfalse; +  if( ent->client->pers.teamSelection == PTE_ALIENS ) +    cs_offset = 1; +  else +    cs_offset = 0; +  if( level.teamVoteTime[ cs_offset ] ) +  { +    trap_SendServerCommand( ent-g_entities, "print \"Can not leave team during a team vote\n\"" ); +    return qfalse; +  }      //guard against build timer exploit    if( ent->client->pers.teamSelection != PTE_NONE && ent->client->sess.sessionTeam != TEAM_SPECTATOR &&        ( ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 || @@ -6315,10 +7079,119 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg )    }    G_ChangeTeam( ent, PTE_NONE ); + +  // check for silent '!specme s' - requires !kick permission +  if( G_SayArgc() > 1 + skiparg ) +  { +    char arg[ 2 ]; + +    G_SayArgv( 1 + skiparg, arg, sizeof( arg ) ); +    if( ( arg[ 0 ] == 's' || arg[ 0 ] == 'S' ) && G_admin_permission( ent, "kick" ) ) +    { +      ADMP("^3!specme: ^7 You have silently joined the spectators\n"); +      return qtrue; +    } +  } +    AP( va("print \"^3!specme: ^7%s^7 decided to join the spectators\n\"", ent->client->pers.netname ) );    return qtrue;  } +qboolean G_admin_outlaw( gentity_t *ent, int skiparg ) +{ +  int pids[ MAX_CLIENTS ]; +  char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; +  char valuebuf[ 8 ]; +  gentity_t *vic; +  int points; +  qboolean activate = qtrue; + +  if( !g_bleedingSpree.integer ) +  { +    ADMP( "^3!outlaw: ^7bleeding sprees are disabled\n" ); +    return qfalse; +  } + +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( "^3!outlaw: ^7usage: !outlaw [name|slot#] (value)\n" ); +    return qfalse; +  } +  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); +  if( G_ClientNumbersFromString( name, pids ) != 1 ) +  { +    G_MatchOnePlayer( pids, err, sizeof( err ) ); +    ADMP( va( "^3!mute: ^7%s\n", err ) ); +    return qfalse; +  } +  vic = &g_entities[ pids[ 0 ] ]; + +  if( G_SayArgc() > 2 + skiparg ) +  { +    G_SayArgv( 2 + skiparg, valuebuf, sizeof( valuebuf ) ); +    if( valuebuf[ 0 ] == '?' ) +    { +      ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is %d\n", +        vic->client->pers.netname, +        vic->client->pers.statscounters.spreebleeds ) ); +      return qtrue; +    } +    if( valuebuf[ 0 ] == '+' || valuebuf[ 0 ] == '-' ) +    { +      activate = qfalse; +    } +    points = atoi( valuebuf ); +  } +  else +  { +    points = ( g_bleedingSpree.integer + 1 ) * 100; +  } + +  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) +  { +    ADMP( "^3!outlaw: ^7sorry, but your intended victim has a higher admin" +        " level than you\n" ); +    return qfalse; +  } +  if( points ) +  { +    vic->client->pers.statscounters.spreebleeds += points; +    if( vic->client->pers.statscounters.spreebleeds < 1 ) +      vic->client->pers.statscounters.spreebleeds = 1; +    if( activate && !vic->client->pers.bleeder ) +    { +      vic->client->pers.bleeder = qtrue; +      level.bleeders++; + +      AP( va( "print \"^3!outlaw: ^7%s^7 has been designated an outlaw by ^7%s\n\"", +            vic->client->pers.netname, +            ( ent ) ? ent->client->pers.netname : "console" ) ); +    } +    else +    { +      AP( va( "print \"^3!outlaw: ^7%s^7 bleeder value has been adjusted by ^7%s\n\"", +            vic->client->pers.netname, +            ( ent ) ? ent->client->pers.netname : "console" ) ); +      ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is now %d\n", +        vic->client->pers.netname, +        vic->client->pers.statscounters.spreebleeds ) ); +    } +  } +  else +  { +    vic->client->pers.statscounters.spreebleeds = 0; +    if( vic->client->pers.bleeder ) +      vic->client->pers.bleeder = qfalse; +    if( level.bleeders ) +      level.bleeders--; + +    AP( va( "print \"^3!outlaw: ^7%s^7 has been pardoned by ^7%s\n\"", +            vic->client->pers.netname, +            ( ent ) ? ent->client->pers.netname : "console" ) ); +  } +  return qtrue; +} +  qboolean G_admin_slap( gentity_t *ent, int skiparg )  {    int pids[ MAX_CLIENTS ]; @@ -6452,6 +7325,45 @@ qboolean G_admin_drop( gentity_t *ent, int skiparg )    return qtrue;  } +qboolean G_admin_bubble( gentity_t *ent, int skiparg ) +{ +  int pids[ MAX_CLIENTS ]; +  char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; +  gentity_t *vic; + +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( "^3!bubble: ^7usage: !bubble [name|slot#]\n" ); +    return qfalse; +  } +  G_SayArgv( 1 + skiparg, name, sizeof( name ) ); +  if( G_ClientNumbersFromString( name, pids ) != 1 ) +  { +    G_MatchOnePlayer( pids, err, sizeof( err ) ); +    ADMP( va( "^3!bubble: ^7%s\n", err ) ); +    return qfalse; +  } +  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) +  { +    ADMP( "^3!bubble: ^7sorry, but your intended victim has a higher admin" +        " level than you\n" ); +    return qfalse; +  } + +  vic = &g_entities[ pids[ 0 ] ]; +  if( vic->client->pers.bubbleTime ) +    vic->client->pers.bubbleTime = 0; +  else +    vic->client->pers.bubbleTime = level.time + 500; + +  AP( va( "print \"^3!bubble: ^7bubbles %s for %s^7 by %s\n\"", +    ( vic->client->pers.bubbleTime ) ? "enabled" : "disabled", +    vic->client->pers.netname, +    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + +  return qtrue; +} +  qboolean G_admin_buildlog( gentity_t *ent, int skiparg )  {  #define LOG_DISPLAY_LENGTH 10 @@ -6663,6 +7575,34 @@ qboolean G_admin_buildlog( gentity_t *ent, int skiparg )    return qtrue;  } +int G_admin_autorevert( gentity_t *ent ) +{ +  int count = 0; +  int max; +  buildHistory_t *ptr; + +  if( !g_autoRevert.integer ) +    return 0; +  if( !g_buildLogMaxLength.integer ) +    return 0; + +  max = g_autoRevert.integer; +  ptr = level.buildHistory; +  while( ptr && count < max ) +  { +    if( ptr->ent == ent && ptr->time > level.time - (60000 * 5) && +        ( ptr->fate == BF_TEAMKILLED || ptr->fate == BF_DECONNED ) ) +    { +      trap_SendConsoleCommand( EXEC_APPEND, va("!revert #%d\n", ptr->ID) ); +      count++; +    } + +    ptr = ptr->next; +  } + +  return count; +} +  qboolean G_admin_revert( gentity_t *ent, int skiparg )  {    int i = 0, j = 0, repeat = 1, ID = 0, len, matchlen=0; @@ -6805,33 +7745,6 @@ qboolean G_admin_revert( gentity_t *ent, int skiparg )                return qfalse;              }           } -         // Prevent teleport glitch when reverting an occupied hovel -         if( targ->s.modelindex == BA_A_HOVEL && -             targ->active ) -         { -           gentity_t *builder = targ->builder; -           vec3_t    newOrigin; -           vec3_t    newAngles; - -           VectorCopy( targ->s.angles, newAngles ); -           newAngles[ ROLL ] = 0; - -           VectorCopy( targ->s.origin, newOrigin ); -           VectorMA( newOrigin, 1.0f, targ->s.origin2, newOrigin ); - -           //prevent lerping -           builder->client->ps.eFlags ^= EF_TELEPORT_BIT; -           builder->client->ps.eFlags &= ~EF_NODRAW; -           G_UnlaggedClear( builder ); - -           G_SetOrigin( builder, newOrigin ); -           VectorCopy( newOrigin, builder->client->ps.origin ); -           G_SetClientViewAngle( builder, newAngles ); - -           //client leaves hovel -           builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; -         } -            // if we haven't returned yet then we're good to go, free it            G_FreeEntity( targ );            // put the marked buildables back and mark them again @@ -7225,6 +8138,76 @@ qboolean G_admin_L1(gentity_t *ent, int skiparg ){    return qtrue;  } +qboolean G_admin_nobuild(gentity_t *ent, int skiparg ) +{ +  char func[ 32 ]; + +  if( ent && ent->client->pers.teamSelection != PTE_NONE ) +  { +    ADMP( "^3!bring: ^7you can only use this command from spectator\n" ); +    return qfalse; +  } + +  if( G_SayArgc() < 2 + skiparg ) +  { +    ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" ); +    return qfalse; +  } +  G_SayArgv( 1 + skiparg, func, sizeof( func ) ); + +  if( !Q_stricmp( func, "on" ) ) +  { +    nobuild_set( qtrue, ent ); +  } +  else if( !Q_stricmp( func, "off" ) ) +  { +    nobuild_set( qfalse, ent ); +  } +  else if( !Q_stricmp( func, "save" ) ) +  { +    nobuild_save( ); +  } +  else if( !Q_stricmp( func, "list" ) ) +  { +    nobuild_list( ent ); +  } +  else if( !Q_stricmp( func, "add" ) ) +  { +    nobuild_add( ent ); +  } +  else if( !Q_stricmp( func, "del" ) ) +  { +    nobuild_del( ent ); +  } +  else if( !Q_stricmp( func, "zone" ) ) +  { +    nobuild_command( ent, qtrue, qfalse, 0.0f ); +  } +  else if( !Q_stricmp( func, "mode" ) ) +  { +    nobuild_command( ent, qfalse, qtrue, 0.0f ); +  } +  else if( !Q_stricmp( func, "+" ) ) +  { +    nobuild_command( ent, qfalse, qfalse, 8.0f ); +  } +  else if( !Q_stricmp( func, "-" ) ) +  { +    nobuild_command( ent, qfalse, qfalse, -8.0f ); +  } +  else if( !Q_stricmp( func, "go" ) ) +  { +    nobuild_go( ent ); +  } +  else +  { +    ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" ); +    return qfalse; +  } + +  return qtrue; +} +  qboolean G_admin_invisible( gentity_t *ent, int skiparg )  {    if( !ent ) diff --git a/src/game/g_admin.h b/src/game/g_admin.h index 33103ff..6e6e57a 100644 --- a/src/game/g_admin.h +++ b/src/game/g_admin.h @@ -32,19 +32,24 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  #define ADMBP_end() G_admin_buffer_end(ent)  #define MAX_ADMIN_LEVELS 32  -#define MAX_ADMIN_ADMINS 1024 +#define MAX_ADMIN_ADMINS 2048  #define MAX_ADMIN_BANS 1024  #define MAX_ADMIN_NAMELOGS 128  #define MAX_ADMIN_NAMELOG_NAMES 5  #define MAX_ADMIN_ADMINLOGS 128  #define MAX_ADMIN_ADMINLOG_ARGS 50 +#define MAX_ADMIN_TKLOGS 64  #define MAX_ADMIN_FLAG_LEN 20  #define MAX_ADMIN_FLAGS 1024  #define MAX_ADMIN_COMMANDS 64  #define MAX_ADMIN_CMD_LEN 20  #define MAX_ADMIN_BAN_REASON 50 +  #define MAX_ADMIN_BANSUSPEND_DAYS 14 +#define CHAT_MAXCHAN 10 +#define CHAT_MAXPASS 12 +  /*   * IMMUNITY - cannot be vote kicked, vote muted   * NOCENSORFLOOD - cannot be censored or flood protected @@ -63,14 +68,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA   * SEESFULLLISTPLAYERS - sees all information in !listplayers    * DBUILDER - permanent designated builder   * STEALTH - uses admin stealth - * SPECIAL - allows some special permissions (unlimited votes etc) - * SPECIALNAME - allows black text in name - * .NOCHAT - mutes a player on connect - * .NOVOTE - disallows voting by a player + * BANIMMUNITY - immune from IP based bans   * ALLFLAGS - all flags (including command flags) apply to this player   */ -  #define ADMF_IMMUNITY            "IMMUNITY"  #define ADMF_NOCENSORFLOOD       "NOCENSORFLOOD"  #define ADMF_TEAMCHANGEFREE      "TEAMCHANGEFREE" @@ -92,14 +93,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  #define ADMF_BAN_IMMUNITY        "BANIMMUNITY" -#define ADMF_SPECIAL             "SPECIAL" -#define ADMF_SPECIALNAME         "SPECIALNAME" -  #define ADMF_NO_CHAT             ".NOCHAT"  #define ADMF_NO_VOTE             ".NOVOTE"  #define MAX_ADMIN_LISTITEMS 20  #define MAX_ADMIN_SHOWBANS 10 +  #define MAX_ADMIN_MAPLOG_LENGTH 5  // important note: QVM does not seem to allow a single char to be a @@ -129,6 +128,8 @@ typedef struct g_admin_admin    int level;    char flags[ MAX_ADMIN_FLAGS ];    int seen; +  char chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ]; +  int karma;  }  g_admin_admin_t; @@ -168,7 +169,6 @@ typedef struct g_admin_namelog    int       denyHumanWeapons;    int       denyAlienClasses;    int       specExpires; -  int       voteCount;  }  g_admin_namelog_t; @@ -184,6 +184,19 @@ typedef struct g_admin_adminlog  }  g_admin_adminlog_t; +typedef struct g_admin_tklog +{ +  char      name[ MAX_NAME_LENGTH ]; +  char      victim[ MAX_NAME_LENGTH ]; +  int       id; +  int       time; +  int       damage; +  int       value; +  int       team; +  int       weapon; +} +g_admin_tklog_t; +  qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen );  qboolean G_admin_cmd_check( gentity_t *ent, qboolean say );  qboolean G_admin_readconfig( gentity_t *ent, int skiparg ); @@ -195,24 +208,31 @@ int G_admin_level( gentity_t *ent );  void G_admin_set_adminname( gentity_t *ent );  char* G_admin_adminPrintName( gentity_t *ent ); -qboolean G_admin_seen(gentity_t *ent, int skiparg ); -void G_admin_seen_update( char *guid ); +void G_admin_chat_writeconfig( void ); +qboolean G_admin_chat_readconfig( gentity_t *ent ); +void G_admin_chat_sync( gentity_t *ent ); +void G_admin_chat_update( gentity_t *ent, int chan );  // ! command functions  qboolean G_admin_time( gentity_t *ent, int skiparg );  qboolean G_admin_setlevel( gentity_t *ent, int skiparg ); -qboolean G_admin_flaglist( gentity_t *ent, int skiparg ); -qboolean G_admin_flag( gentity_t *ent, int skiparg );  qboolean G_admin_kick( gentity_t *ent, int skiparg );  qboolean G_admin_adjustban( gentity_t *ent, int skiparg );  qboolean G_admin_subnetban( gentity_t *ent, int skiparg );  qboolean G_admin_suspendban( gentity_t *ent, int skiparg );  qboolean G_admin_ban( gentity_t *ent, int skiparg );  qboolean G_admin_unban( gentity_t *ent, int skiparg ); +qboolean G_admin_seen(gentity_t *ent, int skiparg ); +void G_admin_karma_sync( void ); +void G_admin_seen_update( gclient_t *client, qboolean disconnect ); +qboolean G_admin_expire( gentity_t *ent, int skiparg );  qboolean G_admin_putteam( gentity_t *ent, int skiparg );  qboolean G_admin_adminlog( gentity_t *ent, int skiparg );  void G_admin_adminlog_cleanup( void );  void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skiparg, qboolean success ); +qboolean G_admin_tklog( gentity_t *ent, int skiparg ); +void G_admin_tklog_cleanup( void ); +void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath );  qboolean G_admin_listadmins( gentity_t *ent, int skiparg );  qboolean G_admin_listlayouts( gentity_t *ent, int skiparg );  qboolean G_admin_listplayers( gentity_t *ent, int skiparg ); @@ -237,25 +257,33 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg );  qboolean G_admin_register( gentity_t *ent, int skiparg );  qboolean G_admin_rename( gentity_t *ent, int skiparg );  qboolean G_admin_restart( gentity_t *ent, int skiparg ); -qboolean G_admin_nobuild( gentity_t *ent, int skiparg );  qboolean G_admin_nextmap( gentity_t *ent, int skiparg );  qboolean G_admin_namelog( gentity_t *ent, int skiparg );  qboolean G_admin_lock( gentity_t *ent, int skiparg );  qboolean G_admin_unlock( gentity_t *ent, int skiparg );  qboolean G_admin_info( gentity_t *ent, int skiparg ); +qboolean G_admin_nobuild(gentity_t *ent, int skiparg );  qboolean G_admin_buildlog( gentity_t *ent, int skiparg );  qboolean G_admin_revert( gentity_t *ent, int skiparg ); +int G_admin_autorevert( gentity_t *ent );  qboolean G_admin_pause( gentity_t *ent, int skiparg ); +qboolean G_admin_practice( gentity_t *ent, int skiparg );  qboolean G_admin_L0( gentity_t *ent, int skiparg );  qboolean G_admin_L1( gentity_t *ent, int skiparg ); +qboolean G_admin_bring( gentity_t *ent, int skiparg );  qboolean G_admin_putmespec( gentity_t *ent, int skiparg ); +qboolean G_admin_outlaw( gentity_t *ent, int skiparg );  qboolean G_admin_warn( gentity_t *ent, int skiparg );  qboolean G_admin_designate( gentity_t *ent, int skiparg ); +qboolean G_admin_flaglist( gentity_t *ent, int skiparg ); +qboolean G_admin_flag( gentity_t *ent, int skiparg ); +qboolean G_admin_immunity( gentity_t *ent, int skiparg );  qboolean G_admin_cp( gentity_t *ent, int skiparg ); +qboolean G_admin_invisible( gentity_t *ent, int skiparg );  qboolean G_admin_slap( gentity_t *ent, int skiparg );  qboolean G_admin_drop( gentity_t *ent, int skiparg ); -qboolean G_admin_invisible( gentity_t *ent, int skiparg ); +qboolean G_admin_bubble( gentity_t *ent, int skiparg );  void G_admin_print( gentity_t *ent, char *m );  void G_admin_buffer_print( gentity_t *ent, char *m ); diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 3290ccc..5a2698b 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -513,6 +513,54 @@ static void freeBuildable( gentity_t *self )    G_FreeEntity( self );  } +/* +================ +G_RecoverBuildPoints + +Schedule build points for delayed recovery when a buildable dies + credit to Mercenaries Guild dev team (mgdev) for the whole idea +================ +*/ +void G_RecoverBuildPoints( gentity_t *self ) +{ +  int team; +  int value; + +  if( g_buildPointsRecoverRate.integer < 1 ) +    return; + +  if( self->killedBy != ENTITYNUM_NONE ) +    return; + +  team = BG_FindTeamForBuildable( self->s.modelindex ); +  value = BG_FindBuildPointsForBuildable( self->s.modelindex ); +  if( team == BIT_ALIENS ) +  { +    if( !level.alienRecoverBuildPoints ) +      level.alienRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer; +    level.alienRecoverBuildPoints += value; +  } +  else if( team == BIT_HUMANS ) +  { +    if( !level.humanRecoverBuildPoints ) +      level.humanRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer; +    level.humanRecoverBuildPoints += value; +  } + +  self->killedBy = ENTITYNUM_NONE; +} + +static void G_RecoverSetKiller( gentity_t *self, gentity_t *attacker ) +{ +  if( g_buildPointsRecoverRate.integer < 1 ) +    return; + +  // note attacker if teamkill +  if( attacker && attacker->client && +      attacker->client->ps.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( self->s.modelindex ) ) +    self->killedBy = attacker - g_entities; +} +  //================================================================================== @@ -531,6 +579,7 @@ void A_CreepRecede( gentity_t *self )    if( !( self->s.eFlags & EF_DEAD ) )    {      self->s.eFlags |= EF_DEAD; +    G_RecoverBuildPoints( self );      G_AddEvent( self, EV_BUILD_DESTROY, 0 );      if( self->spawned ) @@ -573,6 +622,7 @@ void ASpawn_Melt( gentity_t *self )    if( !( self->s.eFlags & EF_DEAD ) )    {      self->s.eFlags |= EF_DEAD; +    G_RecoverBuildPoints( self );      G_AddEvent( self, EV_BUILD_DESTROY, 0 );      if( self->spawned ) @@ -631,6 +681,7 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    buildHistory_t *new;    new = G_Alloc( sizeof( buildHistory_t ) );    new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID; +  new->time = level.time;    new->ent = ( attacker && attacker->client ) ? attacker : NULL;    if( new->ent )      new->name[ 0 ] = 0; @@ -658,6 +709,8 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    self->s.eFlags &= ~EF_FIRING; //prevent any firing effects +  G_RecoverSetKiller( self, attacker ); +    if( attacker && attacker->client )    {      if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) @@ -796,6 +849,18 @@ void AOvermind_Think( gentity_t *self )    vec3_t    mins, maxs;    int       i, num;    gentity_t *enemy; +  float om_range = OVERMIND_ATTACK_RANGE; +  float om_damage = self->splashDamage; +  float om_splash = self->splashRadius; + +  if( g_modMainStrength.integer > 0 ) +  { +    om_range = om_range * g_modMainStrength.value / 100; +    range[0] = range[1] = range[2] = om_range; + +    om_damage = om_damage * g_modMainStrength.value / 100; +    om_splash = om_splash * g_modMainStrength.value / 100; +  }    VectorAdd( self->s.origin, range, maxs );    VectorSubtract( self->s.origin, range, mins ); @@ -814,8 +879,8 @@ void AOvermind_Think( gentity_t *self )        if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )        {          self->timestamp = level.time; -        G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, -          self->splashRadius, self, MOD_OVERMIND, PTE_ALIENS ); +        G_SelectiveRadiusDamage( self->s.pos.trBase, self, om_damage, +          om_splash, self, MOD_OVERMIND, PTE_ALIENS );          G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );        }      } @@ -940,6 +1005,7 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,    buildHistory_t *new;    new = G_Alloc( sizeof( buildHistory_t ) );    new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID; +  new->time = level.time;    new->ent = ( attacker && attacker->client ) ? attacker : NULL;    if( new->ent )      new->name[ 0 ] = 0; @@ -966,6 +1032,8 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,    else      self->nextthink = level.time; //blast immediately +  G_RecoverSetKiller( self, attacker ); +    if( attacker && attacker->client )    {      if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) @@ -1031,13 +1099,18 @@ void AAcidTube_Damage( gentity_t *self )  {    if( self->spawned )    { +    int repeatRate = ACIDTUBE_REPEAT; +      if( !( self->s.eFlags & EF_FIRING ) )      {        self->s.eFlags |= EF_FIRING;        G_AddEvent( self, EV_ALIEN_ACIDTUBE, DirToByte( self->s.origin2 ) );      } -    if( ( self->timestamp + ACIDTUBE_REPEAT ) > level.time ) +    if( g_modBuildableSpeed.integer > 0 ) +      repeatRate = repeatRate * 100 / g_modBuildableSpeed.integer; + +    if( ( self->timestamp + repeatRate ) > level.time )        self->think = AAcidTube_Damage;      else      { @@ -1096,7 +1169,8 @@ void AAcidTube_Think( gentity_t *self )        if( !G_Visible( self, enemy ) )          continue; -      if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) +      if( enemy->client && +          ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS || enemy->client->pers.bleeder ) )        {          if( level.paused || enemy->client->pers.paused )            continue; @@ -1389,6 +1463,7 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    buildHistory_t *new;    new = G_Alloc( sizeof( buildHistory_t ) );    new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID; +  new->time = level.time;    new->ent = ( attacker && attacker->client ) ? attacker : NULL;    if( new->ent )      new->name[ 0 ] = 0; @@ -1447,6 +1522,8 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    self->r.contents = 0;    //stop collisions...    trap_LinkEntity( self ); //...requires a relink +  G_RecoverSetKiller( self, attacker ); +    if( attacker && attacker->client )    {      if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) @@ -1502,6 +1579,18 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )    if( client && client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )      return; +  if( client->pers.bleeder ) +  { +    if( !(client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) ) +    { +      client->ps.stats[ STAT_STATE ] |= SS_POISONCLOUDED; +      client->lastPoisonCloudedTime = level.time; +      trap_SendServerCommand( client->ps.clientNum, "poisoncloud" ); +      trap_SendServerCommand( client->ps.clientNum, "print \"Your booster has poisoned you\n\"" ); +    } +    return; +  } +    //only allow boostage once every 30 seconds    if( client->lastBoostedTime + BOOSTER_INTERVAL > level.time )      return; @@ -1772,6 +1861,15 @@ void HReactor_Think( gentity_t *self )    vec3_t    mins, maxs;    int       i, num;    gentity_t *enemy, *tent; +  float rc_range = REACTOR_ATTACK_RANGE; +  float rc_damage =  REACTOR_ATTACK_DAMAGE; + +  if( g_modMainStrength.integer > 0 ) +  { +    rc_range = rc_range * g_modMainStrength.value / 100; +    rc_damage = rc_damage * g_modMainStrength.value / 100; +    range[0] = range[1] = range[2] = rc_range; +  }    VectorAdd( self->s.origin, range, maxs );    VectorSubtract( self->s.origin, range, mins ); @@ -1792,8 +1890,8 @@ void HReactor_Think( gentity_t *self )          if( level.paused || enemy->client->pers.paused )            continue;          self->timestamp = level.time; -        G_SelectiveRadiusDamage( self->s.pos.trBase, self, REACTOR_ATTACK_DAMAGE, -          REACTOR_ATTACK_RANGE, self, MOD_REACTOR, PTE_HUMANS ); +        G_SelectiveRadiusDamage( self->s.pos.trBase, self, rc_damage, +          rc_range, self, MOD_REACTOR, PTE_HUMANS );          tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL ); @@ -1943,6 +2041,7 @@ void HMedistat_Think( gentity_t *self )        {          if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&              player->client->ps.pm_type != PM_DEAD && +            !player->client->pers.bleeder &&              self->enemy == player )            occupied = qtrue;        } @@ -1960,7 +2059,8 @@ void HMedistat_Think( gentity_t *self )      if( player->flags & FL_NOTARGET )        continue; // notarget cancels even beneficial effects? -        if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) +        if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && +            !player->client->pers.bleeder )          {            if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&                player->client->ps.pm_type != PM_DEAD ) @@ -1980,6 +2080,34 @@ void HMedistat_Think( gentity_t *self )        }      } +    // bleeding spree retribution +    if( level.bleeders && !self->enemy ) +    { +      //look for something to hurt +      for( i = 0; i < num; i++ ) +      { +        player = &g_entities[ entityList[ i ] ]; + +        if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && player->client->pers.bleeder ) +        { +          if( player->health > 0 && +              player->client->ps.pm_type != PM_DEAD ) +          { +            self->enemy = player; + +            //start the heal anim +            if( !self->active ) +            { +              G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse ); +              self->active = qtrue; +            } +          } +          if( BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) ) +            BG_RemoveUpgradeFromInventory( UP_MEDKIT, player->client->ps.stats ); +        } +      } +    } +      //nothing left to heal so go back to idling      if( !self->enemy && self->active )      { @@ -1990,6 +2118,12 @@ void HMedistat_Think( gentity_t *self )      }      else if( self->enemy ) //heal!      { +      if( self->enemy->client->pers.bleeder ) +      { +        G_Damage( self->enemy, NULL, NULL, NULL, NULL, 10, 0, MOD_SLIME ); +        return; +      } +        if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED )          self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; @@ -2147,7 +2281,8 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ign    if( !traceEnt->client )      return qfalse; -  if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS ) +  if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS && +      !traceEnt->client->pers.bleeder )      return qfalse;    return qtrue; @@ -2211,6 +2346,24 @@ void HMGTurret_FindEnemy( gentity_t *self )      }    } +  // bleeder retribution +  if( level.bleeders ) +  { +    for( i = 0; i < num; i++ ) +    { +      target = &g_entities[ entityList[ i ] ]; + +      if( target->client && target->client->pers.bleeder ) +      { +        if( !HMGTurret_CheckTarget( self, target, qfalse ) ) +          continue; + +        self->enemy = target; +        return; +      } +    } +  } +    //couldn't find a target    self->enemy = NULL;  } @@ -2239,7 +2392,7 @@ void HMGTurret_Think( gentity_t *self )    //if not powered don't do anything and check again for power next think    if( !( self->powered = G_FindPower( self ) ) )    { -    if( self->spawned ) +    if( g_turretAim.integer && self->spawned )      {        // unpowered turret barrel falls to bottom of range        float droop; @@ -2254,7 +2407,7 @@ void HMGTurret_Think( gentity_t *self )          return;        }      } - +          self->nextthink = level.time + POWER_REFRESH_TIME;      return;    } @@ -2275,9 +2428,24 @@ void HMGTurret_Think( gentity_t *self )      //if a new target cannot be found don't do anything      if( !self->enemy ) +    { +      if( g_turretAim.integer && +          self->rotatorAngle >= 360.0f && +          level.time > self->last_move_time + TURRET_REST_TIME ) +      { +        float diff; + +        diff = AngleSubtract( self->s.angles2[ YAW ], self->rotatorAngle - 360.0f ); +        if( diff < -TURRET_REST_TOLERANCE ) +          self->s.angles2[ YAW ] += TURRET_REST_SPEED; +        else if ( diff > TURRET_REST_TOLERANCE ) +          self->s.angles2[ YAW ] -= TURRET_REST_SPEED; +      }        return; +    }      self->enemy->targeted = self; +    self->last_move_time = level.time;      //if we are pointing at our target and we can fire shoot it      if( HMGTurret_TrackEnemy( self ) && ( self->count < level.time ) ) @@ -2346,7 +2514,8 @@ void HTeslaGen_Think( gentity_t *self )        if( enemy->flags & FL_NOTARGET )          continue; -      if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && +      if( enemy->client && +          ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || enemy->client->pers.bleeder ) &&            enemy->health > 0 &&            !level.paused && !enemy->client->pers.paused &&            Distance( enemy->s.pos.trBase, self->s.pos.trBase ) <= TESLAGEN_RANGE ) @@ -2404,6 +2573,9 @@ void HSpawn_Disappear( gentity_t *self )    self->r.contents = 0;    //stop collisions...    trap_LinkEntity( self ); //...requires a relink + +  self->s.eFlags |= EF_DEAD; +  G_RecoverBuildPoints( self );  } @@ -2436,6 +2608,9 @@ void HSpawn_Blast( gentity_t *self )    self->r.contents = 0;    //stop collisions...    trap_LinkEntity( self ); //...requires a relink + +  self->s.eFlags |= EF_DEAD; +  G_RecoverBuildPoints( self );  } @@ -2451,6 +2626,7 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    buildHistory_t *new;    new = G_Alloc( sizeof( buildHistory_t ) );    new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID; +  new->time = level.time;    new->ent = ( attacker && attacker->client ) ? attacker : NULL;    if( new->ent )      new->name[ 0 ] = 0; @@ -2485,6 +2661,8 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int      self->nextthink = level.time; //blast immediately    } +  G_RecoverSetKiller( self, attacker ); +    if( attacker && attacker->client )    {      if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) @@ -2769,6 +2947,50 @@ qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable )    return qfalse;  } +/* +=============== +G_BuildableRangeClosest + +Find Closest distance to a type of buildable, -1 +=============== +*/ +float G_BuildableRangeClosest( vec3_t origin, float r, buildable_t buildable ) +{ +  int       entityList[ MAX_GENTITIES ]; +  vec3_t    range; +  vec3_t    mins, maxs; +  int       i, num; +  gentity_t *ent; +  float     found = -1.0; + +  VectorSet( range, r, r, r ); +  VectorAdd( origin, range, maxs ); +  VectorSubtract( origin, range, mins ); + +  num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); +  for( i = 0; i < num; i++ ) +  { +    ent = &g_entities[ entityList[ i ] ]; + +    if( ent->s.eType != ET_BUILDABLE ) +      continue; + +    if( ent->biteam == BIT_HUMANS && !ent->powered ) +      continue; + +    if( ent->s.modelindex == buildable && ent->spawned ) +    { +      float dist; + +      dist = Distance( origin, ent->s.origin ); +      if( found < 0.0 || dist < found ) +        found = dist; +    } +  } + +  return found; +} +  static qboolean G_BoundsIntersect(const vec3_t mins, const vec3_t maxs,                                    const vec3_t mins2, const vec3_t maxs2)  { @@ -2851,7 +3073,6 @@ static int G_CompareBuildablesForRemoval( const void *a, const void *b )    buildableA = *(gentity_t **)a;    buildableB = *(gentity_t **)b; -    // Prefer the one that collides with the thing we're building    aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin,        buildableA->s.modelindex, buildableA->s.origin ); @@ -2909,6 +3130,7 @@ void G_FreeMarkedBuildables( void )      new = G_Alloc( sizeof( buildHistory_t ) );      new->ID = -1; +    new->time = 0;      new->ent = NULL;      Q_strncpyz( new->name, "<markdecon>", 12 );      new->buildable = ent->s.modelindex; @@ -3035,12 +3257,7 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t     buildable,      // Don't allow destruction of hovel with granger inside      if( ent->s.modelindex == BA_A_HOVEL && ent->active ) -    { -      if( buildable == BA_A_HOVEL ) -        return IBE_HOVEL; -      else -        continue; -    } +      continue;      // Explicitly disallow replacement of the core buildable with anything      // other than the core buildable @@ -3163,18 +3380,17 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance  {    vec3_t            angles;    vec3_t            entity_origin, normal; -  vec3_t            mins, maxs, nbmins, nbmaxs; -  vec3_t	    nbVect; +  vec3_t            mins, maxs;    trace_t           tr1, tr2, tr3;    int               i;    itemBuildError_t  reason = IBE_NONE; +  itemBuildError_t  old_reason = IBE_NONE;    gentity_t         *tempent;    float             minNormal;    qboolean          invert;    int               contents;    playerState_t     *ps = &ent->client->ps;    int               buildPoints; -  gentity_t	    *tmp;    itemBuildError_t  tempReason;    // Stop all buildables from interacting with traces @@ -3200,28 +3416,13 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance    if( tr1.entityNum != ENTITYNUM_WORLD )      reason = IBE_NORMAL; +  // check nobuild zones +  if( nobuild_check( entity_origin ) >= 0 ) +    reason = IBE_PERMISSION; +    contents = trap_PointContents( entity_origin, -1 );    buildPoints = BG_FindBuildPointsForBuildable( buildable ); -   -  //check if we are near a nobuild marker, if so, can't build here... -  for( i = 0; i < MAX_GENTITIES; i++ ) -  { -	tmp = &g_entities[ i ]; -	 -	if( !tmp->noBuild.isNB ) -	continue; -	 -	nbVect[0] = tmp->noBuild.Area; -	nbVect[1] = tmp->noBuild.Area; -	nbVect[2] = tmp->noBuild.Height; -	   -    	VectorSubtract( origin, nbVect, nbmins ); -  	VectorAdd( origin, nbVect, nbmaxs ); -	 -	if( trap_EntityContact( nbmins, nbmaxs, tmp ) ) -	reason = IBE_PERMISSION; -	 -  } +    if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )    {      //alien criteria @@ -3276,6 +3477,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance            switch( buildable )            {              case BA_A_OVERMIND: +              old_reason = reason;                reason = IBE_OVERMIND;                break; @@ -3359,6 +3561,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance          if( tempent->s.modelindex == BA_H_REACTOR && !tempent->deconstruct )          { +          old_reason = reason;            reason = IBE_REACTOR;            break;          } @@ -3369,6 +3572,38 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance    if( ( tempReason = G_SufficientBPAvailable( buildable, origin ) ) != IBE_NONE )      reason = tempReason; +  if( g_modBuildableCount.integer > 1 && old_reason == IBE_NONE ) +  { +    if( reason == IBE_REACTOR ) +    { +      int rcs = 0; + +      for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) +      { +        if( tempent->s.eType == ET_BUILDABLE && +            tempent->s.modelindex == BA_H_REACTOR && +            !tempent->deconstruct ) +        rcs++; +      } +      if( rcs < g_modBuildableCount.integer ) +        reason = IBE_NONE; +    } +    else if( reason == IBE_OVERMIND ) +    { +      int oms = 0; + +      for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) +      { +        if( tempent->s.eType == ET_BUILDABLE && +            tempent->s.modelindex == BA_A_OVERMIND && +            !tempent->deconstruct ) +        oms++; +      } +      if( oms < g_modBuildableCount.integer ) +        reason = IBE_NONE; +    } +  } +    // Relink buildables    G_SetBuildableLinkState( qtrue ); @@ -3498,8 +3733,10 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori    built->buildTime = built->s.time = level.time;    built->spawnBlockTime = 0; +  built->killedBy = ENTITYNUM_NONE; +    // build instantly in cheat mode -  if( builder->client && g_cheats.integer ) +  if( builder->client && g_cheats.integer || g_instantBuild.integer )    {      built->health = BG_FindHealthForBuildable( buildable );      built->buildTime = built->s.time = @@ -3704,6 +3941,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori    {      new = level.buildHistory;      new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID; +    new->time = level.time;      new->ent = builder;      new->name[ 0 ] = 0;      new->buildable = buildable; @@ -3722,39 +3960,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori    return built;  } -static void G_SpawnMarker( vec3_t origin ) -{ -	gentity_t *nb; -	int i; -   -  	// Make the marker... -	nb = G_Spawn( ); -	nb->s.modelindex = 0; //Coder humor is win -	VectorCopy( origin, nb->s.pos.trBase ); -	VectorCopy( origin, nb->r.currentOrigin ); -	nb->noBuild.isNB = qtrue; -	nb->noBuild.Area = level.nbArea; -	nb->noBuild.Height = level.nbHeight; -	trap_LinkEntity( nb ); -	 -	// Log markers made... -	for( i = 0; i < MAX_GENTITIES; i++ ) -	{ -		if( level.nbMarkers[ i ].Marker != NULL ) -		continue; -		 -		level.nbMarkers[ i ].Marker = nb; -		VectorCopy( origin, level.nbMarkers[ i ].Origin ); -		SnapVector( level.nbMarkers[ i ].Origin ); -		break; -	} -	 -  	// End nobuild mode... -	level.noBuilding = qfalse; -	level.nbArea = 0.0f; -	level.nbHeight = 0.0f; -} -  /*  =================  G_BuildIfValid @@ -3770,15 +3975,6 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )    switch( G_CanBuild( ent, buildable, dist, origin ) )    {      case IBE_NONE: -      if( level.noBuilding ) -      { -      	vec3_t    mins; -  	BG_FindBBoxForBuildable( buildable, mins, NULL ); -  	origin[2] += mins[2]; -	 -      	G_SpawnMarker( origin ); -	return qtrue; -      }        G_Build( ent, buildable, origin, ent->s.apos.trBase );        return qtrue; @@ -3844,40 +4040,16 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )        return qfalse;      case IBE_SPWNWARN: -      if( level.noBuilding ) -      { -      	vec3_t    mins; -  	BG_FindBBoxForBuildable( buildable, mins, NULL ); -  	origin[2] += mins[2]; -      	G_SpawnMarker( origin ); -	return qtrue; -      }        G_TriggerMenu( ent->client->ps.clientNum, MN_A_SPWNWARN );        G_Build( ent, buildable, origin, ent->s.apos.trBase );        return qtrue;      case IBE_TNODEWARN: -      if( level.noBuilding ) -      { -      	vec3_t    mins; -  	BG_FindBBoxForBuildable( buildable, mins, NULL ); -  	origin[2] += mins[2]; -      	G_SpawnMarker( origin ); -	return qtrue; -      }        G_TriggerMenu( ent->client->ps.clientNum, MN_H_TNODEWARN );        G_Build( ent, buildable, origin, ent->s.apos.trBase );        return qtrue;      case IBE_RPTWARN: -      if( level.noBuilding ) -      { -      	vec3_t    mins; -  	BG_FindBBoxForBuildable( buildable, mins, NULL ); -  	origin[2] += mins[2]; -      	G_SpawnMarker( origin ); -	return qtrue; -      }        G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTWARN );        G_Build( ent, buildable, origin, ent->s.apos.trBase );        return qtrue; @@ -4583,128 +4755,624 @@ void G_BaseSelfDestruct( pTeam_t team )     return "<buildlog entry expired>";   } +  /* -============ -G_NobuildLoad + * ======= + * nobuild + * ======= + */ -load the nobuild markers that were previously saved (if there are any). -============ -*/ -void G_NobuildLoad( void ) +#define MAX_NOBUILD_ZONES 16 +#define NOBUILD_THINK_TIME 200 + +typedef struct +{ +  qboolean in_use; +  vec3_t origin; +  vec3_t size; +} +nobuild_t; + +typedef struct +{ +  vec3_t dir; +  float yaw; +} +nobuild_corner_t; + +static nobuild_corner_t nobuildCorner[] = { +  { { -1, -1, -1 }, 0 }, +  { { -1, 1, -1 }, 270 }, +  { { 1, -1, -1 }, 90 }, +  { { 1, 1, -1 }, 180 }, + +  { { -1, -1, 1 }, 270 }, +  { { -1, 1, 1 }, 180 }, +  { { 1, -1, 1 }, 0 }, +  { { 1, 1, 1 }, 90 } +}; +#define MAX_NOBUILD_CORNERS ( sizeof( nobuildCorner ) / sizeof( nobuild_corner_t ) ) + +typedef enum +{ +  NB_ORIGIN_X, +  NB_ORIGIN_Y, +  NB_ORIGIN_Z, +  NB_SIZE_X, +  NB_SIZE_Y, +  NB_SIZE_Z +} +nobuild_sizer_t; +#define NB_COUNT ( NB_SIZE_Z + 1 ) + +static nobuild_t noBuildZones[ MAX_NOBUILD_ZONES ]; + +static qboolean noBuildActive = qfalse; +static int noBuildEditZone = 0; +static nobuild_sizer_t noBuildEditType = NB_ORIGIN_X; + +void nobuild_init( void )  { -  fileHandle_t f; -  int len; -  char *nobuild;    char map[ MAX_QPATH ]; -  vec3_t origin = { 0.0f, 0.0f, 0.0f };    char line[ MAX_STRING_CHARS ]; -  int i = 0; -  int i2; -  gentity_t *nb; -  float area; -  float height; +  char *data, *pool; +  fileHandle_t f; +  int len; +  int lcount = 0; +  int zonecount = 0; +  int i; +  int id; +  vec3_t origin, size; + +  memset( noBuildZones, 0, sizeof( noBuildZones ) );    trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); -  len = trap_FS_FOpenFile( va( "nobuild/%s.dat", map ), -    &f, FS_READ ); +  len = trap_FS_FOpenFile( va( "layouts/%s/nobuild.nbd", map ), &f, FS_READ );    if( len < 0 )    { -    // This isn't needed since nobuild is pretty much optional... -    //G_Printf( "ERROR: nobuild for %s could not be opened\n", map );      return;    } -  nobuild = G_Alloc( len + 1 ); -  trap_FS_Read( nobuild, len, f ); -  *( nobuild + len ) = '\0'; +  data = pool = G_Alloc( len + 1 ); +  trap_FS_Read( data, len, f ); +  *( data + len ) = '\0';    trap_FS_FCloseFile( f ); -  while( *nobuild ) + +  i =  0; +  while( *data )    {      if( i >= sizeof( line ) - 1 )      { -      return;  +      G_Printf( S_COLOR_RED "ERROR: line overflow in %s before \"%s\"\n", +        va( "layouts/%s/nobuild.nbd", map ), line );      } -     -    line[ i++ ] = *nobuild; +    line[ i++ ] = *data;      line[ i ] = '\0'; -    if( *nobuild == '\n' ) +    if( *data == '\n' )      { -      i = 0;  -      sscanf( line, "%f %f %f %f %f\n", -        &origin[ 0 ], &origin[ 1 ], &origin[ 2 ], &area, &height  ); - -	// Make the marker... -	nb = G_Spawn( ); -	nb->s.modelindex = 0; -	VectorCopy( origin, nb->s.pos.trBase ); -	VectorCopy( origin, nb->r.currentOrigin ); -	nb->noBuild.isNB = qtrue; -	nb->noBuild.Area = area; -	nb->noBuild.Height = height; -	trap_LinkEntity( nb ); -	 -	// Log markers made... -	for( i = 0; i < MAX_GENTITIES; i++ ) -	{ -		if( level.nbMarkers[ i ].Marker != NULL ) -		continue; -		 -		level.nbMarkers[ i ].Marker = nb; -		VectorCopy( origin, level.nbMarkers[ i ].Origin ); -		SnapVector( level.nbMarkers[ i ].Origin ); -		break; -	} -       +      i = 0; +      lcount++; +      id = -1; +      if( sscanf( line, "%d %f %f %f %f %f %f\n", +          &id, +          &origin[ 0 ], &origin[ 1 ], &origin[ 2 ], +          &size[ 0 ], &size[ 1 ], &size[ 2 ]) == 7 ) +      { +        if( zonecount >= MAX_NOBUILD_ZONES ) +        { +          G_Printf( S_COLOR_YELLOW "WARNING: nobuild zone count exceeded on line %d\n", lcount ); +          break; +        } +        noBuildZones[ zonecount ].in_use = qtrue; +        VectorCopy( origin, noBuildZones[ zonecount ].origin ); +        VectorCopy( size, noBuildZones[ zonecount ].size ); +        zonecount++; +      } +      else +      { +        G_Printf( S_COLOR_YELLOW "WARNING: nobuild data parse error on line %d\n", lcount ); +      }      } -    nobuild++; +    data++;    } + +  G_Free( pool ); + +  if( zonecount ) +    G_Printf( "nobuild: loaded %d zones\n", zonecount );  } -/* -============ -G_NobuildSave -Save all currently placed nobuild markers into the "nobuild" folder -============ -*/ -void G_NobuildSave( void ) +static void nobuild_sizer_think( gentity_t *self ) +{ +  int zone, size; +  vec3_t origin; + +  if( !noBuildActive ) +  { +    G_FreeEntity( self ); +    return; +  } + +  self->nextthink = level.time + NOBUILD_THINK_TIME; + +  zone = noBuildEditZone; +  if( zone < 0 || zone >= MAX_NOBUILD_ZONES || !noBuildZones[ zone ].in_use ) +  { +    if( self->r.linked ) +      trap_UnlinkEntity( self ); +    return; +  } + +  size = self->damage; +  VectorCopy( noBuildZones[ zone ].origin, origin ); +  switch ( noBuildEditType ) +  { +    case NB_ORIGIN_X: +    case NB_SIZE_X: +      origin[ 0 ] += size; +      break; +    case NB_ORIGIN_Y: +    case NB_SIZE_Y: +      origin[ 1 ] += size; +      break; +    case NB_ORIGIN_Z: +    case NB_SIZE_Z: +      origin[ 2 ] += size; +      break; +  } + +  VectorCopy( origin, self->s.origin ); +  VectorCopy( origin, self->s.pos.trBase ); +  VectorCopy( origin, self->r.currentOrigin ); + +  if( self->watertype != noBuildEditType ) +  { +    self->watertype = noBuildEditType; +    switch( noBuildEditType ) +    { +      case NB_ORIGIN_X: +      case NB_ORIGIN_Y: +      case NB_ORIGIN_Z: +        self->s.modelindex = G_ModelIndex( "models/players/human_base/jetpack.md3" ); +        break; +      case NB_SIZE_X: +      case NB_SIZE_Y: +      case NB_SIZE_Z: +        self->s.modelindex = G_ModelIndex( "models/players/human_base/battpack.md3" ); +        break; +    } +  } + +  if( !self->r.linked ) +    trap_LinkEntity( self ); +} + +static gentity_t *nobuild_sizer( float distance ) +{ +  gentity_t *self; + +  self = G_Spawn( ); +  self->classname = "nobuild_sizer"; +  self->s.eType = ET_GENERAL; + +  self->s.time = level.time; +  self->s.pos.trType = TR_STATIONARY; +  self->s.pos.trTime = level.time; + +  self->r.svFlags = SVF_BROADCAST; + +  self->damage = distance; +  self->watertype = -1; +  self->think = nobuild_sizer_think; + +  // run a think to set positions +  nobuild_sizer_think( self ); + +  return self; +} + +static void nobuild_corner_think( gentity_t *self ) +{ +  int zone, corner; +  vec3_t origin; + +  zone = self->count; +  corner = self->damage; + +  if( !noBuildActive || !noBuildZones[ zone ].in_use ) +  { +    G_FreeEntity( self ); +    return; +  } + +  self->nextthink = level.time + NOBUILD_THINK_TIME; + +  VectorCopy( noBuildZones[ zone ].origin, origin ); +  origin[ 0 ] += noBuildZones[ zone ].size[ 0 ] * nobuildCorner[ corner ].dir[ 0 ]; +  origin[ 1 ] += noBuildZones[ zone ].size[ 1 ] * nobuildCorner[ corner ].dir[ 1 ]; +  origin[ 2 ] += noBuildZones[ zone ].size[ 2 ] * nobuildCorner[ corner ].dir[ 2 ]; + +  VectorCopy( origin, self->s.origin ); +  VectorCopy( origin, self->s.pos.trBase ); +  VectorCopy( origin, self->r.currentOrigin ); +} + +static gentity_t *nobuild_corner( int zone, int corner ) +{ +  gentity_t *self; + +  if( zone < 0 || zone >= MAX_NOBUILD_ZONES ) +    return NULL; +  if( !noBuildZones[ zone ].in_use ) +    return NULL; +  if( corner < 0 || corner >= MAX_NOBUILD_CORNERS ) +    return NULL; + +  self = G_Spawn( ); +  self->classname = "nobuild_corner"; +  self->s.eType = ET_GENERAL; +  self->s.modelindex = 9999; + +  self->s.time = level.time; +  self->s.pos.trType = TR_STATIONARY; +  self->s.pos.trTime = level.time; + +  self->count = zone; +  self->damage = corner; +  self->think = nobuild_corner_think; + +  // run a think to set positions +  nobuild_corner_think( self ); + +  self->s.apos.trBase[ YAW ] = nobuildCorner[ corner ].yaw; +  if( nobuildCorner[ corner ].dir[ 2 ] > 0.0f ) +    self->s.apos.trBase[ PITCH ] = 180.0f; + +  trap_LinkEntity( self ); + +  return self; +} + +static void nobuild_think( gentity_t *self ) +{ +  int zone; + +  zone = self->count; + +  if( !noBuildActive || !noBuildZones[ zone ].in_use ) +  { +    G_FreeEntity( self ); +    return; +  } + +  self->nextthink = level.time + NOBUILD_THINK_TIME; +  VectorCopy( noBuildZones[ zone ].origin, self->s.origin ); +  VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase ); +  VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin ); +} + +static gentity_t *nobuild_spawn( int zone ) +{ +  gentity_t *self; +  int i; + +  if( zone < 0 || zone >= MAX_NOBUILD_ZONES ) +    return NULL; +  if( !noBuildZones[ zone ].in_use ) +    return NULL; + +  self = G_Spawn( ); +  self->classname = "nobuild_zone"; +  self->s.eType = ET_GENERAL; +  self->s.modelindex = G_ModelIndex( "models/buildables/repeater/repeater.md3" ); + +  self->s.time = level.time; +  self->s.pos.trType = TR_STATIONARY; +  self->s.pos.trTime = level.time; + +  self->think = nobuild_think; +  self->nextthink = level.time + NOBUILD_THINK_TIME; + +  VectorCopy( noBuildZones[ zone ].origin, self->s.origin ); +  VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase ); +  VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin ); + +  self->s.apos.trBase[ PITCH ] = 180.0f; + +  self->count = zone; + +  trap_LinkEntity( self ); + +  for( i = 0; i < MAX_NOBUILD_CORNERS; i++ ) +    nobuild_corner( zone, i ); + +  return self; +} + +static void nobuild_on( gentity_t *ent ) +{ +  int i; + +  if( noBuildActive ) +    return; + +  noBuildActive = qtrue; +  noBuildEditZone = MAX_NOBUILD_ZONES - 1; +  noBuildEditType = NB_ORIGIN_X; + +  nobuild_command( ent, qtrue, qfalse, 0.0f ); + +  for( i = 0; i < MAX_NOBUILD_ZONES; i++ ) +  { +    nobuild_spawn( i ); +  } + +  nobuild_sizer( 32.0f ); +  nobuild_sizer( -32.0f ); + +  trap_SendServerCommand( -1, +    va( "print \"^3!nobuild ^7edit mode enabled by %s\n\"", +      ( ent ) ? ent->client->pers.netname : "console " ) ); +} + +static void nobuild_off( gentity_t *ent ) +{ +  if( noBuildActive ) +  { +    noBuildActive = qfalse; + +    trap_SendServerCommand( -1, +      va( "print \"^3!nobuild ^7edit mode disabled by %s\n\"", +        ( ent ) ? ent->client->pers.netname : "console " ) ); +  } +} + +void nobuild_add( gentity_t *ent ) +{ +  int i; + +  if( !noBuildActive ) +  { +    ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" ); +    return; +  } + +  if( !ent ) +    return; + +  for( i = 0; i < MAX_NOBUILD_ZONES; i++ ) +  { +    if( noBuildZones[ i ].in_use ) +      continue; + +    noBuildZones[ i ].in_use = qtrue; +    VectorCopy( ent->s.origin, noBuildZones[ i ].origin ); +    VectorSet( noBuildZones[ i ].size, 64.0f, 64.0f, 64.0f ); + +    noBuildEditZone = i; +    nobuild_spawn( i ); + +    ADMP( va( "added nobuild zone #%d\n", i ) ); +    return; +  } + +  ADMP( "maximum nobuild zones reached, can not add another zone.\n" ); +} + +void nobuild_del( gentity_t *ent ) +{ +  int zone; + +  if( !noBuildActive ) +  { +    ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" ); +    return; +  } + +  if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES || +      !noBuildZones[ noBuildEditZone ].in_use ) +  { +    ADMP( "a nobuild zone is not selected\n" ); +    return; +  } + +  zone = noBuildEditZone; +  nobuild_command( ent, qtrue, qfalse, 0.0f ); +  noBuildZones[ zone ].in_use = qfalse; +  if( noBuildEditZone == zone ) +    noBuildEditZone = -1; + +  ADMP( va( "removed nobuild zone #%d\n", zone ) ); +} + +#define SIZE_MIN( a, b ) ( a = ( a + b < 32.0 ? a = 32.0 : a + b ) ) + +void nobuild_command( gentity_t *ent, qboolean next_zone, qboolean next_type, float size ) +{ +  int n; + +  if( !noBuildActive ) +  { +    ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" ); +    return; +  } + +  if( next_zone ) +  { +    n = noBuildEditZone + 1; +    while( n != noBuildEditZone ) +    { +      if( n >= MAX_NOBUILD_ZONES ) +        n = 0; +      if( noBuildZones[ n ].in_use ) +      { +        noBuildEditZone = n; +        ADMP( va ( "nobuild zone %d selected\n", n ) ); +        return; +      } +      n++; +    } +  } +  else if( next_type ) +  { +    noBuildEditType++; +    if ( noBuildEditType >= NB_COUNT ) +      noBuildEditType = 0; +  } +  else if( size ) +  { +    switch( noBuildEditType ) +    { +      case NB_ORIGIN_X: +        noBuildZones[ noBuildEditZone ].origin[ 0 ] += size; +        break; +      case NB_ORIGIN_Y: +        noBuildZones[ noBuildEditZone ].origin[ 1 ] += size; +        break; +      case NB_ORIGIN_Z: +        noBuildZones[ noBuildEditZone ].origin[ 2 ] += size; +        break; +      case NB_SIZE_X: +        SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 0 ], size ); +        break; +      case NB_SIZE_Y: +        SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 1 ], size ); +        break; +      case NB_SIZE_Z: +        SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 2 ], size ); +        break; +    } +  } +} + +void nobuild_go( gentity_t *ent ) +{ +  if( !ent ) +    return; + +  if( !noBuildActive ) +  { +    ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" ); +    return; +  } +  if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES || +      !noBuildZones[ noBuildEditZone ].in_use ) +  { +    ADMP( "a nobuild zone is not selected\n" ); +    return; +  } + +  VectorCopy( noBuildZones[ noBuildEditZone ].origin, ent->client->ps.origin ); +} + +void nobuild_set( qboolean enable, gentity_t *ent ) +{ +  if( !ent && enable ) +  { +    ADMP( "only a connected client can enable nobuild mode\n" ); +    return; +  } +  if( enable ) +  { +    nobuild_on( ent ); +  } +  else +  { +    nobuild_off( ent ); +  } +}  + +void nobuild_save( void )  {    char map[ MAX_QPATH ];    char fileName[ MAX_OSPATH ];    fileHandle_t f;    int len; +  int count = 0;    int i; -  gentity_t *ent;    char *s;    trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );    if( !map[ 0 ] )    { -    G_Printf( "NobuildSave( ): no map is loaded\n" ); +    G_Printf( "LayoutSave( ): no map is loaded\n" );      return;    } -  Com_sprintf( fileName, sizeof( fileName ), "nobuild/%s.dat", map ); - +  Com_sprintf( fileName, sizeof( fileName ), "layouts/%s/nobuild.nbd", map );    len = trap_FS_FOpenFile( fileName, &f, FS_WRITE ); -  if( len < 0 ) +    if( len < 0 )    { -    G_Printf( "nobuildsave: could not open %s\n", fileName ); +    G_Printf( "layoutsave: could not open %s\n", fileName );      return;    } -  G_Printf("nobuildsave: saving nobuild to %s\n", fileName ); +  G_Printf("layoutsave: saving layout to %s\n", fileName ); -  for( i = 0; i < MAX_GENTITIES; i++ ) +  for( i = 0; i < MAX_NOBUILD_ZONES; i++ )    { -    ent = &level.gentities[ i ]; -    if( ent->noBuild.isNB != qtrue ) +    if( !noBuildZones[ i ].in_use )        continue; -    s = va( "%f %f %f %f %f\n", -      ent->r.currentOrigin[ 0 ], -      ent->r.currentOrigin[ 1 ], -      ent->r.currentOrigin[ 2 ], -      ent->noBuild.Area, -      ent->noBuild.Height ); +    s = va( "%d %f %f %f %f %f %f\n", +      count, +      noBuildZones[ i ].origin[ 0 ], +      noBuildZones[ i ].origin[ 1 ], +      noBuildZones[ i ].origin[ 2 ], +      noBuildZones[ i ].size[ 0 ], +      noBuildZones[ i ].size[ 1 ], +      noBuildZones[ i ].size[ 2 ] );      trap_FS_Write( s, strlen( s ), f ); +    count++;    } +    trap_FS_FCloseFile( f ); +  G_Printf( "saved %d nobuild zones\n", count );  } + +void nobuild_list( gentity_t *ent ) +{ +  int count = 0; +  int i; + +  ADMBP_begin(); +  for( i = 0; i < MAX_NOBUILD_ZONES; i++ ) +  { +    if( !noBuildZones[ i ].in_use ) +      continue; + +    ADMBP( va( "%2d @ x: %6.2f, y: %6.2f, z: %6.2f - %4.2f x %4.2f x %4.2f\n", +      i, +      noBuildZones[ i ].origin[ 0 ], +      noBuildZones[ i ].origin[ 1 ], +      noBuildZones[ i ].origin[ 2 ], +      noBuildZones[ i ].size[ 0 ], +      noBuildZones[ i ].size[ 1 ], +      noBuildZones[ i ].size[ 2 ] ) ); +    count++; +  } + +  ADMBP( va( "nobuild contains %d zones\n", count ) ); + +  ADMBP_end(); +} + +int nobuild_check( vec3_t origin ) +{ +  int i; + +  for( i = 0; i < MAX_NOBUILD_ZONES; i ++ ) +  { +    if( !noBuildZones[ i ].in_use ) +      continue; + +    if( origin[ 0 ] > noBuildZones[ i ].origin[ 0 ] - noBuildZones[ i ].size[ 0 ] && +        origin[ 0 ] < noBuildZones[ i ].origin[ 0 ] + noBuildZones[ i ].size[ 0 ] && +        origin[ 1 ] > noBuildZones[ i ].origin[ 1 ] - noBuildZones[ i ].size[ 1 ] && +        origin[ 1 ] < noBuildZones[ i ].origin[ 1 ] + noBuildZones[ i ].size[ 1 ] && +        origin[ 2 ] > noBuildZones[ i ].origin[ 2 ] - noBuildZones[ i ].size[ 2 ] && +        origin[ 2 ] < noBuildZones[ i ].origin[ 2 ] + noBuildZones[ i ].size[ 2 ] ) +      return i; +  } + +  return -1; +} + diff --git a/src/game/g_client.c b/src/game/g_client.c index 3d274ee..abdfa8e 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -910,7 +910,7 @@ team_t TeamCount( int ignoreClientNum, int team )  ClientCleanName  ============  */ -static void ClientCleanName( const char *in, char *out, int outSize, qboolean special ) +static void ClientCleanName( const char *in, char *out, int outSize )  {    int   len, colorlessLen;    char  ch; @@ -957,8 +957,8 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp          break;        } -      // don't allow black in a name, unless if special -      if( ColorIndex( *in ) == 0 && !special ) +      // don't allow black in a name, period +      if( ColorIndex( *in ) == 0 )          *out++ = COLOR_WHITE;        else          *out++ = *in; @@ -1154,11 +1154,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )    // set name    Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );    s = Info_ValueForKey( userinfo, "name" ); - -  if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) ) -    ClientCleanName( s, newname, sizeof( newname ), qfalse ); -  else -    ClientCleanName( s, newname, sizeof( newname ), qtrue ); +  ClientCleanName( s, newname, sizeof( newname ) );    if( strcmp( oldname, newname ) )    { @@ -1166,10 +1162,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )        showRenameMsg = qfalse;      // in case we need to revert and there's no oldname -    if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) ) -      ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qfalse ); -    else -      ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qtrue ); +    ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ) );      if( g_newbieNumbering.integer )      { @@ -1198,8 +1191,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )          revertName = qtrue;        }        else if( g_maxNameChanges.integer > 0 -        && client->pers.nameChanges >= g_maxNameChanges.integer -        && !G_admin_permission( ent, ADMF_SPECIAL ) ) +      && client->pers.nameChanges >= g_maxNameChanges.integer  )        {          trap_SendServerCommand( ent - g_entities, va(              "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", @@ -1248,7 +1240,8 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )        //dont show if players invisible        if( client->sess.invisible != qtrue )          trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE -          " renamed to %s^7\n\"", oldname, client->pers.netname ) ); +            " renamed to %s^7\n\"", oldname, client->pers.netname ) ); +        if( g_decolourLogfiles.integer)        {          char    decoloured[ MAX_STRING_CHARS ] = "";    @@ -1367,17 +1360,18 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )    if ( client->sess.invisible != qtrue )    {      Com_sprintf( userinfo, sizeof( userinfo ), -      "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\" -      "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" -      "tl\\%d\\ig\\%16s", -      client->pers.netname, team, model, model, c1, c2, -      client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, -      teamLeader, BG_ClientListString( &client->sess.ignoreList ) ); +                 "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\" +                 "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" +                 "tl\\%d\\ig\\%16s", +                 client->pers.netname, team, model, model, c1, c2, +                 client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, +                 teamLeader, BG_ClientListString( &client->sess.ignoreList ) );      trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );    } else {      trap_SetConfigstring( CS_PLAYERS + clientNum, "" );    } +    /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/  } @@ -1426,6 +1420,7 @@ char *ClientConnect( int clientNum, qboolean firstTime )      return va( "%s", reason );    } +    // IP filtering    // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500    // recommanding PB based IP / GUID banning, the builtin system is pretty limited @@ -1443,13 +1438,38 @@ char *ClientConnect( int clientNum, qboolean firstTime )    if( G_FilterPacket( value ) )      return "You are banned from this server."; -  if( ip[ 0 ] == 0 ||strlen( ip ) < 7 ) +  if( strlen( ip ) < 7 )    {      G_AdminsPrintf( "Connect from client with invalid IP: '%s' NAME: '%s^7'\n", -                    ip, Info_ValueForKey( userinfo, "name" ) ); +      ip, Info_ValueForKey( userinfo, "name" ) );      return "Invalid client data";    } +  // limit max clients per IP +  if( g_maxGhosts.integer > 1 ) +  { +    gclient_t *other; +    int count = 0; + +    for( i = 0 ; i < level.maxclients; i++ ) +    { +      other = &level.clients[ i ]; +      if( other && +        ( other->pers.connected == CON_CONNECTED || other->pers.connected == CON_CONNECTING ) && +          strcmp( ip, other->pers.ip ) == 0 ) +      { +        count++; +      } +    } + +    if( count + 1 > g_maxGhosts.integer ) +    { +    G_AdminsPrintf( "Connect from client exceeds %d maximum connections per IP: '%s' NAME: '%s^7'\n", +      g_maxGhosts.integer, ip, Info_ValueForKey( userinfo, "name" ) ); +      return "Maximum simultaneous clients exceeded"; +    } +  } +    // check for a password    value = Info_ValueForKey( userinfo, "password" ); @@ -1464,7 +1484,7 @@ char *ClientConnect( int clientNum, qboolean firstTime )    memset( client, 0, sizeof(*client) );    // add guid to session so we don't have to keep parsing userinfo everywhere -  if( !guid[ 0 ] ) +  if( !guid[0] )    {      Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",        sizeof( client->pers.guid ) ); @@ -1473,23 +1493,9 @@ char *ClientConnect( int clientNum, qboolean firstTime )    {      Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) );    } -    Q_strncpyz( client->pers.ip, ip, sizeof( client->pers.ip ) );    client->pers.adminLevel = G_admin_level( ent ); -  // do autoghost now so that there won't be any name conflicts later on -  if ( g_autoGhost.integer && client->pers.guid[ 0 ] != 'X' ) -  { -    for ( i = 0; i < MAX_CLIENTS; i++ ) -    { -      if ( i != ent - g_entities && g_entities[i].client && g_entities[i].client->pers.connected != CON_DISCONNECTED && !Q_stricmp( g_entities[i].client->pers.guid, client->pers.guid ) ) -      { -        trap_SendServerCommand( i, "disconnect \"You may not be connected to this server multiple times\"" ); -        trap_DropClient( i, "disconnected" ); -      } -    } -  } -    client->pers.connected = CON_CONNECTING;    // read or initialize the session data @@ -1545,7 +1551,6 @@ char *ClientConnect( int clientNum, qboolean firstTime )      CalculateRanks( );      G_admin_namelog_update( client, qfalse );    } -      // if this is after !restart keepteams or !restart switchteams, apply said selection    if ( client->sess.restartTeam != PTE_NONE ) { @@ -1602,35 +1607,58 @@ void ClientBegin( int clientNum )    // locate ent at a spawn point    ClientSpawn( ent, NULL, NULL, NULL ); - +      // Ignore invisible players for this section:    if ( client->sess.invisible != qtrue )    {      trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) ); -    // auto mute flag -    if( G_admin_permission( ent, ADMF_NO_CHAT ) ) -      client->pers.muted = qtrue; -      // name can change between ClientConnect() and ClientBegin()      G_admin_namelog_update( client, qfalse ); - +     +    // rejoin any saved chat channels +    G_admin_chat_sync( ent ); +          // request the clients PTR code      trap_SendServerCommand( ent - g_entities, "ptrcrequest" );    } +     +  // auto mute flag +  if( G_admin_permission( ent, ADMF_NO_CHAT ) ) +    client->pers.muted = qtrue; + +  if( g_karma.integer ) +  { +    if( g_karma.integer > 1 && +        client->pers.adminLevel == 0 && +        client->pers.guid[0] != 'X' ) +    { +      if( !(g_newbieNumbering.integer && g_newbieNamePrefix.string[ 0 ] && +            Q_stricmpn ( client->pers.netname, g_newbieNamePrefix.string, +              strlen(g_newbieNamePrefix.string ) ) == 0 ) ) +      { +        trap_SendConsoleCommand( EXEC_APPEND, va( "!l1 %d", clientNum ) ); +        trap_SendServerCommand( client->ps.clientNum, +          "print \"^5The karma feature automatically !registers all players.\n\"" ); +      } +    } +    else if( client->pers.adminLevel > 0 && +             level.time - level.startTime > 60000 ) +    { +      trap_SendServerCommand( client->ps.clientNum, +        va( "print \"^5Welcome back, your karma is %d\n\"", client->pers.karma / 1000 ) ); +    } +  } +    G_LogPrintf( "ClientBegin: %i\n", clientNum );    if( g_clientUpgradeNotice.integer )    {      if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) )      { -      trap_SendServerCommand( client->ps.clientNum, va( "print \"^3Your client is out of date. Updating your client will allow you to " -          "become an admin on servers and download maps much more quickly. Please replace your client executable with a newer client. \n\"" ) ); - -      trap_SendServerCommand( client->ps.clientNum, va("print \"^3Some available clients: \n" -          "^2TremFusion^7- ^3http://www.tremfusion.net/download/^7\n" -          "^2FSM-Trem^7 - ^3http://code.google.com/p/fsm-trem/^7\n" -          "^2MGClient^7 - ^3http://releases.mercenariesguild.net/client/^7\n\"" ) ); +      trap_SendServerCommand( client->ps.clientNum, va( "print \"^1Your client is out of date. Updating your client will allow you to " +        "become an admin on servers and download maps much more quickly. Please replace your client executable with the one " +        "at ^2http://trem.tjw.org/backport/^1 and reconnect. \n\"" ) );      }    } @@ -1847,6 +1875,15 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles      client->jetpackfuel = mod_jetpackFuel.value;    } +  //free credits +  if( g_freeCredits.integer && ent != spawn ) +  { +    if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) +      client->ps.persistant[ PERS_CREDIT ] = ALIEN_MAX_KILLS; +    else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) +      client->ps.persistant[ PERS_CREDIT ] = HUMAN_MAX_CREDITS; +  } +    G_SetOrigin( ent, spawn_origin );    VectorCopy( spawn_origin, client->ps.origin ); @@ -1999,7 +2036,8 @@ void ClientDisconnect( int clientNum )        Q_strncpyz( ptr->name, ent->client->pers.netname, MAX_NETNAME );      }    } -   + +  //update namelog only if they are not invisible    if ( ent->client->sess.invisible != qtrue )      G_admin_namelog_update( ent->client, qtrue );    G_LeaveTeam( ent ); diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 3857ef1..b9cca49 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -760,19 +760,47 @@ void Cmd_Team_f( gentity_t *ent )    // stop team join spam    if( level.time - ent->client->pers.teamChangeTime < 1000 )      return; + +  if( ent->client->pers.teamSelection != PTE_NONE ) +  { +    int cs_offset; + +    if( ent->client->pers.teamSelection == PTE_ALIENS ) +      cs_offset = 1; +    else +      cs_offset = 0; +    if( level.teamVoteTime[ cs_offset ] ) +    { +      trap_SendServerCommand( ent-g_entities, "print \"Can not leave team during a team vote\n\"" ); +      return; +    } +  } +    // Prevent invisible players from joining a team -  if ( ent->client->sess.invisible == qtrue ) +  if( ent->client->sess.invisible == qtrue )    {      trap_SendServerCommand( ent-g_entities, -          va( "print \"You cannot join a team while invisible\n\"" ) ); +                            va( "print \"You cannot join a team while invisible\n\"" ) );      return;    } -   +    if( oldteam == PTE_ALIENS )      aliens--;    else if( oldteam == PTE_HUMANS )      humans--; +  // practice mode +  if( !force && g_practiceCount.integer ) +  { +    char name[ MAX_NAME_LENGTH ]; + +    G_DecolorString( ent->client->pers.netname, name ); +    if( strstr( name, g_practiceText.string ) ) +    { +      force = qtrue; +    } +  } +    // do warm up    if( g_doWarmup.integer && g_warmupMode.integer == 1 &&        level.time - level.startTime < g_warmup.integer * 1000 ) @@ -938,6 +966,10 @@ void Cmd_Team_f( gentity_t *ent )         Com_sprintf( buf, sizeof( buf ), "%s^7 abandoned humans and joined the aliens.", ent->client->pers.netname );       else         Com_sprintf( buf, sizeof( buf ), "%s^7 joined the aliens.", ent->client->pers.netname ); + +     if( g_modAlienRegenRange.integer ) +       trap_SendServerCommand( ent-g_entities, +         va( "print \"Reminder: aliens must be near base to regenerate health.\n\"" ) );     }     else if( team == PTE_HUMANS ) {       if ( oldteam == PTE_ALIENS ) @@ -991,15 +1023,12 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons    }    if( mode == SAY_ADMINS && -     (!G_admin_permission( other, ADMF_ADMINCHAT ) || other->client->pers.ignoreAdminWarnings ) ) +     (!G_admin_permission( other, ADMF_ADMINCHAT) || other->client->pers.ignoreAdminWarnings ) )       return;    if( BG_ClientListTest( &other->client->sess.ignoreList, ent-g_entities ) )      ignore = qtrue; - -  if ( ignore && g_fullIgnore.integer ) -    return; - +      trap_SendServerCommand( other-g_entities, va( "%s \"%s%s%s%c%c%s\"",      ( mode == SAY_TEAM || mode == SAY_ACTION_T ) ? "tchat" : "chat",      ( ignore ) ? "[skipnotify]" : "", @@ -1032,24 +1061,6 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )      return;    } -  // Spam limit: If they said this message recently, ignore it. -  if( g_spamTime.integer ) -    if ( ( level.time - ent->client->pers.lastMessageTime ) < ( g_spamTime.integer * 1000 ) && -         !Q_stricmp( ent->client->pers.lastMessage, chatText) && -         !G_admin_permission( ent, ADMF_NOCENSORFLOOD ) && -         ent->client->pers.floodDemerits <= g_floodMaxDemerits.integer ) -    { -      trap_SendServerCommand( ent-g_entities, "print \"Your message has been ignored to prevent spam\n\"" ); -      return; -    } -    else -    { -      ent->client->pers.lastMessageTime = level.time; - -      Q_strncpyz( ent->client->pers.lastMessage, chatText, -        sizeof( ent->client->pers.lastMessage ) ); -    } -    // Flood limit.  If they're talking too fast, determine that and return.    if( g_floodMinTime.integer )      if ( G_Flood_Limited( ent ) ) @@ -1057,7 +1068,7 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )        trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );        return;      } - +           if (g_chatTeamPrefix.integer && ent && ent->client )    {      switch( ent->client->pers.teamSelection) @@ -1158,16 +1169,6 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )    Com_sprintf( text, sizeof( text ), "%s^7", chatText ); -  if( ent && ent->client && g_aimbotAdvertBan.integer && ( Q_stricmp( text, "^1N^7ullify for ^1T^7remulous [beta] | Get it at CheatersUtopia.com^7" ) == 0 ) ) -  { -    trap_SendConsoleCommand( 0, -                             va( "!ban %s %s %s\n", -                                 ent->client->pers.ip, -                                 ( g_aimbotAdvertBanTime.string && Q_stricmp( g_aimbotAdvertBanTime.string, "0" ) == 1 ) ? g_aimbotAdvertBanTime.string : "" , -                                   g_aimbotAdvertBanReason.string ) ); -    Q_strncpyz( text, "^7has been caught hacking and will be dealt with.", sizeof( text ) ); -  } -    if( target )    {      G_SayTo( ent, target, mode, color, name, text, prefix ); @@ -1215,11 +1216,14 @@ static void Cmd_SayArea_f( gentity_t *ent )    int    num, i;    int    color = COLOR_BLUE;    const char  *prefix; -  vec3_t range = { HELMET_RANGE, HELMET_RANGE, HELMET_RANGE }; +  vec3_t range = { 1000.0f, 1000.0f, 1000.0f };    vec3_t mins, maxs;    char   *msg = ConcatArgs( 1 );    char   name[ 64 ]; +  for(i = 0; i < 3; i++ ) +    range[ i ] = g_sayAreaRange.value; +     if( g_floodMinTime.integer )     if ( G_Flood_Limited( ent ) )     { @@ -1373,6 +1377,31 @@ static void Cmd_Say_f( gentity_t *ent )      }    } +  args = G_SayConcatArgs(0); +  if( !Q_stricmpn( args, "say /join", 9 ) ) +  { +    Cmd_Join_f( ent ); +    return; +  } +  if( !Q_stricmpn( args, "say /part", 9 ) ) +  { +    Cmd_Part_f( ent ); +    return; +  } +  if( !Q_stricmpn( args, "say /0", 6 ) || +      !Q_stricmpn( args, "say /1", 6 ) || +      !Q_stricmpn( args, "say /2", 6 ) || +      !Q_stricmpn( args, "say /3", 6 ) || +      !Q_stricmpn( args, "say /4", 6 ) || +      !Q_stricmpn( args, "say /5", 6 ) || +      !Q_stricmpn( args, "say /6", 6 ) || +      !Q_stricmpn( args, "say /7", 6 ) || +      !Q_stricmpn( args, "say /8", 6 ) || +      !Q_stricmpn( args, "say /9", 6 ) ) +  { +    Cmd_Channel_f( ent ); +    return; +  }    if( trap_Argc( ) < 2 )      return; @@ -1417,6 +1446,244 @@ static void Cmd_Tell_f( gentity_t *ent )      G_Say( ent, ent, SAY_TELL, p );  } +void Cmd_Join_f( gentity_t *ent ) +{ +  char pass[CHAT_MAXPASS]; +  char arg[MAX_TOKEN_CHARS]; +  int  skipargs = 0; +  int  chan; +  int  i; +  gentity_t *target; + +  if( g_floodMinTime.integer && +      G_Flood_Limited( ent ) ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"Your chat is flood-limited; wait before chatting again\n\"" ); +    return; +  } + +  G_SayArgv( skipargs, arg, sizeof( arg ) ); +  if( !Q_stricmp( arg, "say" ) ) +    skipargs++; + +  if( G_SayArgc( ) < 2 + skipargs ) +  { +    char message[ 64 ]; +    int n; +     +    n = 0; +    for( i = 0; i < CHAT_MAXCHAN && n < 64 - 3; i++ ) +    { +      if( ent->client->pers.chat[i][0] ) +      { +        message[ n ] = ' '; n++; +        message[ n ] = '0' + i; n++; +      } +    } +    if( n == 0) +      Com_sprintf( message, sizeof( message ), " no channels, use: /join [0-%d] (password)", CHAT_MAXCHAN - 1 ); +    else +      message[ n ] = '\0'; + +    trap_SendServerCommand( ent-g_entities, va( "print \"joined in:%s\n\"", message ) ); +    return; +  } + +  G_SayArgv( 1 + skipargs, arg, sizeof( arg ) ); +  chan = atoi( arg ); +  if( chan < 0 || chan >= CHAT_MAXCHAN ) +  { +    trap_SendServerCommand( ent-g_entities, +     va( "print \"^3/join^7: invalid channel, usage: /join [0-%d] (password)\n\"", CHAT_MAXCHAN - 1 ) ); +    return; +  } + +  pass[ 0 ] = '\0'; +  if ( G_SayArgc( ) >= 2 + skipargs ) +    G_SayArgv( 2 + skipargs, pass, sizeof( pass ) ); +  if( pass[ 0 ] == '\0' ) +    Q_strncpyz( pass, "default", sizeof( pass ) ); + +  if( !Q_stricmp( ent->client->pers.chat[chan], pass ) ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"^3/join^7: already join to channel %d\n\"", chan ) ); +    return; +  } + +  G_LogPrintf( "join: channel %d %s^7\n", chan, ent->client->pers.netname ); +  Q_strncpyz( ent->client->pers.chat[chan], pass, sizeof( ent->client->pers.chat[chan] ) ); +  G_admin_chat_update( ent, chan ); + +  for( i = 0; i < level.maxclients; i++ ) +  { +    target = &g_entities[ i ]; +    if( target && target->client && +        target->client->pers.connected == CON_CONNECTED && +        !Q_stricmp( target->client->pers.chat[ chan ], pass ) ) +    { +      trap_SendServerCommand( i, va( "print \"join: %s^7 has joined channel #%d\n\"", +        ent->client->pers.netname, chan ) ); +    } +  } +} + +void Cmd_Part_f( gentity_t *ent ) +{ +  char arg[MAX_TOKEN_CHARS]; +  int  skipargs = 0; +  int  chan; +  int  i; +  gentity_t *target; + +  G_SayArgv( skipargs, arg, sizeof( arg ) ); +  if( !Q_stricmp( arg, "say" ) ) +    skipargs++; + +  if( G_SayArgc( ) < 2 + skipargs ) +  { +    trap_SendServerCommand( ent-g_entities, va( "print \"^3/part^7 usage: /part [0-%d]\n\"", CHAT_MAXCHAN - 1 ) ); +    return; +  } + +  if( g_floodMinTime.integer && +      G_Flood_Limited( ent ) ) +  { +    trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); +    return; +  } + +  G_SayArgv( 1 + skipargs, arg, sizeof( arg ) ); +  chan = atoi( arg ); +  if( chan < 0 || chan >= CHAT_MAXCHAN ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"^3/part^7: invalid channel, available channels are 0 to %d\n\"", CHAT_MAXCHAN - 1 ) ); +    return; +  } + +  if( ent->client->pers.chat[chan][0] == '\0' ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"^3/part^7: not in that channel\n\"" ); +    return; +  } + +  for( i = 0; i < level.maxclients; i++ ) +  { +    target = &g_entities[ i ]; +    if( target && target->client && +        target->client->pers.connected == CON_CONNECTED && +        !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) +    { +      trap_SendServerCommand( i, va( "print \"part: %s^7 has left channel #%d\n\"", +        ent->client->pers.netname, chan ) ); +    } +  } + +  G_LogPrintf( "part: channel %d %s^7\n", chan, ent->client->pers.netname ); +  Q_strncpyz( ent->client->pers.chat[chan], "", sizeof( ent->client->pers.chat[chan] ) ); +  G_admin_chat_update( ent, chan ); +} + +void Cmd_Channel_f( gentity_t *ent ) +{ +  char arg[MAX_TOKEN_CHARS]; +  char str[MAX_STRING_CHARS]; +  char *cmd, *p; +  char location[ 64 ]; +  char escaped[ 64 ]; +  int  chan; +  int  i; +  int  num = 0; +  int  skipargs = 0; +  qboolean who = qfalse; +  gentity_t *target; + +  if( g_floodMinTime.integer && +      G_Flood_Limited( ent ) ) +  { +    trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); +    return; +  } + +  G_SayArgv( skipargs, arg, sizeof( arg ) ); +  if( !Q_stricmp( arg, "say" ) ) +  { +    skipargs++; +    G_SayArgv( skipargs, arg, sizeof( arg ) ); +  } +  cmd = arg; +  if( *cmd == '/' ) +    cmd++; +  chan = atoi( cmd ); +  if( chan < 0 || chan >= CHAT_MAXCHAN || +      ent->client->pers.chat[chan][0] == '\0' ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"^3/%s^7: you are not joined to that channel, use /join\n\"", cmd ) ); +    return; +  } + +  p = G_SayConcatArgs( 1 + skipargs ); +  if( !p || p[0] == '\0' ) +    who = qtrue; + +  if( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) +  { +    Com_sprintf( escaped, sizeof( escaped ), " (^4%s^7)", location ); +  } +  else +  { +    escaped[ 0 ] = '\0'; +  } + +  str[ 0 ] = '\0'; +  for( i = 0; i < level.maxclients; i++ ) +  { +    target = &g_entities[ i ]; +    if( target && target->client && +        target->client->pers.connected == CON_CONNECTED && +        !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) +    { +      if( num > 0 ) +        Q_strcat( str, sizeof( str ), "^7, " ); +      Q_strcat( str, sizeof( str ), target->client->pers.netname ); +      num++; +    } +  } + +  if( who ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"channel ^3%d^7 has %s%s and contains %d player%s%s%s\n\"", +      chan, +      ( !Q_stricmp( ent->client->pers.chat[ chan ], "default" ) ) ? "no password" : "password ", +      ( !Q_stricmp( ent->client->pers.chat[ chan ], "default" ) ) ? "" : ent->client->pers.chat[ chan ], +      num, +      ( num == 1 ) ? "" : "s", +      ( num ) ? ": " : "", +      str ) ); +    return; +  } + +  G_LogPrintf( "channel%d: %s^7 %s\n", chan, ent->client->pers.netname, p ); +  for( i = 0; i < level.maxclients; i++ ) +  { +    target = &g_entities[ i ]; +    if( target && target->client && +        target->client->pers.connected == CON_CONNECTED && +        !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) +    { +      trap_SendServerCommand( i, va( "chat \"(^1#%d^7 %d) [%s^7]%s: ^1%s^7\"", +        chan, num, ent->client->pers.netname, +        ( OnSameTeam( target, ent ) ) ? escaped : "", +        p ) ); +    } +  } +} +   /*  ==================  Cmd_Where_f @@ -1427,6 +1694,24 @@ void Cmd_Where_f( gentity_t *ent )    trap_SendServerCommand( ent-g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) );  } +static int map_vote_percent( const char *map, int fallback ) +{ +  char maps[ MAX_CVAR_VALUE_STRING ]; +  char *token, *token_p; + +  if( !g_popularMapsVotePercent.integer ) +    return fallback; + +  Q_strncpyz( maps, g_popularMaps.string, sizeof( maps ) ); +  token_p = maps; +  while( *( token = COM_Parse( &token_p ) ) ) +  { +    if( !Q_stricmp( token, map ) ) +      return g_popularMapsVotePercent.integer; +  } + +  return fallback; +}  static qboolean map_is_votable( const char *map )  { @@ -1469,7 +1754,7 @@ void Cmd_CallVote_f( gentity_t *ent )    arg1plus = G_SayConcatArgs( 1 );    arg2plus = G_SayConcatArgs( 2 ); -   +    // Invisible players cannot call votes    if( ent->client->sess.invisible == qtrue )    { @@ -1483,6 +1768,12 @@ void Cmd_CallVote_f( gentity_t *ent )      return;    } +  if( G_admin_permission( ent, ADMF_NO_VOTE ) ) +  { +    trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" ); +    return; +  } +      // Flood limit.  If they're talking too fast, determine that and return.    if( g_floodMinTime.integer )      if ( G_Flood_Limited( ent ) ) @@ -1491,13 +1782,6 @@ void Cmd_CallVote_f( gentity_t *ent )        return;      } -  //see if they can vote -  if( G_admin_permission( ent, ADMF_NO_VOTE ) ) -  { -    trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" ); -    return; -  } -    if( g_voteMinTime.integer      && ent->client->pers.firstConnect       && level.time - ent->client->pers.enterTime < g_voteMinTime.integer * 1000 @@ -1554,6 +1838,10 @@ void Cmd_CallVote_f( gentity_t *ent )      {        G_admin_maplog_result( "m" );      } +    else if( !Q_stricmpn( level.voteString, "!restart", 8 ) ) +    { +      G_admin_maplog_result( "l" ); +    }      level.voteExecuteTime = 0;      trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) ); @@ -1628,7 +1916,7 @@ void Cmd_CallVote_f( gentity_t *ent )      }      if( clientNum != -1 && -        level.clients[ clientNum ].pers.connected != CON_CONNECTED ) +      level.clients[ clientNum ].pers.connected == CON_DISCONNECTED )      {        clientNum = -1;      } @@ -1659,6 +1947,8 @@ void Cmd_CallVote_f( gentity_t *ent )    if( !Q_stricmp( arg1, "kick" ) )    { +    char n1[ MAX_NAME_LENGTH ]; +      if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) )      {        trap_SendServerCommand( ent-g_entities, @@ -1673,9 +1963,13 @@ void Cmd_CallVote_f( gentity_t *ent )        "!ban %s \"%s\" vote kick", level.clients[ clientNum ].pers.ip,        g_adminTempBan.string );      if ( reason[0]!='\0' ) -      Q_strcat( level.voteString, sizeof( level.voteDisplayString ), va( "(%s^7)", reason ) ); +      Q_strcat( level.voteString, sizeof( level.voteString ), va( ": %s^7", reason ) ); +    G_SanitiseString( ent->client->pers.netname, n1, sizeof( n1 ) ); +    Q_strcat( level.voteString, sizeof( level.voteString ), va( ", %s", n1 ) );      Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ),        "Kick player \'%s\'", name ); + +    level.votePassThreshold = g_kickVotesPercent.integer;    }    else if( !Q_stricmp( arg1, "spec" ) )    { @@ -1772,7 +2066,7 @@ void Cmd_CallVote_f( gentity_t *ent )      Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );      Com_sprintf( level.voteDisplayString,          sizeof( level.voteDisplayString ), "Change to map '%s'", arg2 ); -    level.votePassThreshold = g_mapVotesPercent.integer; +    level.votePassThreshold = map_vote_percent( arg2, g_mapVotesPercent.integer );    }    else if( !Q_stricmp( arg1, "nextmap" ) )    { @@ -1808,7 +2102,7 @@ void Cmd_CallVote_f( gentity_t *ent )        "set g_nextMap %s", arg2 );      Com_sprintf( level.voteDisplayString,          sizeof( level.voteDisplayString ), "Set the next map to '%s^7'", arg2 ); -    level.votePassThreshold = g_mapVotesPercent.integer; +    level.votePassThreshold = map_vote_percent( arg2, g_mapVotesPercent.integer );    }    else if( !Q_stricmp( arg1, "draw" ) )    { @@ -1817,16 +2111,53 @@ void Cmd_CallVote_f( gentity_t *ent )          "End match in a draw" );      level.votePassThreshold = g_mapVotesPercent.integer;    } +  else if( !Q_stricmp( arg1, "layout" ) ) +  { +    char map[ 64 ]; + +    if( g_mapvoteMaxTime.integer  +      && (( level.time - level.startTime ) >= g_mapvoteMaxTime.integer * 1000 ) +      && !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT )  +      && (level.numPlayingClients > 0 && level.numConnectedClients>1) ) +    { +       trap_SendServerCommand( ent-g_entities, va( +         "print \"You cannot call for a layout change after %d seconds\n\"", +         g_mapvoteMaxTime.integer ) ); +       G_admin_adminlog_log( ent, "vote", NULL, 0, qfalse ); +       return; +    } + +    trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); +    if( Q_stricmp( arg2, "*BUILTIN*" ) && +        !trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, arg2 ), NULL, FS_READ ) ) +    { +      trap_SendServerCommand( ent - g_entities, va( "print \"callvote: " +        "layout '%s' could not be found on the server\n\"", arg2 ) ); +      return; +    } +    Com_sprintf( level.voteString, sizeof( level.voteString ), "!restart %s", arg2 ); +    Com_sprintf( level.voteDisplayString, +        sizeof( level.voteDisplayString ), "Change to map layout '%s'", arg2 ); +    level.votePassThreshold = g_mapVotesPercent.integer; +  }     else if( !Q_stricmp( arg1, "poll" ) )      { -      if( arg2plus[ 0 ] == '\0' ) +      if(!g_pollVotes.integer) +      { +       trap_SendServerCommand( ent-g_entities, "print \"Poll Votes have been disabled\n\"" ); +       return; +      } +      else if( arg2plus[ 0 ] == '\0' )        {          trap_SendServerCommand( ent-g_entities, "print \"callvote: You forgot to specify what people should vote on.\n\"" );          return;        } -      Com_sprintf( level.voteString, sizeof( level.voteString ), nullstring); -      Com_sprintf( level.voteDisplayString, -          sizeof( level.voteDisplayString ), "[Poll] \'%s\'", arg2plus ); +      else +       { +         Com_sprintf( level.voteString, sizeof( level.voteString ), nullstring); +         Com_sprintf( level.voteDisplayString, +              sizeof( level.voteDisplayString ), "[Poll] \'%s" S_COLOR_WHITE "\'", arg2plus ); +       }     }     else if( !Q_stricmp( arg1, "sudden_death" ) ||       !Q_stricmp( arg1, "suddendeath" ) ) @@ -1858,41 +2189,36 @@ void Cmd_CallVote_f( gentity_t *ent )       }     } -   else if( !Q_stricmp( arg1, "extend" ) ) -   { -     if( !g_extendVotesPercent.integer ) -     { -       trap_SendServerCommand( ent-g_entities, "print \"Extend votes have been disabled\n\"" ); -       return; -     } -     if( g_extendVotesCount.integer -         && level.extend_vote_count >= g_extendVotesCount.integer ) -     { -       trap_SendServerCommand( ent-g_entities, -                               va( "print \"callvote: Maximum number of %d extend votes has been reached\n\"", -                                   g_extendVotesCount.integer ) ); -       return; -     } -     if( !g_timelimit.integer ) { -       trap_SendServerCommand( ent-g_entities, -                               "print \"This match has no timelimit so extend votes wont work\n\"" ); -       return; -     } -     if( level.time - level.startTime < -         ( g_timelimit.integer - g_extendVotesTime.integer / 2 ) * 60000 ) -     { -       trap_SendServerCommand( ent-g_entities, -                               va( "print \"callvote: Extend votes only allowed with less than %d minutes remaining\n\"", -                                   g_extendVotesTime.integer / 2 ) ); -       return; -     } -     level.extend_vote_count++; -     level.votePassThreshold = g_extendVotesPercent.integer;  -     Com_sprintf( level.voteString, sizeof( level.voteString ), -                  "timelimit %i", g_timelimit.integer + g_extendVotesTime.integer ); -     Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), -                  "Extend the timelimit by %d minutes", g_extendVotesTime.integer ); -   } +  else if( !Q_stricmp( arg1, "extend" ) ) +  { +    if( !g_extendVotesPercent.integer ) +    { +      trap_SendServerCommand( ent-g_entities, "print \"Extend votes have been disabled\n\"" ); +      return; +    } +    if( g_extendVotesCount.integer +      && level.extend_vote_count >= g_extendVotesCount.integer ) +    { +      trap_SendServerCommand( ent-g_entities, +        va( "print \"callvote: Maximum number of %d extend votes has been reached\n\"", +        g_extendVotesCount.integer ) ); +      return; +    } +    if( level.time - level.startTime < +      ( g_timelimit.integer - g_extendVotesTime.integer / 2 ) * 60000 ) +    { +      trap_SendServerCommand( ent-g_entities, +        va( "print \"callvote: Extend votes only allowed with less than %d minutes remaining\n\"", +        g_extendVotesTime.integer / 2 ) ); +      return; +    } +    level.extend_vote_count++; +    level.votePassThreshold = g_extendVotesPercent.integer;  +    Com_sprintf( level.voteString, sizeof( level.voteString ), +      "timelimit %i", g_timelimit.integer + g_extendVotesTime.integer ); +    Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), +      "Extend the timelimit by %d minutes", g_extendVotesTime.integer ); +  }    else    {      qboolean match = qfalse; @@ -1976,13 +2302,13 @@ void Cmd_CallVote_f( gentity_t *ent )          match = qtrue;          break;        } -   } +    }      if( !match )      {        trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" );        trap_SendServerCommand( ent-g_entities, "print \"Valid vote commands are: " -        "map, map_restart, draw, extend, nextmap, kick, spec, mute, unmute, poll, and sudden_death\n" ); +        "map, map_restart, nextmap, layout, draw, extend, kick, spec, mute, unmute, poll, and sudden_death\n" );        if( customVoteKeys[ 0 ] != '\0' )          trap_SendServerCommand( ent-g_entities,            va( "print \"Additional custom vote commands: %s\n\"", customVoteKeys ) ); @@ -1992,7 +2318,7 @@ void Cmd_CallVote_f( gentity_t *ent )    if( level.votePassThreshold!=50 )    { -    Q_strcat( level.voteDisplayString, sizeof( level.voteDisplayString ), va( "^7 (Needs > %d percent)", level.votePassThreshold ) ); +    Q_strcat( level.voteDisplayString, sizeof( level.voteDisplayString ), va( " (Needs > %d percent)", level.votePassThreshold ) );    }    if ( reason[0]!='\0' ) @@ -2002,6 +2328,7 @@ void Cmd_CallVote_f( gentity_t *ent )    trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE           " called a vote: %s" S_COLOR_WHITE "\n\"", ent->client->pers.netname, level.voteDisplayString ) ); +  trap_SendServerCommand( -1, "cp \"A vote has been called\n^2F1: Yes^7, ^1F2: No^7\"" );    G_LogPrintf("Vote: %s^7 called a vote: %s^7\n", ent->client->pers.netname, level.voteDisplayString ); @@ -2138,22 +2465,21 @@ void Cmd_CallTeamVote_f( gentity_t *ent )    else if(team==PTE_HUMANS)      numVoters = level.numHumanClients; -  if( !g_allowVote.integer ) +  if( !g_allowVote.integer || g_allowVote.integer == 2 )    { -    trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here\n\"" ); +    trap_SendServerCommand( ent-g_entities, "print \"Team voting not allowed here\n\"" );      return;    } -  if( level.teamVoteTime[ cs_offset ] ) +  if( G_admin_permission( ent, ADMF_NO_VOTE ) )    { -    trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress\n\"" ); +    trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" );      return;    } -  //see if they can vote -  if( G_admin_permission( ent, ADMF_NO_VOTE ) ) +  if( level.teamVoteTime[ cs_offset ] )    { -    trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" ); +    trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress\n\"" );      return;    } @@ -2300,8 +2626,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent )      }    } +  level.teamVotePassThreshold[ cs_offset ] = 50; +    if( !Q_stricmp( arg1, "kick" ) )    { +    char n1[ MAX_NAME_LENGTH ]; +      if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) )      {        trap_SendServerCommand( ent-g_entities, @@ -2311,15 +2641,28 @@ void Cmd_CallTeamVote_f( gentity_t *ent )        return;      } +    if( g_allowVote.integer == 3 ) +    { +      trap_SendServerCommand( ent-g_entities, "print \"Team kick votes are disabled\n\"" ); +      return; +    }      // use ip in case this player disconnects before the vote ends      Com_sprintf( level.teamVoteString[ cs_offset ],        sizeof( level.teamVoteString[ cs_offset ] ),        "!ban %s \"%s\" team vote kick", level.clients[ clientNum ].pers.ip,        g_adminTempBan.string ); +    if( reason[0] ) +      Q_strcat( level.teamVoteString[ cs_offset ], sizeof( level.teamVoteString[ cs_offset ] ), +        va( ": %s", reason ) ); +    G_SanitiseString( ent->client->pers.netname, n1, sizeof( n1 ) ); +    Q_strcat( level.teamVoteString[ cs_offset ], sizeof( level.teamVoteString[ cs_offset ] ), +      va( ", %s", n1 ) );      Com_sprintf( level.teamVoteDisplayString[ cs_offset ],          sizeof( level.teamVoteDisplayString[ cs_offset ] ),          "Kick player '%s'", name ); + +    level.teamVotePassThreshold[ cs_offset ] = g_kickVotesPercent.integer;    }    else if( !Q_stricmp( arg1, "denybuild" ) )    { @@ -2405,6 +2748,16 @@ void Cmd_CallTeamVote_f( gentity_t *ent )    }    else if( !Q_stricmp( arg1, "admitdefeat" ) )    { +    if( g_defeatVoteMinTime.integer && +        level.time - level.startTime < abs( g_defeatVoteMinTime.integer ) * 1000 && +        ( !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) || g_defeatVoteMinTime.integer < 0 ) ) +    { +       trap_SendServerCommand( ent-g_entities, va( +         "print \"You cannot admit defeat until after %d seconds\n\"", +         g_defeatVoteMinTime.integer ) ); +       G_admin_adminlog_log( ent, "vote", NULL, 0, qfalse ); +       return; +    }      if( numVoters <=1 )      {        trap_SendServerCommand( ent-g_entities, @@ -2440,6 +2793,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent )    ent->client->pers.voteCount++;    G_admin_adminlog_log( ent, "teamvote", arg1, 0, qtrue ); + +  if( level.teamVotePassThreshold[ cs_offset ] != 50 ) +  { +    Q_strcat( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ), +      va( " (Needs > %d percent)", level.teamVotePassThreshold[ cs_offset ] ) ); +  }    if ( reason[0]!='\0' )      Q_strcat( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ), va( " Reason: '%s'^7", reason ) ); @@ -2453,6 +2812,7 @@ void Cmd_CallTeamVote_f( gentity_t *ent )      {        trap_SendServerCommand( i, va("print \"%s " S_COLOR_WHITE              "called a team vote: %s^7 \n\"", ent->client->pers.netname, level.teamVoteDisplayString[ cs_offset ] ) ); +      trap_SendServerCommand( i, "cp \"A team vote has been called\n^2F1: Yes^7, ^1F2: No^7\"" );      }      else if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) &&                ( ( !Q_stricmp( arg1, "kick" ) || !Q_stricmp( arg1, "denybuild" ) ) ||  @@ -2656,7 +3016,6 @@ void Cmd_Class_f( gentity_t *ent )    vec3_t    mins, maxs;    int       num;    gentity_t *other; -  qboolean  humanNear = qfalse;    clientNum = ent->client - level.clients; @@ -2779,21 +3138,11 @@ void Cmd_Class_f( gentity_t *ent )          if( ( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||              ( other->s.eType == ET_BUILDABLE && other->biteam == BIT_HUMANS ) )          { -          humanNear = qtrue; -        } -        //If its the OM, then ignore all humans. -        if(other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_OVERMIND) -        { -          humanNear = qfalse; -          break; +          G_TriggerMenu( clientNum, MN_A_TOOCLOSE ); +          return;          }        } -      if(humanNear == qtrue) { -        G_TriggerMenu( clientNum, MN_A_TOOCLOSE ); -        return; -      } -        if( !level.overmindPresent )        {          G_TriggerMenu( clientNum, MN_A_NOOVMND_EVOLVE ); @@ -2902,19 +3251,33 @@ DBCommand  Send command to all designated builders of selected team  =================  */ -void DBCommand( pTeam_t team, const char *text ) +void DBCommand( gentity_t *builder, pTeam_t team, const char *text )  {    int i;    gentity_t *ent; +  if( g_floodMinTime.integer && G_Flood_Limited( builder ) ) +  { +    trap_SendServerCommand( builder-g_entities, +      "print \"Your deconstruct attempt is flood-limited; wait before trying again\n\"" ); +    return; +  } + +  trap_SendServerCommand( builder-g_entities,  +    "print \"This structure is protected by designated builder\n\"" ); +    for( i = 0, ent = g_entities + i; i < level.maxclients; i++, ent++ )    { -    if( !ent->client || ( ent->client->pers.connected != CON_CONNECTED ) || -        ( ent->client->pers.teamSelection != team ) || -        !ent->client->pers.designatedBuilder ) +    if( !ent->client || ent->client->pers.connected != CON_CONNECTED )        continue; -    trap_SendServerCommand( i, text ); +    if( ( ent->client->pers.teamSelection == team && +          ent->client->pers.designatedBuilder ) || +      ( ent->client->pers.teamSelection == PTE_NONE && +        G_admin_permission( ent, ADMF_SPEC_ALLCHAT ) ) ) +    { +      trap_SendServerCommand( i, text ); +    }    }  } @@ -2937,6 +3300,23 @@ void Cmd_Destroy_f( gentity_t *ent )        "print \"Your building rights have been revoked\n\"" );      return;    } +  if( ent->client->pers.paused ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"You may not deconstruct while paused\n\"" ); +    return; +  } + +  if( g_newbieNoBuild.integer +    && g_newbieNamePrefix.string[ 0 ] +    && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"You cannot build until you set a name\n%s%s\"", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) ); +    return; +  }    trap_Argv( 0, cmd, sizeof( cmd ) );    if( Q_stricmp( cmd, "destroy" ) == 0 ) @@ -2947,11 +3327,10 @@ void Cmd_Destroy_f( gentity_t *ent )      if( ( ent->client->hovel->s.eFlags & EF_DBUILDER ) &&        !ent->client->pers.designatedBuilder )      { -      trap_SendServerCommand( ent-g_entities,  -        "print \"This structure is protected by designated builder\n\"" ); -      DBCommand( ent->client->pers.teamSelection, -        va( "print \"%s^3 has attempted to decon a protected structure!\n\"", -          ent->client->pers.netname ) ); +      DBCommand( ent, ent->client->pers.teamSelection, +        va( "print \"%s^3 has attempted to decon a protected %s!\n\"", +          ent->client->pers.netname, +          BG_FindHumanNameForBuildable( ent->client->hovel->s.modelindex ) ) );        return;      }      G_Damage( ent->client->hovel, ent, ent, forward, ent->s.origin,  @@ -2981,21 +3360,13 @@ void Cmd_Destroy_f( gentity_t *ent )        if( ( traceEnt->s.eFlags & EF_DBUILDER ) &&          !ent->client->pers.designatedBuilder )        { -        trap_SendServerCommand( ent-g_entities,  -          "print \"This structure is protected by designated builder\n\"" ); -        DBCommand( ent->client->pers.teamSelection, -          va( "print \"%s^3 has attempted to decon a protected structure!\n\"", -            ent->client->pers.netname ) ); +        DBCommand( ent, ent->client->pers.teamSelection, +          va( "print \"%s^3 has attempted to decon a protected %s!\n\"", +            ent->client->pers.netname, +            BG_FindHumanNameForBuildable( traceEnt->s.modelindex ) ) );          return;        } -      // Check the minimum level to deconstruct -      if ( G_admin_level( ent ) < g_minDeconLevel.integer && !ent->client->pers.designatedBuilder ) -      { -        trap_SendServerCommand( ent-g_entities, -          "print \"You do not have deconstructuction rights.\n\"" ); -        return; -      }        // Prevent destruction of the last spawn        if( g_markDeconstruct.integer != 1 && !g_cheats.integer ) @@ -3024,8 +3395,7 @@ void Cmd_Destroy_f( gentity_t *ent )                !BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) ||              ( g_suddenDeathMode.integer == SDMODE_BP &&                BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) || -            g_suddenDeathMode.integer == SDMODE_NO_BUILD  || -            g_suddenDeathMode.integer == SDMODE_NO_DECON ) ) +            g_suddenDeathMode.integer == SDMODE_NO_BUILD ) )        {          trap_SendServerCommand( ent-g_entities,            "print \"During Sudden Death you can only decon buildings that " @@ -3055,6 +3425,7 @@ void Cmd_Destroy_f( gentity_t *ent )              new = G_Alloc( sizeof( buildHistory_t ) );              new->ID = ( ++level.lastBuildID > 1000 )                   ? ( level.lastBuildID = 1 ) : level.lastBuildID; +            new->time = level.time;              new->ent = ent;              new->name[ 0 ] = 0;              new->buildable = traceEnt->s.modelindex; @@ -3078,6 +3449,8 @@ void Cmd_Destroy_f( gentity_t *ent )                ent->client->pers.netname,                 BG_FindNameForBuildable( traceEnt->s.modelindex ) );          } +          else if( deconstruct ) +            G_RecoverBuildPoints( traceEnt );            if( !deconstruct )              G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10000, 0, MOD_SUICIDE ); @@ -3112,12 +3485,21 @@ void Cmd_Mark_f( gentity_t *ent )        "print \"Your building rights have been revoked\n\"" );      return;    } +  if( ent->client->pers.paused ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"You may not mark while paused\n\"" ); +    return; +  } -  // Check the minimum level to deconstruct -  if ( G_admin_level( ent ) < g_minDeconLevel.integer  && !ent->client->pers.designatedBuilder && g_minDeconAffectsMark.integer > 0 ) +  if( g_newbieNoBuild.integer +    && g_newbieNamePrefix.string[ 0 ] +    && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 )    {      trap_SendServerCommand( ent-g_entities, -      "print \"You do not have deconstructuction rights.\n\"" ); +      va( "print \"You cannot build until you set a name\n%s%s\"", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );      return;    } @@ -3164,8 +3546,7 @@ void Cmd_Mark_f( gentity_t *ent )                !BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) ||              ( g_suddenDeathMode.integer == SDMODE_BP &&                BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) || -            g_suddenDeathMode.integer == SDMODE_NO_BUILD || -            g_suddenDeathMode.integer == SDMODE_NO_DECON ) ) +            g_suddenDeathMode.integer == SDMODE_NO_BUILD ) )        {          trap_SendServerCommand( ent-g_entities,            "print \"During Sudden Death you can only mark buildings that " @@ -3186,6 +3567,65 @@ void Cmd_Mark_f( gentity_t *ent )    }  } +void Cmd_Aim_f( gentity_t *ent ) +{ +  vec3_t      forward, end; +  trace_t     tr; +  gentity_t   *traceEnt; + +  if( !g_turretAim.integer ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"Turret aim is disabled\n\"" ); +    return; +  } + +  if( ent->client->pers.denyBuild || +      ent->client->pers.paused ) +    return; + +  if( g_newbieNoBuild.integer && +      g_newbieNamePrefix.string[ 0 ] && +      !Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"You cannot use /aim until you set a name\n%s%s\"", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) ); +    return; +  } + +  if( !( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) ) +  { +    AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL ); +    VectorMA( ent->client->ps.origin, 100, forward, end ); + +    trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); +    traceEnt = &g_entities[ tr.entityNum ]; + +    if( tr.fraction < 1.0f && +        ( traceEnt->s.eType == ET_BUILDABLE ) && +        ( traceEnt->s.modelindex == BA_H_MGTURRET ) && +        ( traceEnt->biteam == ent->client->pers.teamSelection ) && +        ( ( ent->client->ps.weapon >= WP_ABUILD ) && +          ( ent->client->ps.weapon <= WP_HBUILD ) ) ) +    { +      if( traceEnt->rotatorAngle >= 360.0f ) +      { +        traceEnt->rotatorAngle = 0.0f; +        trap_SendServerCommand( ent-g_entities, "print \"Turret aim direction disabled\n\"" ); +      } +      else +      { +        traceEnt->rotatorAngle = ent->client->ps.viewangles[ YAW ]; +        while( traceEnt->rotatorAngle < 360.0f ) +          traceEnt->rotatorAngle += 360.0f; +        trap_SendServerCommand( ent-g_entities, "print \"Turret aim direction enabled\n\"" ); +      } +    } +  } +} +  /*  ================= @@ -3507,12 +3947,6 @@ void Cmd_Buy_f( gentity_t *ent )      if( !Q_stricmp( s, "retrigger" ) )        ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;    } -  if( ent->client->pers.paused ) -  { -    trap_SendServerCommand( ent-g_entities, -      "print \"You may not deconstruct while paused\n\"" ); -    return; -  }    //update ClientInfo    ClientUserinfoChanged( ent->client->ps.clientNum, qfalse ); @@ -3688,7 +4122,18 @@ void Cmd_Build_f( gentity_t *ent )    if( ent->client->pers.paused )    {      trap_SendServerCommand( ent-g_entities, -      "print \"You may not mark while paused\n\"" ); +      "print \"You may not build while paused\n\"" ); +    return; +  } + +  if( g_newbieNoBuild.integer +    && g_newbieNamePrefix.string[ 0 ] +    && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 ) +  { +    trap_SendServerCommand( ent-g_entities, +      va( "print \"You cannot build until you set a name\n%s%s\"", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "", +      ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );      return;    } @@ -3722,6 +4167,13 @@ void Cmd_Build_f( gentity_t *ent )      }    } +  if( g_vampireDeath.integer ) +  { +    trap_SendServerCommand( ent-g_entities, +      "print \"Building is not allowed during Vampire Sudden Death\n\"" ); +    return; +  } +    team = ent->client->ps.stats[ STAT_PTEAM ];    if( buildable != BA_NONE && @@ -4005,113 +4457,6 @@ char *G_statsString( statsCounters_t *sc, pTeam_t *pt )    return s;  } - /* - ================= - Cmd_AllStats_f - ================= - */ - void Cmd_AllStats_f( gentity_t *ent ) - { -    int i; -    int NextViewTime; -    int NumResults = 0; -    int Teamcolor = 3; -    gentity_t *tmpent; -  -    //check if ent exists -    if(!ent) return; -  -    NextViewTime = ent->client->pers.statscounters.AllstatstimeLastViewed + g_AllStatsTime.integer * 1000; -    //check if you can use the cmd at this time -    if( !level.intermissiontime && level.time < NextViewTime) -    { -      ADMP( va("You may only check your stats every %i Seconds and during intermission. Next valid time is %d:%02d\n",( g_AllStatsTime.integer ) ? ( g_AllStatsTime.integer ) : 60, ( NextViewTime / 60000 ), ( NextViewTime / 1000 ) % 60 ) ); -      return; -    } -    //see if allstats is enabled -    if( !g_AllStats.integer ) -    { -     ADMP( "AllStats has been disabled\n"); -     return; -    } -    ADMP("^3K^2=^7Kills ^3A^2=^7Assists ^3SK^2=^7StructKills\n^3D^2=^7Deaths ^3F^2=^7Feeds ^3S^2=^7Suicides ^3TK^2=^7Teamkills\n^3DD^2=^7Damage done ^3TDD^2=^7Team Damage done\n^3SB^2=^7Structs Built\n\n" ); -    //display a header describing the data -    ADMP( "^3 #|  K   A  SK|  D   F   S  TK|   DD   TDD| SB| Name\n" ); - -    //loop through the clients that are connected -    for( i = 0; i < level.numConnectedClients; i++ )  -    { -       //assign a tempent 4 the hell of it -       tmpent = &g_entities[ level.sortedClients[ i ] ]; -  -       //check for what mode we are working in and display relevent data -       if( g_AllStats.integer == 1 ) -       { -            //check if client is connected and on same team -            if( tmpent->client && tmpent->client->pers.connected == CON_CONNECTED && tmpent->client->pers.teamSelection == ent->client->pers.teamSelection && tmpent->client->pers.teamSelection != PTE_NONE ) -            { -               NumResults++; -               if( tmpent->client->pers.teamSelection == PTE_ALIENS ) Teamcolor = 1; -               if( tmpent->client->pers.teamSelection == PTE_HUMANS ) Teamcolor = 5; -               ADMP( va( "^%i%2i^3|^%i%3i %3i %3i^3|^%i%3i %3i %3i %3i^3|^%i%5i %5i^3|^%i%3i^3|^7 %s\n", -               Teamcolor, -               NumResults, -               Teamcolor, -               ( tmpent->client->pers.statscounters.kills ) ? tmpent->client->pers.statscounters.kills : 0, -               ( tmpent->client->pers.statscounters.assists ) ? tmpent->client->pers.statscounters.assists : 0, -               ( tmpent->client->pers.statscounters.structskilled ) ? tmpent->client->pers.statscounters.structskilled : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.deaths ) ? tmpent->client->pers.statscounters.deaths : 0, -               ( tmpent->client->pers.statscounters.feeds ) ? tmpent->client->pers.statscounters.feeds : 0, -               ( tmpent->client->pers.statscounters.suicides ) ? tmpent->client->pers.statscounters.suicides : 0, -               ( tmpent->client->pers.statscounters.teamkills ) ? tmpent->client->pers.statscounters.teamkills : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.dmgdone ) ? tmpent->client->pers.statscounters.dmgdone : 0, -               ( tmpent->client->pers.statscounters.ffdmgdone ) ? tmpent->client->pers.statscounters.ffdmgdone : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.structsbuilt ) ? tmpent->client->pers.statscounters.structsbuilt : 0, -               ( tmpent->client->pers.netname ) ? tmpent->client->pers.netname : "Unknown" ) ); -            } -       } -       else if( g_AllStats.integer == 2 ) -       { -            //check if client is connected and has some stats or atleast is on a team -            if( tmpent->client && tmpent->client->pers.connected == CON_CONNECTED && ( tmpent->client->pers.teamSelection != PTE_NONE ) ) -            { -               NumResults++; -               if( tmpent->client->pers.teamSelection == PTE_ALIENS ) Teamcolor = 1; -               if( tmpent->client->pers.teamSelection == PTE_HUMANS ) Teamcolor = 5; -               ADMP( va( "^%i%2i^3|^%i%3i %3i %3i^3|^%i%3i %3i %3i %3i^3|^%i%5i %5i^3|^%i%3i^3|^7 %s\n", -               Teamcolor, -               NumResults, -               Teamcolor, -               ( tmpent->client->pers.statscounters.kills ) ? tmpent->client->pers.statscounters.kills : 0, -               ( tmpent->client->pers.statscounters.assists ) ? tmpent->client->pers.statscounters.assists : 0, -               ( tmpent->client->pers.statscounters.structskilled ) ? tmpent->client->pers.statscounters.structskilled : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.deaths ) ? tmpent->client->pers.statscounters.deaths : 0, -               ( tmpent->client->pers.statscounters.feeds ) ? tmpent->client->pers.statscounters.feeds : 0, -               ( tmpent->client->pers.statscounters.suicides ) ? tmpent->client->pers.statscounters.suicides : 0, -               ( tmpent->client->pers.statscounters.teamkills ) ? tmpent->client->pers.statscounters.teamkills : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.dmgdone ) ? tmpent->client->pers.statscounters.dmgdone : 0, -               ( tmpent->client->pers.statscounters.ffdmgdone ) ? tmpent->client->pers.statscounters.ffdmgdone : 0, -               Teamcolor, -               ( tmpent->client->pers.statscounters.structsbuilt ) ? tmpent->client->pers.statscounters.structsbuilt : 0, -               ( tmpent->client->pers.netname ) ? tmpent->client->pers.netname : "Unknown" ) ); -            } -       } -    } -    if( NumResults == 0 ) { -       ADMP( "   ^3EMPTY!\n" ); -    } else { -       ADMP( va( "^7 %i Players found!\n", NumResults ) ); -    } -    //update time last viewed - -    ent->client->pers.statscounters.AllstatstimeLastViewed = level.time; -    return; -}  /*  ================= @@ -4412,12 +4757,6 @@ void Cmd_Follow_f( gentity_t *ent )      trap_SendServerCommand( ent - g_entities, "print \"follow: You cannot follow unless you are dead or on the spectators.\n\"" );      return;    } -  if( ent->client->pers.paused ) -  { -    trap_SendServerCommand( ent-g_entities, -      "print \"You may not build while paused\n\"" ); -    return; -  }    if( trap_Argc( ) != 2 )    { @@ -4569,30 +4908,8 @@ void Cmd_PTRCRestore_f( gentity_t *ent )    connection = ent->client->pers.connection;    if( connection && connection->ptrCode == code )    { -    // Set the correct team -    if( !( ent->client->pers.specExpires > level.time ) ) -    { -        // Check if the alien team is full -        if( connection->clientTeam == PTE_ALIENS && -            !G_admin_permission(ent, ADMF_FORCETEAMCHANGE) && -            g_teamForceBalance.integer && -            level.numAlienClients > level.numHumanClients ) -        { -            G_TriggerMenu( ent - g_entities, MN_A_TEAMFULL ); -        } -        // Check if the human team is full -        else if( connection->clientTeam == PTE_HUMANS && -                 !G_admin_permission(ent, ADMF_FORCETEAMCHANGE) && -                 g_teamForceBalance.integer && -                 level.numHumanClients > level.numAlienClients ) -        { -            G_TriggerMenu( ent - g_entities, MN_H_TEAMFULL ); -        } -        else -        { -            G_ChangeTeam( ent, connection->clientTeam ); -        } -    } +    // set the correct team +    G_ChangeTeam( ent, connection->clientTeam );      // set the correct credit etc.      ent->client->ps.persistant[ PERS_CREDIT ] = 0; @@ -4688,39 +5005,39 @@ static void Cmd_Ignore_f( gentity_t *ent )     char  arg1[ MAX_STRING_TOKENS ];     char  arg2[ MAX_STRING_TOKENS ];     pTeam_t team; - +        if( !ent || !ent->client || ( ent->client->pers.teamSelection == PTE_NONE ) )     {       return;     } - +        if( !g_allowShare.integer )     {       trap_SendServerCommand( ent-g_entities, "print \"Share has been disabled.\n\"" );       return;     } - +        if( g_floodMinTime.integer )     if ( G_Flood_Limited( ent ) )     {      trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );      return;     } - +      team = ent->client->pers.teamSelection; - +      G_SayArgv( 0, cmd, sizeof( cmd ) );     if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) )     {       skipargs = 1;       G_SayArgv( 1, cmd, sizeof( cmd ) );     } - +      // target player name is in arg1     G_SayArgv( 1+skipargs, arg1, sizeof( arg1 ) );     // amount to be shared is in arg2     G_SayArgv( 2+skipargs, arg2, sizeof( arg2 ) ); - +      if( arg1[0] && !strchr( arg1, ';' ) && Q_stricmp( arg1, "target_in_aim" ) )     {       //check arg1 is a number @@ -4732,7 +5049,7 @@ static void Cmd_Ignore_f( gentity_t *ent )           break;         }       } - +        if( clientNum >= 0 )       {         clientNum = atoi( arg1 ); @@ -4759,14 +5076,15 @@ static void Cmd_Ignore_f( gentity_t *ent )       vec3_t      forward, end;       trace_t     tr;       gentity_t   *traceEnt; - +  +            // trace a teammate       AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );       VectorMA( ent->client->ps.origin, 8192 * 16, forward, end ); - +        trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID );       traceEnt = &g_entities[ tr.entityNum ]; - +        if( tr.fraction < 1.0f && traceEnt->client &&         ( traceEnt->client->pers.teamSelection == team ) )       { @@ -4780,7 +5098,7 @@ static void Cmd_Ignore_f( gentity_t *ent )         return;       }     } - +      // verify target player team     if( ( clientNum < 0 ) || ( clientNum >= level.maxclients ) ||         ( level.clients[ clientNum ].pers.teamSelection != team ) ) @@ -4789,7 +5107,7 @@ static void Cmd_Ignore_f( gentity_t *ent )         "print \"share: not a valid player of your team.\n\"" );       return;     } - +      if( !arg2[0] || strchr( arg2, ';' ) )     {       // default credit count @@ -4814,11 +5132,11 @@ static void Cmd_Ignore_f( gentity_t *ent )           return;         }       } - +        // credit count from parameter       creds = atoi( arg2 );     } - +      // player specified "0" to transfer     if( creds <= 0 )     { @@ -4826,13 +5144,13 @@ static void Cmd_Ignore_f( gentity_t *ent )         "print \"Ooh, you are a generous one, indeed!\n\"" );       return;     } - +      // transfer only credits the player really has     if( creds > ent->client->pers.credit )     {       creds = ent->client->pers.credit;     } - +      // player has no credits     if( creds <= 0 )     { @@ -4840,7 +5158,7 @@ static void Cmd_Ignore_f( gentity_t *ent )         "print \"Earn some first, lazy gal!\n\"" );       return;     } - +      // allow transfers only up to the credit/evo limit     if( ( team == PTE_HUMANS ) &&          ( creds > HUMAN_MAX_CREDITS - level.clients[ clientNum ].pers.credit ) ) @@ -4852,7 +5170,7 @@ static void Cmd_Ignore_f( gentity_t *ent )     {       creds = ALIEN_MAX_KILLS - level.clients[ clientNum ].pers.credit;     } - +      // target cannot take any more credits     if( creds <= 0 )     { @@ -4861,7 +5179,7 @@ static void Cmd_Ignore_f( gentity_t *ent )           ( team == PTE_HUMANS ) ? "credits" : "evolvepoints" ) );       return;     } - +      // transfer credits     G_AddCreditToClient( ent->client, -creds, qfalse );     trap_SendServerCommand( ent-g_entities, @@ -4873,7 +5191,7 @@ static void Cmd_Ignore_f( gentity_t *ent )       va( "print \"You have received %d %s from %s^7.\n\"", creds,         ( team == PTE_HUMANS ) ? "credits" : "evolvepoints",         ent->client->pers.netname ) ); - +      G_LogPrintf( "Share: %i %i %i %d: %s^7 transferred %d%s to %s^7\n",       ent->client->ps.clientNum,       clientNum, @@ -4884,35 +5202,36 @@ static void Cmd_Ignore_f( gentity_t *ent )       ( team == PTE_HUMANS ) ? "c" : "e",       level.clients[ clientNum ].pers.netname );   } - +    /*   =================   Cmd_Donate_f - +    Alms for the poor   =================   */   void Cmd_Donate_f( gentity_t *ent ) {     char s[ MAX_TOKEN_CHARS ] = "", *type = "evo(s)";     int i, value, divisor, portion, new_credits, total=0, -     max = ALIEN_MAX_KILLS, *amounts, *totals; +     max = ALIEN_MAX_KILLS, *amounts;     qboolean donated = qtrue; - +      if( !ent->client ) return; - +      if( !g_allowShare.integer )     {       trap_SendServerCommand( ent-g_entities, "print \"Donate has been disabled.\n\"" );       return;     } - +       if( g_floodMinTime.integer )     if ( G_Flood_Limited( ent ) )     {      trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );      return;     } - +  +      if( ent->client->pers.teamSelection == PTE_ALIENS )       divisor = level.numAlienClients-1;     else if( ent->client->pers.teamSelection == PTE_HUMANS ) { @@ -4924,13 +5243,13 @@ static void Cmd_Ignore_f( gentity_t *ent )         va( "print \"donate: spectators cannot be so gracious\n\"" ) );       return;     } - +      if( divisor < 1 ) {       trap_SendServerCommand( ent-g_entities,         "print \"donate: get yourself some teammates first\n\"" );       return;     } - +      trap_Argv( 1, s, sizeof( s ) );     value = atoi(s);     if( value <= 0 ) { @@ -4940,15 +5259,11 @@ static void Cmd_Ignore_f( gentity_t *ent )     }     if( value > ent->client->pers.credit)       value = ent->client->pers.credit; - +      // allocate memory for distribution amounts     amounts = G_Alloc( level.maxclients * sizeof( int ) ); -   totals = G_Alloc( level.maxclients * sizeof( int ) ); -   for( i = 0; i < level.maxclients; i++ ) { -     amounts[ i ] = 0; -     totals[ i ] = 0; -   } - +   for( i = 0; i < level.maxclients; i++ ) amounts[ i ] = 0; +      // determine donation amounts for each client     total = value;     while( donated && value ) { @@ -4962,10 +5277,8 @@ static void Cmd_Ignore_f( gentity_t *ent )              ent->client->pers.teamSelection ) {           new_credits = level.clients[ i ].pers.credit + portion;           amounts[ i ] = portion; -         totals[ i ] += portion;           if( new_credits > max ) {             amounts[ i ] -= new_credits - max; -           totals[ i ] -= new_credits - max;             new_credits = max;           }           if( amounts[ i ] ) { @@ -4976,23 +5289,23 @@ static void Cmd_Ignore_f( gentity_t *ent )           }         }     } - +      // transfer funds     G_AddCreditToClient( ent->client, value - total, qtrue );     for( i = 0; i < level.maxclients; i++ ) -     if( totals[ i ] ) { +     if( amounts[ i ] ) {         trap_SendServerCommand( i,           va( "print \"%s^7 donated %d %s to you, don't forget to say 'thank you'!\n\"", -         ent->client->pers.netname, totals[ i ], type ) ); +         ent->client->pers.netname, amounts[ i ], type ) );       } - +      G_Free( amounts ); -   G_Free( totals ); - +      trap_SendServerCommand( ent-g_entities,       va( "print \"Donated %d %s to the cause.\n\"",       total-value, type ) );   } +   commands_t cmds[ ] = {    // normal commands @@ -5016,9 +5329,22 @@ commands_t cmds[ ] = {    { "me", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f },    { "me_team", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f }, +  // channel chat +  { "join", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Join_f }, +  { "part", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Part_f }, +  { "0", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "1", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "2", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "3", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "4", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "5", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "6", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "7", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "8", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +  { "9", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, +    { "score", CMD_INTERMISSION, ScoreboardMessage },    { "mystats", CMD_TEAM|CMD_INTERMISSION, Cmd_MyStats_f }, -  { "allstats", 0|CMD_INTERMISSION, Cmd_AllStats_f },    { "teamstatus", CMD_TEAM, Cmd_TeamStatus_f },    // cheats @@ -5047,6 +5373,7 @@ commands_t cmds[ ] = {    { "build", CMD_TEAM|CMD_LIVING, Cmd_Build_f },    { "deconstruct", CMD_TEAM|CMD_LIVING, Cmd_Destroy_f },    { "mark", CMD_TEAM|CMD_LIVING, Cmd_Mark_f }, +  { "aim", CMD_HUMAN|CMD_LIVING, Cmd_Aim_f },    { "buy", CMD_HUMAN|CMD_LIVING, Cmd_Buy_f },    { "sell", CMD_HUMAN|CMD_LIVING, Cmd_Sell_f }, @@ -5403,15 +5730,15 @@ void G_PrivateMessage( gentity_t *ent )        if( teamonly && !OnSameTeam( ent, tmpent ) )          continue; -       +        // Ignore sending to invisible players        if( tmpent->client->sess.invisible == qtrue && !G_admin_permission( ent, "invisible" ) )          continue; -       +        // Ignore sending to non-invisible-capable players while invisible        if( ent->client->sess.invisible == qtrue && !G_admin_permission( tmpent, "invisible" ) )          continue; -       +        if( BG_ClientListTest( &tmpent->client->sess.ignoreList,          ent-g_entities ) )        { @@ -5662,48 +5989,3 @@ qboolean G_IsMuted( gclient_t *client )    return muteState;  } - -/* -================== -G_TeamKill_Repent - -Determine whether a players team kill activity is high -================== -*/ - -qboolean G_TeamKill_Repent( gentity_t *ent ) -{ -  int millisSinceLastTeamKill; - -  // Doesn't work if g_teamKillThreshold isn't set -  if( !g_teamKillThreshold.integer || -       g_teamKillThreshold.integer == 0 ) -    return qfalse; - -  // Doesn't work when game is paused -  if( level.paused ) -    return qfalse; - -  millisSinceLastTeamKill = level.time - ent->client->pers.lastTeamKillTime; -  if( millisSinceLastTeamKill < 30000 ) -    ent->client->pers.teamKillDemerits++; -  else -  { -    ent->client->pers.teamKillDemerits--; -    if( ent->client->pers.teamKillDemerits < 0 ) -      ent->client->pers.teamKillDemerits = 0; -  } - -  ent->client->pers.lastTeamKillTime = level.time; - -  if ( ent->client->pers.teamKillDemerits >= ( g_teamKillThreshold.integer + 2 ) ) -    trap_SendConsoleCommand( 0, va( "!ban %s 30m team killing\n", ent->client->pers.ip ) ); -  else if ( ent->client->pers.teamKillDemerits == ( g_teamKillThreshold.integer + 1 ) ) -    trap_SendConsoleCommand( 0, va( "!warn %i team killing\n", ent->client->ps.clientNum ) ); -  else if ( ent->client->pers.teamKillDemerits == g_teamKillThreshold.integer ) -    G_AdminsPrintf( "Team killer %s^7 has team killed ^6%i^7 times.\n", -                    ent->client->pers.netname, -                    ent->client->pers.statscounters.teamkills ); - -  return qfalse; -} diff --git a/src/game/g_combat.c b/src/game/g_combat.c index 0cc079a..0d8d013 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -138,6 +138,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    float     percentDamage = 0.0f;    gentity_t *player;    qboolean  tk = qfalse; +  int       spreeRate = 0;    if( self->client->ps.pm_type == PM_DEAD ) @@ -163,6 +164,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        if( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ]  == self->client->ps.stats[ STAT_PTEAM ] )         {          attacker->client->pers.statscounters.teamkills++; +        attacker->client->pers.karma -= 300;          if( attacker->client->pers.teamSelection == PTE_ALIENS )           {            level.alienStatsCounters.teamkills++; @@ -205,6 +207,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int    for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )      BG_DeactivateUpgrade( i, self->client->ps.stats ); +  // killing spree over +  if( self->client->pers.statscounters.spreekills == -1 ) +  { +    spreeRate = 2; +    trap_SendServerCommand( -1, +     va( "print \"%s^7's killing spree has come to an end\n\"", +     self->client->pers.netname ) ); +  } +  self->client->pers.statscounters.spreekills = 0; +    if( meansOfDeath == MOD_SLAP )    {      trap_SendServerCommand( -1, @@ -233,13 +245,14 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) );      G_LogOnlyPrintf("%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n",        self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ); -    G_TeamKill_Repent( attacker ); +    G_admin_tklog_log( attacker, self, meansOfDeath );    }    self->enemy = attacker;    self->client->ps.persistant[ PERS_KILLED ]++;    self->client->pers.statscounters.deaths++; +  self->client->pers.karma -= 10;    if( self->client->pers.teamSelection == PTE_ALIENS )     {      level.alienStatsCounters.deaths++; @@ -280,6 +293,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        attacker->client->lastKillTime = level.time;        attacker->client->pers.statscounters.kills++; +      attacker->client->pers.karma += 50;        if( attacker->client->pers.teamSelection == PTE_ALIENS )         {          level.alienStatsCounters.kills++; @@ -288,11 +302,26 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        {           level.humanStatsCounters.kills++;        } + +      if( g_killingSpree.integer > 2 ) +      { +        if( attacker->client->pers.statscounters.spreekills >= 0 ) +          attacker->client->pers.statscounters.spreekills += 60; +        if( attacker->client->pers.statscounters.spreekills > ( g_killingSpree.integer - 1 ) * 60 ) +        { +          attacker->client->pers.statscounters.spreekills = -1; +          attacker->client->pers.karma += 50; +          trap_SendServerCommand( -1, +            va( "print \"%s^3 is on a killing spree! killer gets a double reward bonus\n\"", +            attacker->client->pers.netname ) ); +        } +      }       }      if( attacker == self )      {        attacker->client->pers.statscounters.suicides++; +      attacker->client->pers.karma -= 100;        if( attacker->client->pers.teamSelection == PTE_ALIENS )         {          level.alienStatsCounters.suicides++; @@ -441,7 +470,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        if( g_alienStage.integer < 2 )        {          self->client->pers.statscounters.feeds++; +        self->client->pers.karma -= 25;          level.humanStatsCounters.feeds++; + +        if( g_feedingSpree.integer && +            level.reactorPresent && +            !G_BuildableRange( self->client->ps.origin, 600, BA_H_REACTOR ) && +            !G_BuildableRange( self->client->ps.origin, 200, BA_H_SPAWN ) ) +        { +          self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE; +        }        }      }      else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) @@ -450,7 +488,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int        if( g_humanStage.integer < 2 )        {          self->client->pers.statscounters.feeds++; +        self->client->pers.karma -= 25;          level.alienStatsCounters.feeds++; + +        if( g_feedingSpree.integer && +            level.overmindPresent && +            !G_BuildableRange( self->client->ps.origin, 600, BA_A_OVERMIND ) && +            !G_BuildableRange( self->client->ps.origin, 200, BA_A_SPAWN ) ) +        { +          self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE; +        }        }      }    } @@ -482,6 +529,9 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int            level.humanStatsCounters.assists++;          } +        if( spreeRate && player == attacker ) +          percentDamage *= (float)spreeRate; +          //add credit          G_AddCreditToClient( player->client,              (int)( classValue * percentDamage ), qtrue ); @@ -516,6 +566,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int           if( percentDamage > 0 && percentDamage < 1)           {              player->client->pers.statscounters.assists++; +            player->client->pers.karma += 25;              level.alienStatsCounters.assists++;           } @@ -524,7 +575,10 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int          if( frags > 0 )          {            //add kills -          G_AddCreditToClient( player->client, frags, qtrue ); +          if( spreeRate && player == attacker ) +            G_AddCreditToClient( player->client, frags * spreeRate, qtrue ); +          else +            G_AddCreditToClient( player->client, frags, qtrue );            //can't revist this account later            self->credits[ i ] = 0; @@ -565,7 +619,10 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int              player = g_entities + topClient;              //add kills -            G_AddCreditToClient( player->client, 1, qtrue ); +            if( spreeRate && player == attacker ) +              G_AddCreditToClient( player->client, spreeRate, qtrue ); +            else +              G_AddCreditToClient( player->client, 1, qtrue );              //can't revist this account again              self->credits[ topClient ] = 0; @@ -618,6 +675,38 @@ finish_dying: // from MOD_SLAP    // g_forcerespawn may force spawning at some later time    self->client->respawnTime = level.time + 1700; +  if( g_feedingSpree.integer ) +  { +    int maxfeed; + +    maxfeed = g_feedingSpree.integer * SPREE_FEED_VALUE; +    if( self->client->pers.statscounters.spreefeeds > maxfeed ) +    { +      self->client->respawnTime += SPREE_FEED_DELAY * (self->client->pers.statscounters.spreefeeds - maxfeed ); +      self->client->pers.karma -= 20; +      trap_SendServerCommand( self->client->ps.clientNum, +        va( "print \"You are on a feeding spree! respawn delayed %d seconds\n\"", +        (self->client->respawnTime - level.time) / 1000 ) ); +    } +  } + +  if( self->client->pers.bleeder ) +  { +    int spreeLength; + +    spreeLength = self->client->pers.statscounters.spreebleeds / 10; +    self->client->respawnTime += 9 * 1000; +     +    trap_SendServerCommand( self->client->ps.clientNum, +      va( "print \"^3Bleeding has made you an enemy of your own base for ^7%d:%02d\n^1Respawn delayed ^7%d^1 seconds\n", +        spreeLength / 60, spreeLength % 60, +        ( self->client->respawnTime - level.time) / 1000 ) ); +    trap_SendServerCommand( self->client->ps.clientNum, +      va( "cp \"^3Bleeding made you an enemy of your own base!\n\n^7for %d:%02d\n\n^1Respawn delayed %d seconds\"", +        spreeLength / 60, spreeLength % 60, +        ( self->client->respawnTime - level.time) / 1000 ) ); +  } +    // remove powerups    memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); @@ -1013,6 +1102,7 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att      if( attacker && attacker->client && modifier == 2 )      {        attacker->client->pers.statscounters.headshots++; +      attacker->client->pers.karma += 5;        level.alienStatsCounters.headshots++;      } @@ -1149,7 +1239,7 @@ dflags    these flags are used to control how T_Damage works  void G_SelectiveDamage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,           vec3_t dir, vec3_t point, int damage, int dflags, int mod, int team )  { -  if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] ) ) +  if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] || targ->client->pers.bleeder ) )      G_Damage( targ, inflictor, attacker, dir, point, damage, dflags, mod );  } @@ -1398,6 +1488,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,          targ->client->lastPoisonTime = level.time;          targ->client->lastPoisonClient = attacker;          attacker->client->pers.statscounters.repairspoisons++; +        attacker->client->pers.karma += 1;          level.alienStatsCounters.repairspoisons++;        }      } @@ -1429,6 +1520,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,        if( targ->biteam == attacker->client->pers.teamSelection || OnSameTeam( targ, attacker ) )         {          attacker->client->pers.statscounters.ffdmgdone += takeNoOverkill; +        if( attacker != targ ) +          attacker->client->pers.karma -= take;          if( attacker->client->pers.teamSelection == PTE_ALIENS )           {            level.alienStatsCounters.ffdmgdone+=takeNoOverkill; @@ -1437,10 +1530,88 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,          {            level.humanStatsCounters.ffdmgdone+=takeNoOverkill;          } + +        // bleeding spree +        if( g_bleedingSpree.integer > 1 && attacker != targ && +            mod != MOD_SUICIDE && mod != MOD_TELEFRAG ) +        { +          attacker->client->pers.statscounters.spreebleeds += take; + +          if( g_bleedingSpreeKick.integer == 2 ) +          { +            if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 ) +            { +              char buf[ MAX_STRING_CHARS ]; + +              G_admin_autorevert( attacker ); + +              Com_sprintf( buf, sizeof( buf ), +                "%s^7 moved from %s to spectators due to excessive team damage\n", +                attacker->client->pers.netname, +                ( attacker->client->pers.teamSelection == PTE_ALIENS ) ? "aliens" : "humans" ); +              trap_SendServerCommand( -1, va( "print \"%s\"", buf ) ); +              G_LogOnlyPrintf( "Teamkilling: %s", buf ); + +              G_admin_tklog_log( attacker, NULL, mod ); + +              trap_SendConsoleCommand( EXEC_APPEND, va( "!putteam %d s %s\n", attacker - g_entities, g_adminTempSpec.string ) ); +              attacker->client->pers.statscounters.spreebleeds = 0; +            } +            else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 66 && +                     attacker->client->pers.bleederLastWarn + 20 * 1000 < level.time ) +            { +              attacker->client->pers.bleederLastWarn = level.time; +              trap_SendServerCommand( attacker - g_entities, +                "print \"^3Please do not damage your teammates or your base\n\"" ); +              trap_SendServerCommand( attacker - g_entities, +                "cp \"^1Please do not damage your teammates or your base\"" ); +            } +          } +          else if( g_bleedingSpreeKick.integer && attacker->client->pers.bleeder && +              attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 + g_bleedingSpree.integer * 20 ) +          { +            trap_SendConsoleCommand( EXEC_APPEND, +              va("!kick %d auto-kick for team bleeding\n", ( attacker - g_entities )) ); +            attacker->client->pers.statscounters.spreebleeds = -9000; +          } +          else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 && +                  !attacker->client->pers.bleeder ) +          { +            attacker->client->pers.bleeder = qtrue; +            attacker->client->pers.karma -= 500; +            level.bleeders++; +            trap_SendServerCommand( -1, +              va( "print \"%s^3 has become an enemy of their own base\n\"", +              attacker->client->pers.netname ) ); +            trap_SendServerCommand( attacker - g_entities, +              "cp \"^1Your team bleeding has irritated your base!\"" ); + +            G_admin_tklog_log( attacker, NULL, mod ); + +            if( g_bleedingSpreeKick.integer ) +              trap_SendServerCommand( attacker - g_entities, +               "print \"^1Continued bleeding will result in kick\n\"" ); +          } +          else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 66 && +                  !attacker->client->pers.bleeder && +                   attacker->client->pers.bleederLastWarn + 20 * 1000 < level.time ) +          { +            attacker->client->pers.bleederLastWarn = level.time; +            trap_SendServerCommand( attacker - g_entities, +              "print \"^3Your bleeding is close to aggravating your base!\n\"" ); +            trap_SendServerCommand( attacker - g_entities, +              "cp \"^1Your bleeding is close to aggravating your base!\"" ); + +            if( g_bleedingSpreeKick.integer ) +              trap_SendServerCommand( attacker - g_entities, +               "print \"^1Continued bleeding will result in kick\n\"" ); +          } +        }        }        else if( targ->s.eType == ET_BUILDABLE )        {          attacker->client->pers.statscounters.structdmgdone += takeNoOverkill; +        attacker->client->pers.karma += ( take / 10 );          if( attacker->client->pers.teamSelection == PTE_ALIENS )           { @@ -1454,6 +1625,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,          if( targ->health > 0 && ( targ->health - take ) <=0 )          {            attacker->client->pers.statscounters.structskilled++; +          attacker->client->pers.karma += 10;            if( attacker->client->pers.teamSelection == PTE_ALIENS )             {              level.alienStatsCounters.structskilled++; @@ -1461,12 +1633,27 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,            else if( attacker->client->pers.teamSelection == PTE_HUMANS )            {              level.humanStatsCounters.structskilled++; + +            if( attacker->client->pers.statscounters.spreefeeds ) +            { +              attacker->client->pers.statscounters.spreefeeds -= take; +              if( attacker->client->pers.statscounters.spreefeeds < 0 ) +                attacker->client->pers.statscounters.spreefeeds = 0; +            } + +            if( attacker->client->pers.statscounters.spreebleeds > 10 ) +            { +              attacker->client->pers.statscounters.spreebleeds -= take / 3; +              if( attacker->client->pers.statscounters.spreebleeds < 10 ) +                attacker->client->pers.statscounters.spreebleeds = 10; +            }            }          }        }        else if( targ->client )        {          attacker->client->pers.statscounters.dmgdone +=takeNoOverkill; +        attacker->client->pers.karma += ( take / 10 );          attacker->client->pers.statscounters.hits++;          if( attacker->client->pers.teamSelection == PTE_ALIENS )           { @@ -1476,9 +1663,52 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,          {            level.humanStatsCounters.dmgdone+=takeNoOverkill;          } + +        if( attacker->client->pers.statscounters.spreefeeds ) +        { +          attacker->client->pers.statscounters.spreefeeds -= take; +          if( attacker->client->pers.statscounters.spreefeeds < 0 ) +            attacker->client->pers.statscounters.spreefeeds = 0; +        } + +        if( attacker->client->pers.statscounters.spreebleeds > 10 ) +        { +          attacker->client->pers.statscounters.spreebleeds -= take / 3; +          if( attacker->client->pers.statscounters.spreebleeds < 10 ) +            attacker->client->pers.statscounters.spreebleeds = 10; +        }        }      } +    if( g_vampireDeath.integer && targ->client && attacker->client && targ != attacker ) +    { +      if( OnSameTeam( targ, attacker ) ) +      { +        if( mod == MOD_GRENADE ) +          return; + +        if( take > attacker->health ) +          take = attacker->health; + +        attacker->health -= take; +        attacker->client->ps.stats[ STAT_HEALTH ] = attacker->health; +        attacker->lastDamageTime = level.time; + +        if( attacker->health <= 0 ) +        { +          if( attacker->health < -999 ) +            attacker->health = -999; +          attacker->die( attacker, attacker, attacker, take, MOD_SUICIDE ); +        } + +        take = 0 - take; +      } +      else +      { +        attacker->health += takeNoOverkill; +        attacker->client->ps.stats[ STAT_HEALTH ] = attacker->health; +      } +    }      //Do the damage      targ->health = targ->health - take; diff --git a/src/game/g_local.h b/src/game/g_local.h index 8c65998..2294f6f 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -53,19 +53,6 @@ typedef struct gclient_s gclient_t;  #define FL_NO_HUMANS      0x00004000  // spawn point just for bots  #define FL_FORCE_GESTURE  0x00008000  // spawn point just for bots -typedef struct -{ -  qboolean	isNB; -  float		Area; -  float		Height; -} noBuild_t; - -typedef struct -{ -  gentity_t	*Marker; -  vec3_t	Origin; -} nbMarkers_t; -  // movers are things like doors, plats, buttons, etc  typedef enum  { @@ -256,9 +243,6 @@ struct gentity_s    int               lastDamageTime;    int               bdnumb;     // buildlog entry ID -   -  // For nobuild! -  noBuild_t	    noBuild;  };  typedef enum @@ -319,8 +303,8 @@ typedef struct    spectatorState_t  spectatorState;    int               spectatorClient;  // for chasecam and follow mode    int               wins, losses;     // tournament stats -  qboolean          invisible;       // for being invisible on the server - ghosts!    qboolean          teamLeader;       // true when this client is a team leader +  qboolean          invisible;       // for being invisible on the server - ghosts!    clientList_t      ignoreList;  } clientSession_t; @@ -356,11 +340,13 @@ typedef struct    short headshots;    int hits;    int hitslocational; +  short spreekills; +  short spreefeeds; +  int spreebleeds;    short teamkills;    int dretchbasytime;    int jetpackusewallwalkusetime;    int timeLastViewed; -  int AllstatstimeLastViewed;  } statsCounters_t;  typedef struct @@ -426,11 +412,11 @@ typedef struct    int                 lastFloodTime;         // level.time of last flood-limited command    int                 floodDemerits;         // number of flood demerits accumulated -  char                lastMessage[ MAX_SAY_TEXT ];  // last message said by this player -  int                 lastMessageTime;              // level.time of last message said by this player +  int                 vampireSuckFraction; -  int                 lastTeamKillTime;      // level.time of last team kill -  int                 teamKillDemerits;      // number of team kill demerits accumulated +  int                 spreeTime1000; +  qboolean            bleeder; +  int                 bleederLastWarn;    vec3_t              lastDeathLocation;    char                guid[ 33 ]; @@ -446,6 +432,9 @@ typedef struct    int                 adminLevel;    char                adminName[ MAX_NETNAME ];    qboolean            designatedBuilder; +  char                chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ]; +  int                 karma; +  int                 bubbleTime;    qboolean            firstConnect;        // This is the first map since connect    qboolean            useUnlagged;    statsCounters_t     statscounters; @@ -615,6 +604,7 @@ typedef struct armourRegion_s  typedef enum  {    TW_NOT = 0, +  TW_SOON,    TW_IMMINENT,    TW_PASSED  } timeWarning_t; @@ -633,6 +623,7 @@ typedef struct buildHistory_s buildHistory_t;  struct buildHistory_s  {    int ID; // persistent ID to aid in specific reverting +  int time; // time the event occured    gentity_t *ent; // who, NULL if they've disconnected (or aren't an ent)    char name[ MAX_NETNAME ]; // who, saves name if ent is NULL    int buildable; // what @@ -702,6 +693,7 @@ typedef struct    char              teamVoteString[ 2 ][ MAX_STRING_CHARS ];    char              teamVoteDisplayString[ 2 ][ MAX_STRING_CHARS ];    int               teamVoteTime[ 2 ];            // level.time vote was called +  int               teamVotePassThreshold[ 2 ];    int               teamVoteYes[ 2 ];    int               teamVoteNo[ 2 ];    int               numteamVotingClients[ 2 ];    // set by CalculateRanks @@ -747,6 +739,14 @@ typedef struct    int               humanBuildPoints;    int               humanBuildPointsPowered; +  int               alienRecoverBuildPoints; +  int               alienRecoverTime; +  int               humanRecoverBuildPoints; +  int               humanRecoverTime; + +  int               defaultAlienBuildPoints; +  int               defaultHumanBuildPoints; +    gentity_t         *markedBuildables[ MAX_GENTITIES ];    int               numBuildablesForRemoval; @@ -769,6 +769,10 @@ typedef struct    timeWarning_t     timelimitWarning;    int               extend_vote_count; +  int               vampireDeath; +  int               vampireDeathBeginTime; +  timeWarning_t     vampireDeathWarning; +    spawnQueue_t      alienSpawnQueue;    spawnQueue_t      humanSpawnQueue; @@ -789,6 +793,8 @@ typedef struct    int               pause_ff;    int               pause_ffb; +  int               bleeders; +    int               lastCreditedAlien;    int               lastCreditedHuman; @@ -807,12 +813,6 @@ typedef struct    statsCounters_level alienStatsCounters;    statsCounters_level humanStatsCounters; -   -  qboolean	    noBuilding; -  float		    nbArea; -  float		    nbHeight; -   -  nbMarkers_t	    nbMarkers[ MAX_GENTITIES ];  } level_locals_t;  #define CMD_CHEAT         0x01 @@ -863,6 +863,9 @@ void      G_LeaveTeam( gentity_t *self );  void      G_ChangeTeam( gentity_t *ent, pTeam_t newTeam );  void      G_SanitiseString( char *in, char *out, int len );  void      G_PrivateMessage( gentity_t *ent ); +void      Cmd_Join_f( gentity_t *ent ); +void      Cmd_Part_f( gentity_t *ent ); +void      Cmd_Channel_f( gentity_t *ent );  char      *G_statsString( statsCounters_t *sc, pTeam_t *pt );  void      Cmd_Share_f( gentity_t *ent );  void      Cmd_Donate_f( gentity_t *ent ); @@ -871,7 +874,6 @@ void      Cmd_Builder_f( gentity_t *ent );  void      G_WordWrap( char *buffer, int maxwidth );  void      G_CP( gentity_t *ent );  qboolean  G_IsMuted( gclient_t *ent ); -qboolean  G_TeamKill_Repent( gentity_t *ent );  //  // g_physics.c @@ -919,8 +921,11 @@ qboolean          G_IsPowered( vec3_t origin );  qboolean          G_IsDCCBuilt( void );  qboolean          G_IsOvermindBuilt( void ); +void              G_RecoverBuildPoints( gentity_t *self ); +  void              G_BuildableThink( gentity_t *ent, int msec );  qboolean          G_BuildableRange( vec3_t origin, float r, buildable_t buildable ); +float             G_BuildableRangeClosest( vec3_t origin, float r, buildable_t buildable );  itemBuildError_t  G_CanBuild( gentity_t *ent, buildable_t buildable, int distance, vec3_t origin );  qboolean G_BuildingExists( int bclass ) ;  qboolean          G_BuildIfValid( gentity_t *ent, buildable_t buildable ); @@ -941,8 +946,16 @@ qboolean          G_RevertCanFit( buildHistory_t *bh );  int               G_LogBuild( buildHistory_t *new );  int               G_CountBuildLog( void );  char             *G_FindBuildLogName( int id ); -void		  G_NobuildSave( void ); -void 		  G_NobuildLoad( void ); + +void              nobuild_init( void ); +void              nobuild_add( gentity_t *ent ); +void              nobuild_del( gentity_t *ent ); +void              nobuild_command( gentity_t *ent, qboolean next_zone, qboolean next_type, float size ); +void              nobuild_set( qboolean enable, gentity_t *ent ); +void              nobuild_save( void ); +void              nobuild_go( gentity_t *ent ); +void              nobuild_list( gentity_t *ent ); +int               nobuild_check( vec3_t origin );  //  // g_utils.c @@ -1131,7 +1144,6 @@ void QDECL G_LogPrintf( const char *fmt, ... );  void QDECL G_LogPrintfColoured( const char *fmt, ... );  void QDECL G_LogOnlyPrintf( const char *fmt, ... );  void QDECL G_AdminsPrintf( const char *fmt, ... ); -void QDECL G_WarningsPrintf( char *flag, const char *fmt, ... );  void QDECL G_LogOnlyPrintf( const char *fmt, ... );  void SendScoreboardMessageToAllClients( void );  void QDECL G_Printf( const char *fmt, ... ); @@ -1140,6 +1152,7 @@ void CheckVote( void );  void CheckTeamVote( int teamnum );  void LogExit( const char *string );  int  G_TimeTilSuddenDeath( void ); +int  G_TimeTilVampireDeath( void );  void CheckMsgTimer( void );  qboolean G_Flood_Limited( gentity_t *ent ); @@ -1205,8 +1218,7 @@ typedef enum    MCV_RANDOM,    MCV_NUMCLIENTS,    MCV_LASTWIN, -  MCV_VOTE, -  MCV_SELECTEDRANDOM +  MCV_VOTE  } mapConditionVariable_t;  typedef enum @@ -1268,6 +1280,7 @@ qboolean  G_AdvanceMapRotation( void );  qboolean  G_StartMapRotation( char *name, qboolean changeMap );  void      G_StopMapRotation( void );  qboolean  G_MapRotationActive( void ); +qboolean  G_CurrentMapIsRotation( void );  void      G_InitMapRotations( void );  qboolean  G_MapExists( char *name );  int       G_GetCurrentMap( int rotation ); @@ -1277,7 +1290,6 @@ qboolean G_IntermissionMapVoteWinner( void );  void G_IntermissionMapVoteMessage( gentity_t *ent );  void G_IntermissionMapVoteMessageAll( void );  void G_IntermissionMapVoteCommand( gentity_t *ent, qboolean next, qboolean choose ); -static qboolean G_GetRandomMap( char *name, int size, int rotation, int map );  //  // g_ptr.c @@ -1296,6 +1308,11 @@ extern  gentity_t       g_entities[ MAX_GENTITIES ];  #define FOFS(x) ((int)&(((gentity_t *)0)->x)) +//spree values +#define SPREE_FEED_VALUE 120 +#define SPREE_FEED_FADE  3 +#define SPREE_FEED_DELAY 50 +  extern  vmCvar_t  g_dedicated;  extern  vmCvar_t  g_cheats;  extern  vmCvar_t  g_maxclients;     // allow this many total, including spectators @@ -1307,11 +1324,16 @@ extern  vmCvar_t  g_minNameChangePeriod;  extern  vmCvar_t  g_maxNameChanges;  extern  vmCvar_t  g_newbieNumbering;  extern  vmCvar_t  g_newbieNamePrefix; +extern  vmCvar_t  g_newbieNoBuild; +extern  vmCvar_t  g_newbieNoBuildMessage;  extern  vmCvar_t  g_timelimit;  extern  vmCvar_t  g_suddenDeathTime;  extern  vmCvar_t  g_suddenDeath;  extern  vmCvar_t  g_suddenDeathMode; +extern  vmCvar_t  g_vampireDeathTime; +extern  vmCvar_t  g_vampireDeath; +extern  vmCvar_t  g_vampireDeathInfo;  extern  vmCvar_t  g_friendlyFire;  extern  vmCvar_t  g_friendlyFireHumans;  extern  vmCvar_t  g_friendlyFireAliens; @@ -1325,6 +1347,7 @@ extern  vmCvar_t  g_speed;  extern  vmCvar_t  g_knockback;  extern  vmCvar_t  g_quadfactor;  extern  vmCvar_t  g_inactivity; +extern  vmCvar_t  g_inactivityMode;  extern  vmCvar_t  g_debugMove;  extern  vmCvar_t  g_debugAlloc;  extern  vmCvar_t  g_debugDamage; @@ -1339,6 +1362,7 @@ extern  vmCvar_t  g_blood;  extern  vmCvar_t  g_allowVote;  extern  vmCvar_t  g_requireVoteReasons;  extern  vmCvar_t  g_voteLimit; +extern  vmCvar_t  g_pollVotes;  extern  vmCvar_t  g_suddenDeathVotePercent;  extern  vmCvar_t  g_suddenDeathVoteDelay;  extern  vmCvar_t  g_extendVotesPercent; @@ -1357,13 +1381,12 @@ extern  vmCvar_t  g_customVote8;  extern  vmCvar_t  g_customVotePercent;  extern  vmCvar_t  g_mapVotesPercent;  extern  vmCvar_t  g_mapRotationVote; -extern  vmCvar_t  g_extendVotesPercent; -extern  vmCvar_t  g_extendVotesTime; -extern  vmCvar_t  g_extendVotesCount;  extern  vmCvar_t  g_readyPercent;  extern  vmCvar_t  g_designateVotes;  extern  vmCvar_t  g_teamAutoJoin;  extern  vmCvar_t  g_teamForceBalance; +extern  vmCvar_t  g_popularMaps; +extern  vmCvar_t  g_popularMapsVotePercent;  extern  vmCvar_t  g_banIPs;  extern  vmCvar_t  g_filterBan;  extern  vmCvar_t  g_smoothClients; @@ -1396,6 +1419,9 @@ extern  vmCvar_t  g_unlagged;  extern  vmCvar_t  g_disabledEquipment;  extern  vmCvar_t  g_disabledClasses;  extern  vmCvar_t  g_disabledBuildables; +extern  vmCvar_t  g_buildPointsRecoverRate; +extern  vmCvar_t  g_dynamicBuildPoints; +extern  vmCvar_t  g_instantBuild;  extern  vmCvar_t  g_markDeconstruct;  extern  vmCvar_t  g_markDeconstructMode; @@ -1405,12 +1431,13 @@ extern  vmCvar_t  g_debugMapRotation;  extern  vmCvar_t  g_currentMapRotation;  extern  vmCvar_t  g_currentMap;  extern  vmCvar_t  g_nextMap; +extern  vmCvar_t  g_idleMapSwitch;  extern  vmCvar_t  g_initialMapRotation;  extern  vmCvar_t  g_chatTeamPrefix; +extern  vmCvar_t  g_sayAreaRange;  extern  vmCvar_t  g_actionPrefix;  extern  vmCvar_t  g_floodMaxDemerits;  extern  vmCvar_t  g_floodMinTime; -extern  vmCvar_t  g_spamTime;  extern  vmCvar_t  g_shove; @@ -1426,27 +1453,39 @@ extern  vmCvar_t  g_adminSayFilter;  extern  vmCvar_t  g_adminNameProtect;  extern  vmCvar_t  g_adminTempMute;  extern  vmCvar_t  g_adminTempBan; +extern  vmCvar_t  g_adminBanRepeatKicks;  extern  vmCvar_t  g_adminMaxBan;  extern  vmCvar_t  g_adminTempSpec;  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_minDeconLevel; -extern  vmCvar_t  g_minDeconAffectsMark;  extern  vmCvar_t  g_forceAutoSelect;  extern  vmCvar_t  g_minLevelToSpecMM1;  extern  vmCvar_t  g_banNotice; +extern  vmCvar_t  g_karma; +extern  vmCvar_t  g_chat; +extern  vmCvar_t  g_adminExpireTime;  extern  vmCvar_t  g_devmapKillerHP;  extern  vmCvar_t  g_killerHP; +extern  vmCvar_t  g_maxGhosts; +  extern  vmCvar_t  g_privateMessages; -extern  vmCvar_t  g_fullIgnore;  extern  vmCvar_t  g_decolourLogfiles;  extern  vmCvar_t  g_publicSayadmins;  extern  vmCvar_t  g_myStats;  extern  vmCvar_t  g_teamStatus;  extern  vmCvar_t  g_antiSpawnBlock; +extern  vmCvar_t  g_killingSpree; +extern  vmCvar_t  g_feedingSpree; +extern  vmCvar_t  g_bleedingSpree; +extern  vmCvar_t  g_bleedingSpreeKick; +extern  vmCvar_t  g_autoRevert; +  extern  vmCvar_t  g_dretchPunt;  extern  vmCvar_t  g_devmapNoGod; @@ -1458,6 +1497,11 @@ extern  vmCvar_t  g_slapDamage;  extern  vmCvar_t  g_voteMinTime;  extern  vmCvar_t  g_mapvoteMaxTime;  extern  vmCvar_t  g_votableMaps; +extern  vmCvar_t  g_defeatVoteMinTime; + +extern  vmCvar_t  g_practiceText; +extern  vmCvar_t  g_practiceCount; +extern  vmCvar_t  g_freeCredits;  extern  vmCvar_t  g_msg;  extern  vmCvar_t  g_msgTime; @@ -1466,23 +1510,27 @@ extern  vmCvar_t  g_welcomeMsgTime;  extern  vmCvar_t  g_buildLogMaxLength; -extern vmCvar_t  g_AllStats; -extern vmCvar_t  g_AllStatsTime; +extern  vmCvar_t  g_turretAim; + +extern  vmCvar_t  g_modBuildableHealth;  // Buildable health +extern  vmCvar_t  g_modBuildableSpeed;   // Buildable fire rate +extern  vmCvar_t  g_modBuildableCount;   // Number of OMs/RCs allowed +extern  vmCvar_t  g_modHumanStamina;     // Human stamina +extern  vmCvar_t  g_modHumanHealth;      // Human health +extern  vmCvar_t  g_modAlienHealth;      // Alien health +extern  vmCvar_t  g_modAlienRegenRange;  // Alien regen rate is based on distance from base +extern  vmCvar_t  g_modHumanRate;        // Human fire rate +extern  vmCvar_t  g_modAlienRate;        // Alien fire rate +extern  vmCvar_t  g_modWeaponAmmo;       // Weapon ammo per clip +extern  vmCvar_t  g_modWeaponReload;     // Weapon reload time +extern  vmCvar_t  g_modTurretAngle;      // Allow high turret build angles +extern  vmCvar_t  g_modStage3Strength;   // Alter stage 3 buildables +extern  vmCvar_t  g_modMainStrength;     // Damage/range modifier for Reactor and Overmind  extern  vmCvar_t  mod_jetpackFuel;  extern  vmCvar_t  mod_jetpackConsume;  extern  vmCvar_t  mod_jetpackRegen; -extern  vmCvar_t  g_adminExpireTime; - -extern  vmCvar_t  g_autoGhost; - -extern  vmCvar_t  g_teamKillThreshold; - -extern  vmCvar_t  g_aimbotAdvertBan; -extern  vmCvar_t  g_aimbotAdvertBanTime; -extern  vmCvar_t  g_aimbotAdvertBanReason; -  void      trap_Printf( const char *fmt );  void      trap_Error( const char *fmt );  int       trap_Milliseconds( void ); diff --git a/src/game/g_main.c b/src/game/g_main.c index b946238..732fecd 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -23,8 +23,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  #include "g_local.h" -#define QVM_NAME       "Slackers QVM" " (Lakitu7 5.5)" -#define QVM_VERSIONNUM      "1.1+" +#define QVM_NAME       "cQVM" +#define MAJ_VERSION    "5.5" + +#ifdef SVN_ID +  #define QVM_VERSIONNUM MAJ_VERSION " svn" SVN_ID +#else +  #define QVM_VERSIONNUM MAJ_VERSION +#endif  level_locals_t  level; @@ -47,6 +53,9 @@ vmCvar_t  g_timelimit;  vmCvar_t  g_suddenDeathTime;  vmCvar_t  g_suddenDeath;  vmCvar_t  g_suddenDeathMode; +vmCvar_t  g_vampireDeathTime; +vmCvar_t  g_vampireDeath; +vmCvar_t  g_vampireDeathInfo;  vmCvar_t  g_capturelimit;  vmCvar_t  g_friendlyFire;  vmCvar_t  g_friendlyFireAliens; @@ -65,6 +74,7 @@ vmCvar_t  g_cheats;  vmCvar_t  g_knockback;  vmCvar_t  g_quadfactor;  vmCvar_t  g_inactivity; +vmCvar_t  g_inactivityMode;  vmCvar_t  g_debugMove;  vmCvar_t  g_debugDamage;  vmCvar_t  g_debugAlloc; @@ -85,6 +95,7 @@ vmCvar_t  g_podiumDrop;  vmCvar_t  g_allowVote;  vmCvar_t  g_requireVoteReasons;  vmCvar_t  g_voteLimit; +vmCvar_t  g_pollVotes;  vmCvar_t  g_suddenDeathVotePercent;  vmCvar_t  g_suddenDeathVoteDelay;  vmCvar_t  g_extendVotesPercent; @@ -101,14 +112,13 @@ 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_popularMaps; +vmCvar_t  g_popularMapsVotePercent;  vmCvar_t  g_banIPs;  vmCvar_t  g_filterBan;  vmCvar_t  g_smoothClients; @@ -122,6 +132,8 @@ vmCvar_t  g_minNameChangePeriod;  vmCvar_t  g_maxNameChanges;  vmCvar_t  g_newbieNumbering;  vmCvar_t  g_newbieNamePrefix; +vmCvar_t  g_newbieNoBuild; +vmCvar_t  g_newbieNoBuildMessage;  vmCvar_t  g_humanBuildPoints;  vmCvar_t  g_alienBuildPoints; @@ -142,6 +154,9 @@ vmCvar_t  g_unlagged;  vmCvar_t  g_disabledEquipment;  vmCvar_t  g_disabledClasses;  vmCvar_t  g_disabledBuildables; +vmCvar_t  g_buildPointsRecoverRate; +vmCvar_t  g_dynamicBuildPoints; +vmCvar_t  g_instantBuild;  vmCvar_t  g_markDeconstruct;  vmCvar_t  g_markDeconstructMode; @@ -151,16 +166,17 @@ vmCvar_t  g_debugMapRotation;  vmCvar_t  g_currentMapRotation;  vmCvar_t  g_currentMap;  vmCvar_t  g_nextMap; +vmCvar_t  g_idleMapSwitch;  vmCvar_t  g_initialMapRotation;  vmCvar_t  g_shove;  vmCvar_t  g_mapConfigs;  vmCvar_t  g_chatTeamPrefix; +vmCvar_t  g_sayAreaRange;  vmCvar_t  g_actionPrefix;  vmCvar_t  g_floodMaxDemerits;  vmCvar_t  g_floodMinTime; -vmCvar_t  g_spamTime;  vmCvar_t  g_layouts;  vmCvar_t  g_layoutAuto; @@ -172,31 +188,42 @@ vmCvar_t  g_adminSayFilter;  vmCvar_t  g_adminNameProtect;  vmCvar_t  g_adminTempMute;  vmCvar_t  g_adminTempBan; +vmCvar_t  g_adminBanRepeatKicks;  vmCvar_t  g_adminMaxBan;  vmCvar_t  g_adminTempSpec;  vmCvar_t  g_adminMapLog; +vmCvar_t  g_adminRegisterLevel; +vmCvar_t  g_adminRegisterAdminPass; +vmCvar_t  g_adminRegisterAdminLevel;  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_killingSpree; +vmCvar_t  g_feedingSpree; +vmCvar_t  g_bleedingSpree; +vmCvar_t  g_bleedingSpreeKick; +vmCvar_t  g_autoRevert; +  vmCvar_t  g_banNotice; +vmCvar_t  g_karma; +vmCvar_t  g_chat; +vmCvar_t  g_adminExpireTime;  vmCvar_t  g_devmapKillerHP;  vmCvar_t  g_killerHP;  vmCvar_t  g_buildLogMaxLength; +vmCvar_t  g_maxGhosts; +  vmCvar_t  g_tag;  vmCvar_t  g_dretchPunt; @@ -213,27 +240,38 @@ vmCvar_t  g_slapDamage;  vmCvar_t  g_voteMinTime;  vmCvar_t  g_mapvoteMaxTime;  vmCvar_t  g_votableMaps; +vmCvar_t  g_defeatVoteMinTime; + +vmCvar_t  g_practiceText; +vmCvar_t  g_practiceCount; +vmCvar_t  g_freeCredits;  vmCvar_t  g_msg;  vmCvar_t  g_msgTime;  vmCvar_t  g_welcomeMsg;  vmCvar_t  g_welcomeMsgTime; +vmCvar_t  g_turretAim; + +vmCvar_t  g_modBuildableHealth; +vmCvar_t  g_modBuildableSpeed; +vmCvar_t  g_modBuildableCount; +vmCvar_t  g_modHumanStamina; +vmCvar_t  g_modHumanHealth; +vmCvar_t  g_modAlienHealth; +vmCvar_t  g_modAlienRegenRange; +vmCvar_t  g_modHumanRate; +vmCvar_t  g_modAlienRate; +vmCvar_t  g_modWeaponAmmo; +vmCvar_t  g_modWeaponReload; +vmCvar_t  g_modTurretAngle; +vmCvar_t  g_modStage3Strength; +vmCvar_t  g_modMainStrength;  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; -  static cvarTable_t   gameCvarTable[ ] =  {    // don't override the cheat state set by the system @@ -260,6 +298,10 @@ static cvarTable_t   gameCvarTable[ ] =    { &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_vampireDeathTime, "g_vampireDeathTime", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, +  { &g_vampireDeath, "g_vampireDeath", "0", CVAR_NORESTART, 0, qtrue }, +  { &g_vampireDeathInfo, "g_vampireDeathInfo", "( !info vampire )", CVAR_ARCHIVE, 0, qtrue }, +    { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse  },    { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qtrue  }, @@ -272,11 +314,14 @@ static cvarTable_t   gameCvarTable[ ] =    { &g_devmapNoStructDmg, "g_devmapNoStructDmg", "0", CVAR_ARCHIVE, 0, qtrue  },    { &g_slapKnockback, "g_slapKnockback", "200", CVAR_ARCHIVE, 0, qfalse}, -  { &g_slapDamage, "g_slapDamage", "0", CVAR_ARCHIVE, 0, qfalse}, +  { &g_slapDamage, "g_slapDamage", "5", CVAR_ARCHIVE, 0, qfalse},    { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE  },    { &g_teamForceBalance, "g_teamForceBalance", "1", CVAR_ARCHIVE  }, +  { &g_popularMaps, "g_popularMaps", "arachnid2 atcs karith nexus6 niveus transit tremor", CVAR_ARCHIVE  }, +  { &g_popularMapsVotePercent, "g_popularMapsVotePercent", "0", CVAR_ARCHIVE  }, +    { &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  }, @@ -289,8 +334,6 @@ static cvarTable_t   gameCvarTable[ ] =    { &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  }, @@ -301,6 +344,7 @@ static cvarTable_t   gameCvarTable[ ] =    { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue  },    { &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue },    { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue }, +  { &g_inactivityMode, "g_inactivityMode", "0", 0, 0, qfalse },    { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },    { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },    { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse }, @@ -312,12 +356,19 @@ static cvarTable_t   gameCvarTable[ ] =    { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },    { &g_requireVoteReasons, "g_requireVoteReasons", "0", CVAR_ARCHIVE, 0, qfalse }, +  { &g_pollVotes, "g_pollVotes", "1", 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_defeatVoteMinTime, "g_defeatVoteMinTime", "60", CVAR_ARCHIVE, 0, qfalse },    { &g_suddenDeathVotePercent, "g_suddenDeathVotePercent", "74", CVAR_ARCHIVE, 0, qfalse },    { &g_suddenDeathVoteDelay, "g_suddenDeathVoteDelay", "180", 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_kickVotesPercent, "g_kickVotesPercent", "50", CVAR_ARCHIVE, 0, qtrue  }, +    { &g_customVote1, "g_customVote1", "", CVAR_ARCHIVE, 0, qfalse  },    { &g_customVote2, "g_customVote2", "", CVAR_ARCHIVE, 0, qfalse  },    { &g_customVote3, "g_customVote3", "", CVAR_ARCHIVE, 0, qfalse  }, @@ -327,13 +378,15 @@ static cvarTable_t   gameCvarTable[ ] =    { &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_practiceText, "g_practiceText", "", 0, 0, qfalse}, +  { &g_practiceCount, "g_practiceCount", "0", 0, 0, qfalse}, +  { &g_freeCredits, "g_freeCredits", "0", CVAR_ARCHIVE, 0, qtrue },    { &g_listEntity, "g_listEntity", "0", 0, 0, qfalse },    { &g_minCommandPeriod, "g_minCommandPeriod", "500", 0, 0, qfalse}, @@ -341,6 +394,9 @@ static cvarTable_t   gameCvarTable[ ] =    { &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_newbieNoBuild, "g_newbieNoBuild", "0", CVAR_ARCHIVE, 0, qfalse}, +  { &g_newbieNoBuildMessage, "g_newbieNoBuildMessage", +    "Set a name by pressing Escape and choosing Options", CVAR_ARCHIVE, 0, qfalse},    { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},    { &g_clientUpgradeNotice, "g_clientUpgradeNotice", "1", 0, 0, qfalse}, @@ -362,17 +418,20 @@ static cvarTable_t   gameCvarTable[ ] =    { &g_teamImbalanceWarnings, "g_teamImbalanceWarnings", "30", CVAR_ARCHIVE, 0, qfalse  }, -  { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  }, +  { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse  },    { &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_buildPointsRecoverRate, "g_buildPointsRecoverRate", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_dynamicBuildPoints, "g_dynamicBuildPoints", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_instantBuild, "g_instantBuild", "0", CVAR_ARCHIVE, 0, qfalse  },    { &g_chatTeamPrefix, "g_chatTeamPrefix", "1", CVAR_ARCHIVE  }, +  { &g_sayAreaRange, "g_sayAreaRange", "1000", CVAR_ARCHIVE, 0, qtrue },    { &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  }, @@ -382,6 +441,7 @@ static cvarTable_t   gameCvarTable[ ] =    { &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse  }, // -1 = NOT_ROTATING    { &g_currentMap, "g_currentMap", "0", 0, 0, qfalse  },    { &g_nextMap, "g_nextMap", "", 0 , 0, qtrue  }, +  { &g_idleMapSwitch, "g_idleMapSwitch", "0", CVAR_ARCHIVE, 0, qfalse  },    { &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse  },    { &g_shove, "g_shove", "15", CVAR_ARCHIVE, 0, qfalse  },    { &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse  }, @@ -397,53 +457,74 @@ static cvarTable_t   gameCvarTable[ ] =    { &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_adminBanRepeatKicks, "g_adminBanRepeatKicks", "0", 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_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_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_maxGhosts, "g_maxGhosts", "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_teamStatus, "g_teamStatus", "0", CVAR_ARCHIVE, 0, qfalse  },    { &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_karma, "g_karma", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_chat, "g_chat", "chat.dat", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_adminExpireTime, "g_adminExpireTime", "0", CVAR_ARCHIVE, 0, qfalse  },    { &g_devmapKillerHP, "g_devmapKillerHP", "0", CVAR_ARCHIVE, 0, qtrue  }, -  { &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qtrue  }, +  { &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qfalse  },    { &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse },    { &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qfalse  }, + +  { &g_killingSpree, "g_killingSpree", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_feedingSpree, "g_feedingSpree", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_bleedingSpree, "g_bleedingSpree", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_bleedingSpreeKick, "g_bleedingSpreeKick", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_autoRevert, "g_autoRevert", "0", 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_turretAim, "g_turretAim", "0", CVAR_ARCHIVE, 0, qfalse  }, + +  { &g_modBuildableHealth, "g_modBuildableHealth", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modBuildableSpeed, "g_modBuildableSpeed", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modBuildableCount, "g_modBuildableCount", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modHumanStamina, "g_modHumanStamina", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modHumanHealth, "g_modHumanHealth", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modAlienHealth, "g_modAlienHealth", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modAlienRegenRange, "g_modAlienRegenRange", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modHumanRate, "g_modHumanRate", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modAlienRate, "g_modAlienRate", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modWeaponAmmo, "g_modWeaponAmmo", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modWeaponReload, "g_modWeaponReload", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modTurretAngle, "g_modTurretAngle", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modStage3Strength, "g_modStage3Strength", "0", CVAR_ARCHIVE, 0, qfalse  }, +  { &g_modMainStrength, "g_modMainStrength", "0", 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_jetpackFuel, "mod_jetpackFuel", "0", CVAR_ARCHIVE, 0, qfalse  },    { &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  }, +  { &mod_jetpackRegen, "mod_jetpackRegen", "3", 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  }  };  static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] ); @@ -676,6 +757,23 @@ void G_UpdateCvars( void )      G_RemapTeamShaders( );  } +static void G_InitModCvars( void ) +{ +  BG_MOD_set( MOD_BG_BUILDABLE_HEALTH, g_modBuildableHealth.integer ); +  BG_MOD_set( MOD_BG_BUILDABLE_SPEED, g_modBuildableSpeed.integer ); +  BG_MOD_set( MOD_BG_HUMAN_STAMINA, g_modHumanStamina.integer ); +  BG_MOD_set( MOD_BG_HUMAN_HEALTH, g_modHumanHealth.integer ); +  BG_MOD_set( MOD_BG_ALIEN_HEALTH, g_modAlienHealth.integer ); +  BG_MOD_set( MOD_BG_HUMAN_RATE, g_modHumanRate.integer ); +  BG_MOD_set( MOD_BG_ALIEN_RATE, g_modAlienRate.integer ); +  BG_MOD_set( MOD_BG_WEAPON_AMMO, g_modWeaponAmmo.integer ); +  BG_MOD_set( MOD_BG_WEAPON_RELOAD, g_modWeaponReload.integer ); +  BG_MOD_set( MOD_BG_TURRET_ANGLE, g_modTurretAngle.integer ); +  BG_MOD_set( MOD_BG_STAGE3_STRENGTH, g_modStage3Strength.integer ); + +  BG_MOD_update( ); +} +  /*  =================  G_MapConfigs @@ -813,13 +911,29 @@ void G_InitGame( int levelTime, int randomSeed, int restart )    // load up a custom building layout if there is one    G_LayoutLoad( ); -   -  // load any nobuild markers that have been saved -  G_NobuildLoad( ); + +  // load up nobuild zones +  nobuild_init( );    // the map might disable some things    BG_InitAllowedGameElements( ); +  // practice counter +  if( g_practiceCount.integer > 0 ) +  { +    trap_Cvar_Set( "g_practiceCount", va( "%d", g_practiceCount.integer - 1 ) ); +  } + +  // free credits expiration +  if( g_freeCredits.integer > 1 ) +  { +    trap_Cvar_Set( "g_freeCredits", +      va( "%d", ( g_freeCredits.integer > 2 ) ? g_freeCredits.integer - 1 : 0 ) ); +  } + +  // syncronize mod cvars +  G_InitModCvars( ); +    // general initialization    G_FindTeams( ); @@ -841,6 +955,7 @@ void G_InitGame( int levelTime, int randomSeed, int restart )    trap_Cvar_Set( "g_humanKills", 0 );    trap_Cvar_Set( "g_suddenDeath", 0 );    level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000; +  trap_Cvar_Set( "g_vampireDeath", 0 );    G_Printf( "-----------------------------------\n" ); @@ -891,6 +1006,8 @@ void G_ShutdownGame( int restart )    G_Printf( "==== ShutdownGame ====\n" ); +  G_admin_chat_writeconfig(); +    if( level.logFile )    {      G_LogPrintf( "ShutdownGame:\n" ); @@ -898,15 +1015,13 @@ void G_ShutdownGame( int restart )      trap_FS_FCloseFile( level.logFile );    } -  // 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_admin_tklog_cleanup( );    level.restarted = qfalse;    level.surrenderTeam = PTE_NONE; @@ -1281,6 +1396,15 @@ int G_TimeTilSuddenDeath( void )    return ( ( level.suddenDeathBeginTime ) - ( level.time - level.startTime ) );  } +int G_TimeTilVampireDeath( void ) +{ +  if( !g_vampireDeathTime.integer ) +    return 3600000; // Always some time away + +  return ( g_vampireDeathTime.integer * 60000 ) - +         ( level.time - level.startTime ); +} +  #define PLAYER_COUNT_MOD 5.0f @@ -1366,6 +1490,43 @@ void G_CalculateBuildPoints( void )      }    } +  //vampire death +  if( !g_vampireDeath.integer && level.vampireDeath ) +  { +    level.vampireDeath = 0; +    level.vampireDeathWarning = 0; +  } +  if( !level.vampireDeath ) +  { +    if( g_vampireDeath.integer || G_TimeTilVampireDeath( ) <= 0 ) //Conditions to enter VD +    { +      if( level.vampireDeathWarning < TW_PASSED ) +      { +        trap_SendServerCommand( -1, "cp \"^1Vampire Sudden Death!\"" ); +        level.vampireDeath = 1; +        level.vampireDeathBeginTime = level.time; +        level.vampireDeathWarning = TW_PASSED; +        g_vampireDeath.integer = 1; +      } +    } +    else if( G_TimeTilVampireDeath( ) <= 60000 && level.vampireDeathWarning < TW_IMMINENT ) +    { +        trap_SendServerCommand( -1, +          va( "cp \"^1Vampire\n^7Sudden Death in 60 seconds!%s%s\"", +            ( g_vampireDeathInfo.string[ 0 ] ) ? "\n\n" : "", +            g_vampireDeathInfo.string ) ); +        level.vampireDeathWarning = TW_IMMINENT; +    } +    else if( G_TimeTilVampireDeath( ) <= 300000 && level.vampireDeathWarning < TW_SOON ) +    { +        trap_SendServerCommand( -1, +          va ("cp \"^1Vampire\n^7Sudden Death in 5 minutes!%s%s\"", +            ( g_vampireDeathInfo.string[ 0 ] ) ? "\n\n" : "", +            g_vampireDeathInfo.string ) ); +        level.vampireDeathWarning = TW_SOON; +    } +  } +    //set BP at each cycle    if( g_suddenDeath.integer )    { @@ -1378,8 +1539,34 @@ void G_CalculateBuildPoints( void )      localATP = g_alienBuildPoints.integer;    } -  level.humanBuildPoints = level.humanBuildPointsPowered = localHTP; -  level.alienBuildPoints = localATP; +  // Build Point Recovery +  // each gained stage adds 50% delay +  if( g_buildPointsRecoverRate.integer ) +  { +    while( level.humanRecoverBuildPoints > 0 && +           level.humanRecoverTime < level.time ) +    { +      level.humanRecoverBuildPoints--; +      level.humanRecoverTime += +        ( 60000 + g_humanStage.integer * 30000 ) / g_buildPointsRecoverRate.integer; +    } +    while( level.alienRecoverBuildPoints > 0 && +           level.alienRecoverTime < level.time ) +    { +      level.alienRecoverBuildPoints--; +      level.alienRecoverTime += +        ( 60000 + g_alienStage.integer * 30000 ) / g_buildPointsRecoverRate.integer; +    } +  } +  else +  { +    // recover may have been turned off, reset +    level.humanRecoverBuildPoints = 0; +    level.alienRecoverBuildPoints = 0; +  } + +  level.humanBuildPoints = level.humanBuildPointsPowered = localHTP - level.humanRecoverBuildPoints; +  level.alienBuildPoints = localATP - level.alienRecoverBuildPoints;    level.reactorPresent = qfalse;    level.overmindPresent = qfalse; @@ -1392,6 +1579,9 @@ void G_CalculateBuildPoints( void )      if( ent->s.eType != ET_BUILDABLE )        continue; +    if( ent->s.eFlags & EF_DEAD )  +      continue; +      buildable = ent->s.modelindex;      if( buildable != BA_NONE ) @@ -1612,6 +1802,7 @@ void CalculateRanks( void )    level.numHumanClients = 0;    level.numLiveAlienClients = 0;    level.numLiveHumanClients = 0; +  level.bleeders = 0;    for( i = 0; i < level.maxclients; i++ )    { @@ -1644,6 +1835,9 @@ void CalculateRanks( void )            if( level.clients[ i ].sess.sessionTeam != TEAM_SPECTATOR )              level.numLiveHumanClients++;          } + +        if( level.clients[ i ].pers.bleeder ) +          level.bleeders++;        }      }    } @@ -1940,7 +2134,7 @@ void QDECL G_AdminsPrintf( const char *fmt, ... )    for( j = 0; j < level.maxclients; j++ )    {      tempent = &g_entities[ j ]; -    if( G_admin_permission( tempent, ADMF_ADMINCHAT ) && +    if( G_admin_permission( tempent, ADMF_ADMINCHAT) &&          !tempent->client->pers.ignoreAdminWarnings )       {         trap_SendServerCommand(tempent-g_entities,va( "print \"^6[Admins]^7 %s\"", string) );  @@ -1950,37 +2144,7 @@ void QDECL G_AdminsPrintf( const char *fmt, ... )    G_LogPrintf("%s",string);  } -/* -================= -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 @@ -2148,7 +2312,7 @@ void G_SendGameStat( pTeam_t team )      int ping;      cl = &level.clients[ level.sortedClients[ i ] ]; -     +      // Ignore invisible players      if ( cl->sess.invisible == qtrue )        continue; @@ -2474,6 +2638,8 @@ void CheckVote( void )  {    int votePassThreshold=level.votePassThreshold;    int voteYesPercent; +  int minVotes = 0; +  qboolean pass = qfalse;    if( level.voteExecuteTime && level.voteExecuteTime < level.time )    { @@ -2487,7 +2653,10 @@ void CheckVote( void )      {        G_admin_maplog_result( "m" );      } - +    else if( !Q_stricmpn( level.voteString, "!restart", 8 ) ) +    { +      G_admin_maplog_result( "l" ); +    }      if( !Q_stricmp( level.voteString, "suddendeath" ) )      { @@ -2512,6 +2681,13 @@ void CheckVote( void )    if( !level.voteTime )      return; +  if( !Q_stricmpn( level.voteString, "ban", 3 ) ) +  { +    minVotes = 3; +    if( level.numConnectedClients <  minVotes ) +      minVotes = level.numConnectedClients; +  } +    if( level.voteYes + level.voteNo > 0 )      voteYesPercent = (int)( 100 * ( level.voteYes ) / ( level.voteYes + level.voteNo ) );    else @@ -2520,20 +2696,22 @@ void CheckVote( void )    if( ( level.time - level.voteTime >= VOTE_TIME ) ||         ( level.voteYes + level.voteNo == level.numConnectedClients ) )    { -    if( voteYesPercent> votePassThreshold || level.voteNo == 0 ) +    if( level.voteYes + level.voteNo < minVotes ) +    { +      // not enough voters +      trap_SendServerCommand( -1, va( "print \"This vote type requires at least %d voters\n\"", minVotes ) ); +      pass = qfalse; +    } +    else if( voteYesPercent > votePassThreshold )      {        // 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; +      pass = qtrue;      }      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 ); +      pass = qfalse;      }    }    else @@ -2542,18 +2720,14 @@ void CheckVote( void )                                   ( (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; +      pass = qtrue;      }      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"); +      pass = qfalse;      }      else      { @@ -2562,6 +2736,22 @@ void CheckVote( void )      }    } +  trap_SendServerCommand( -1, +    va( "print \"Vote %s^7 (^2Y:%d^7-^1N:%d^7, %d percent) (%s)\n\"", +    ( pass ) ? "^2passed" : "^1failed", +    level.voteYes, level.voteNo, voteYesPercent, +    level.voteDisplayString ) ); + +  G_LogPrintf( "Vote: Vote %s (%d - %d)\n", +    ( pass ) ? "passed" : "failed", +    level.voteYes, level.voteNo ); + +  G_admin_adminlog_log( NULL, "vote", +    va( "%s^7 (^2Y:%d^7-^1N:%d^7, %d percent)", +      ( pass ) ? "^2passed" : "^1failed", +      level.voteYes, level.voteNo, voteYesPercent ), +    0, pass ); +    level.voteTime = 0;    trap_SetConfigstring( CS_VOTE_TIME, "" );    trap_SetConfigstring( CS_VOTE_STRING, "" ); @@ -2576,6 +2766,9 @@ CheckTeamVote  void CheckTeamVote( int team )  {    int cs_offset; +  int votePassThreshold; +  int voteYesPercent = 0; +  qboolean pass = qfalse;    if ( team == PTE_HUMANS )      cs_offset = 0; @@ -2584,38 +2777,44 @@ void CheckTeamVote( int team )    else      return; +  votePassThreshold = level.teamVotePassThreshold[ cs_offset ]; +    if( !level.teamVoteTime[ cs_offset ] )      return; +  if( ( level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ] ) ) +  { +    voteYesPercent = (100 * level.teamVoteYes[ cs_offset ]) / +                     (level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ]); +  } +    if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME )    { -    if( level.teamVoteYes[ cs_offset ] > level.teamVoteNo[ cs_offset ] && level.teamVoteYes[ cs_offset ] >= 2 ) +    if( voteYesPercent > votePassThreshold && 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 ] ) ); +      pass = qtrue;      }      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 ] ); +      pass = qfalse;      }    }    else    { -    if( level.teamVoteYes[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 ) +    if( level.teamVoteYes[ cs_offset ] > (int)((double)level.numteamVotingClients[ cs_offset ] * +                                               (double)votePassThreshold/100.0) )      {        // 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 ] ) ); +      pass = qtrue;      } -    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-votePassThreshold)/100.0)) )      {        // 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 ] ); +      pass = qfalse;      }      else      { @@ -2624,11 +2823,70 @@ void CheckTeamVote( int team )      }    } +  trap_SendServerCommand( -1, +    va( "print \"Team vote %s^7 (^2Y:%d^7-^1N:%d^7, %d percent)\n\"", +    ( pass ) ? "^2passed" : "^1failed", +    level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ], voteYesPercent ) ); + +  G_LogPrintf( "Teamvote: Team vote %s (%d - %d)\n", +    ( pass ) ? "passed" : "failed", +    level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ); + +  G_admin_adminlog_log( NULL, "teamvote", +    va( "%s^7 (^2Y:%d^7-^1N:%d^7, %d percent)", +      ( pass ) ? "^2passed" : "^1failed", +      level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ], voteYesPercent ), +    0, pass ); +    level.teamVoteTime[ cs_offset ] = 0;    trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" );    trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, "" );  } + +/* +================== +CheckIdleMap +================== +*/ +void CheckIdleMap( void ) +{ +  static int nextCheck = 0; +  static int idleCount = 0; +  static qboolean triggered = qfalse; + +  if( !g_idleMapSwitch.integer ) +    return; + +  if( triggered || +      nextCheck > level.time ) +    return; + +  nextCheck = level.time + 60000; +  if( !level.numPlayingClients ) +    idleCount++; +  else +    idleCount = 0; + +  if( idleCount > g_idleMapSwitch.integer && +      !G_CurrentMapIsRotation() ) +  { +    int newTL; + +    triggered = qtrue; + +    newTL = ( level.time - level.startTime ) / 60000; +    newTL += 5; +    if( !g_timelimit.integer || g_timelimit.integer > newTL ) +    { +      trap_Cvar_Set( "timelimit", va( "%d", newTL ) ); +      trap_SendServerCommand( -1, va( "print \"Timelimit reduced due to idle server\n\"" ) ); +      G_LogPrintf( "Server is idle, timelimit reduced to %d\n", newTL ); +    } +  } +} + +  /*  ==================  CheckMsgTimer @@ -2723,6 +2981,60 @@ void CheckCountdown( void )  /*  ================== +G_CalculateDynamicBuildPoints +================== +*/ +static void G_CalculateDynamicBuildPoints( void ) +{ +  static int LastTime = 0; +  int count = 0; +  gentity_t *ent; +  int bps; +  int i; + +  if( !g_dynamicBuildPoints.integer ) +    return; + +  if( level.time - LastTime < 10000 ) +    return; + +  if( level.time - level.startTime < 30000 || +      !level.defaultAlienBuildPoints ) +  { +    // remember default build points, after some time for exec commands +    if( level.time - level.startTime > 1000 && +      level.defaultAlienBuildPoints == 0 ) +    { +      level.defaultAlienBuildPoints = g_alienBuildPoints.integer; +      level.defaultHumanBuildPoints = g_humanBuildPoints.integer; +    } +    return; +  } + +  LastTime = level.time; + +  for( i = 0; i < level.maxclients; i++ ) +  { +    ent = &g_entities[ i ]; +    if( ent && ent->client && +        ent->client->pers.connected == CON_CONNECTED && +       (ent->client->pers.teamSelection == PTE_ALIENS || ent->client->pers.teamSelection == PTE_HUMANS) ) +    { +      count++; +    } +  } + +  bps = level.defaultAlienBuildPoints + count * g_dynamicBuildPoints.integer; +  if( g_alienBuildPoints.integer < bps ) +    trap_Cvar_Set( "g_alienBuildPoints", va( "%d", bps ) ); + +  bps = level.defaultHumanBuildPoints + count * g_dynamicBuildPoints.integer; +  if( g_humanBuildPoints.integer < bps ) +    trap_Cvar_Set( "g_humanBuildPoints", va( "%d", bps ) ); +} + +/* +==================  CheckCvars  ==================  */ @@ -2773,6 +3085,179 @@ void CheckCvars( void )  }  /* +================== +G_CheckVampireDeathBuildables +================== +*/ +static int G_VampireDeathKillBuildable( buildable_t buildable, int count ) +{ +  int i; +  gentity_t *ent; +  int n = 0; + +  for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) +  { +    if( ent->s.eType != ET_BUILDABLE ) +      continue; + +    if( ent->s.modelindex == buildable && ent->health > 0 ) +    { +      G_Damage( ent, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); +      n++; + +      if( count && n >= count ) +        return n; +    } +  } + +  return n; +} + +static void G_CheckVampireDeathBuildables( void ) +{ +  static int LastAmmoTime = 0; +  static int LastSuckTime = 0; +  static int LastDestructTime = 0; +  int i; +  gentity_t *ent; +  qboolean done = qtrue; + +  if( !level.vampireDeath ) +    return; + +  if( level.intermissionQueued || +      level.intermissiontime ) +    return; + +  // humans get more ammo every 20 seconds +  if( level.time - LastAmmoTime >= 20000 ) +  { +    LastAmmoTime = level.time; + +    for( i = 0; i < level.maxclients; i++ ) +    { +      ent = g_entities + i; +      if( !ent->inuse || ent->health <= 0 || +          ent->client->pers.connected != CON_CONNECTED) +        continue; + +      if( ent->client->pers.teamSelection == PTE_HUMANS ) +      { +        int ammo, clips, maxClips, maxAmmo; + +        BG_FindAmmoForWeapon( ent->client->ps.weapon, &maxAmmo, &maxClips ); +        BG_UnpackAmmoArray( ent->client->ps.weapon, ent->client->ps.ammo, +                            ent->client->ps.powerups, &ammo, &clips ); + +        if( maxClips ) +        { +          if( clips < maxClips ) +            clips++; +        } +        else +        { +          ammo += maxAmmo / 5; +          if( ammo > maxAmmo ) +            ammo = maxAmmo; +        } + +        BG_PackAmmoArray( ent->client->ps.weapon, ent->client->ps.ammo, +                          ent->client->ps.powerups, ammo, clips ); +      } +    } +  } + +  // health countdown +  if( level.time - LastSuckTime >= 3000 ) +  { +    int value; +    int hp_rate; +    int damage; + +    LastSuckTime = level.time; + +    // increase health removal each minute after 1 +    hp_rate = 1 + ( level.time - level.vampireDeathBeginTime ) / 60000; +    if( hp_rate < 1 ) hp_rate = 1; +    if( hp_rate > 10) hp_rate = 10; + +    for( i = 0; i < level.maxclients; i++ ) +    { +      ent = g_entities + i; +      if( !ent->inuse || +          ent->health <= 0 || +          !ent->client || +          ent->client->pers.connected != CON_CONNECTED || +          ent->client->sess.sessionTeam == TEAM_SPECTATOR || +         ( ent->client->pers.teamSelection != PTE_ALIENS && ent->client->pers.teamSelection != PTE_HUMANS ) ) +        continue; + +      value = BG_FindHealthForClass( ent->client->pers.classSelection ); +      if( value < 1 ) +        value = 1; +      // death from full HP in 60s with damage every 3s, 1000 / 60 * 3 = 50 +      ent->client->pers.vampireSuckFraction += value * 50 * hp_rate; + +      damage = ent->client->pers.vampireSuckFraction / 1000; +      ent->client->pers.vampireSuckFraction -= damage * 1000; + +      if ( ent->health > damage ) +        ent->health -= damage; +      else +        G_Damage( ent, NULL, NULL, NULL, NULL, damage, +          DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_PROTECTION|DAMAGE_NO_LOCDAMAGE, MOD_SUICIDE ); +    } +  } + +  // base self destruct +  if( level.vampireDeath == 2 ) +    return; + +  if( level.time - LastDestructTime < 1000 ) +    return; + +  LastDestructTime = level.time; + +  if( level.numLiveAlienClients == 0 && +      level.numLiveHumanClients == 0 ) +  { +    level.lastWin = PTE_NONE; +    trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" ); +    trap_SetConfigstring( CS_WINNER, "Stalemate" ); +    LogExit( "Timelimit hit." ); +    G_admin_maplog_result( "t" ); +  } + +  if( G_VampireDeathKillBuildable( BA_A_ACIDTUBE, 1 ) || +      G_VampireDeathKillBuildable( BA_A_HIVE, 1 ) || +      G_VampireDeathKillBuildable( BA_A_TRAPPER, 1 ) || +      G_VampireDeathKillBuildable( BA_A_BARRICADE, 1 ) || +      G_VampireDeathKillBuildable( BA_A_BOOSTER, 1 ) || +      G_VampireDeathKillBuildable( BA_A_SPAWN, 1 ) || +      G_VampireDeathKillBuildable( BA_A_OVERMIND, 1 ) ) +  { +    done = qfalse; +  } + +  if( G_VampireDeathKillBuildable( BA_H_MGTURRET, 1 ) || +      G_VampireDeathKillBuildable( BA_H_TESLAGEN, 1 ) || +      G_VampireDeathKillBuildable( BA_H_DCC, 1 ) || +      G_VampireDeathKillBuildable( BA_H_REPEATER, 1 ) || +      G_VampireDeathKillBuildable( BA_H_MEDISTAT, 1 ) || +      G_VampireDeathKillBuildable( BA_H_ARMOURY, 1 ) || +      G_VampireDeathKillBuildable( BA_H_SPAWN, 1 ) || +      G_VampireDeathKillBuildable( BA_H_REACTOR, 1 ) ) +  { +    done = qfalse; +  } + +  if( done ) +  { +    level.vampireDeath = 2; +  } +} + +/*  =============  G_RunThink @@ -2960,6 +3445,8 @@ void G_RunFrame( int levelTime )        ClientEndFrame( ent );    } +  G_CheckVampireDeathBuildables(); +    // save position information for all active clients     G_UnlaggedStore( ); @@ -2974,8 +3461,11 @@ void G_RunFrame( int levelTime )    G_CalculateAvgPlayers( );    G_UpdateZaps( msec ); +  G_CalculateDynamicBuildPoints( ); +    // see if it is time to end the level    CheckExitRules( ); +  CheckIdleMap( );    // update to team status?    CheckTeamStatus( ); diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c index 60fd696..eb76d70 100644 --- a/src/game/g_maprotation.c +++ b/src/game/g_maprotation.c @@ -334,68 +334,6 @@ static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p )        continue;      } -    else if( !Q_stricmp( token, "*RANDOM*" ) ) -    { -      if( mr->numMaps == MAX_MAP_ROTATION_MAPS ) -      { -        G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n", -                  MAX_MAP_ROTATION_MAPS ); -        return qfalse; -      } -      mre = &mr->maps[ mr->numMaps ]; -      Q_strncpyz( mre->name, token, sizeof( mre->name ) ); - -      token = COM_Parse( text_p ); - -      if( !Q_stricmp( token, "{" ) ) -      { -        while( 1 ) -        { -          token = COM_Parse( text_p ); - -          if( !token ) -            break; - -          if( !Q_stricmp( token, "}" ) ) -          { -            break; -          } -          else -          { -            if( mre->numConditions < MAX_MAP_ROTATION_CONDS ) -            { -              mrc = &mre->conditions[ mre->numConditions ]; -              mrc->lhs = MCV_SELECTEDRANDOM; -              mrc->unconditional = qfalse; -              Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); - -              mre->numConditions++; -            } -            else -            { -              G_Printf( S_COLOR_YELLOW "WARNING: maximum number of maps for one Random Slot (%d) reached\n", -                        MAX_MAP_ROTATION_CONDS ); -            } -          } -        } -        if( !mre->numConditions ) -        { -          G_Printf( S_COLOR_YELLOW "WARNING: no maps in *RANDOM* section\n" ); -        } -        else -        { -          mr->numMaps++; -          mnSet = qtrue; -        } -      } -      else -      { -        G_Printf( S_COLOR_RED "ERROR: *RANDOM* with no section\n" ); -        return qfalse; -      } - -      continue; -    }      else if( !Q_stricmp( token, "}" ) )        return qtrue; //reached the end of this map rotation @@ -526,7 +464,6 @@ static qboolean G_ParseMapRotationFile( const char *fileName )      for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ )      {        if( Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*VOTE*") != 0 && -          Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*") != 0 &&            !G_MapExists( mapRotations.rotations[ i ].maps[ j ].name ) )        {          G_Printf( S_COLOR_RED "ERROR: map \"%s\" doesn't exist\n", @@ -696,21 +633,6 @@ static void G_IssueMapChange( int rotation )        return;      }    } -  else if(!Q_stricmp( newmap, "*RANDOM*") ) -  { -    fileHandle_t f; - -    G_GetRandomMap( newmap, sizeof( newmap ), rotation, map ); -    if( trap_FS_FOpenFile( va("maps/%s.bsp", newmap), &f, FS_READ ) > 0 ) -    { -      trap_FS_FCloseFile( f ); -    } -    else -    { -      G_AdvanceMapRotation(); -      return; -    } -  }    // allow a manually defined g_layouts setting to override the maprotation    if( !g_layouts.string[ 0 ] && @@ -720,8 +642,7 @@ static void G_IssueMapChange( int rotation )        mapRotations.rotations[ rotation ].maps[ map ].layouts );    } -  trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", -    newmap ) ); +  trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", newmap ) );    // load up map defaults if g_mapConfigs is set    G_MapConfigs( newmap ); @@ -808,9 +729,6 @@ static qboolean G_EvaluateMapCondition( mapRotationCondition_t *mrc )      case MCV_VOTE:        // ignore vote for conditions;        break; -    case MCV_SELECTEDRANDOM: -      // ignore vote for conditions; -      break;      default:      case MCV_ERR: @@ -940,6 +858,51 @@ qboolean G_MapRotationActive( void )  /*  =============== +G_CurrentMapIsRotation + +Test if the current map is from the rotation +=============== +*/ + +qboolean G_CurrentMapIsRotation( void ) +{ +  char mapname[ 64 ]; +  int map, rotation; +  int i; + +  // Check for an active map rotation, +  // only return false if rotation is running and current map is not from it +  if ( !G_MapRotationActive() || g_currentMapRotation.integer == NOT_ROTATING ) +    return qtrue; + +  rotation = g_currentMapRotation.integer; +  if( !( rotation >= 0 && rotation < mapRotations.numRotations ) ) +    return qtrue; + +  map = G_GetCurrentMap( rotation ); +  if( !(map >= 0 && map < mapRotations.rotations[ rotation ].numMaps ) ) +    return qtrue; + +  trap_Cvar_VariableStringBuffer( "mapname", mapname, sizeof( mapname ) ); + +  if( !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].name, mapname ) ) +    return qtrue; + +  if( !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].name, "*VOTE*" ) ) +  { +    for( i = 0; i < mapRotations.rotations[ rotation ].maps[ map ].numConditions; i++ ) +    { +      if( mapRotations.rotations[ rotation ].maps[ map ].conditions[ i ].lhs == MCV_VOTE && +          !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].conditions[ i ].dest, mapname ) ) +        return qtrue; +    } +  } + +  return qfalse; +} + +/* +===============  G_InitMapRotations  Load and intialise the map rotations @@ -1173,9 +1136,6 @@ static void G_IntermissionMapVoteMessageReal( gentity_t *ent, int winner, int wi      "^7+------------------+\n", sizeof( string ) );    for( i = 0; i < rotationVoteLen; i++ )    { -    if( !G_MapExists( rotationVoteList[ i ] ) ) -      continue; -          if( i == selection )        color = "^5";      else if( i == index ) @@ -1275,42 +1235,3 @@ void G_IntermissionMapVoteCommand( gentity_t *ent, qboolean next, qboolean choos    G_IntermissionMapVoteMessage( ent );  } -static qboolean G_GetRandomMap( char *name, int size, int rotation, int map ) -{ -  mapRotation_t           *mr; -  mapRotationEntry_t      *mre; -  mapRotationCondition_t  *mrc; -  int                     i, nummaps; -  int                     randompick = 0; -  int                     maplist[ 32 ]; - -  mr = &mapRotations.rotations[ rotation ]; -  mre = &mr->maps[ map ]; - -  nummaps = 0; -  //count the number of map votes -  for( i = 0; i < mre->numConditions; i++ ) -  { -    mrc = &mre->conditions[ i ]; - -    if( mrc->lhs == MCV_SELECTEDRANDOM ) -    { -      //map doesnt exist -      if( !G_MapExists( mrc->dest ) ) { -        continue; -      } -      maplist[ nummaps ] = i; -      nummaps++; -    } -  } - -  if( nummaps == 0 ) { -    return qfalse; -  } - -  randompick = (int)( random() * nummaps ); - -  Q_strncpyz( name, mre->conditions[ maplist[ randompick ] ].dest, size ); - -  return qtrue; -} diff --git a/src/game/g_mem.c b/src/game/g_mem.c index 6935194..ea8e438 100644 --- a/src/game/g_mem.c +++ b/src/game/g_mem.c @@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  #include "g_local.h" -#define  POOLSIZE ( 1024 * 1024 ) +#define  POOLSIZE ( 1024 * 1024 * 4 )  #define  FREEMEMCOOKIE  ((int)0xDEADBE3F)  // Any unlikely to be used value  #define  ROUNDBITS    31          // Round to 32 bytes diff --git a/src/game/g_missile.c b/src/game/g_missile.c index 6317636..71b5432 100644 --- a/src/game/g_missile.c +++ b/src/game/g_missile.c @@ -571,6 +571,12 @@ void AHive_ReturnToHive( gentity_t *self )      self->think = G_ExplodeMissile;      self->nextthink = level.time + 15000;    } + +  if( g_modStage3Strength.integer > 0 ) +  { +    int nt = self->nextthink - level.time; +    self->nextthink = level.time + nt * 100 / g_modStage3Strength.integer; +  }  }  /* @@ -584,6 +590,7 @@ void AHive_SearchAndDestroy( gentity_t *self )  {    vec3_t dir;    trace_t tr; +  int speed;    trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,                self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask ); @@ -603,13 +610,26 @@ void AHive_SearchAndDestroy( gentity_t *self )      VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir );      VectorNormalize( dir ); +    if( g_modStage3Strength.integer > 0 ) +      speed = HIVE_SPEED * g_modStage3Strength.integer / 100; +    else +      speed = HIVE_SPEED; +      //change direction towards the player -    VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta ); +    VectorScale( dir, speed, self->s.pos.trDelta );      SnapVector( self->s.pos.trDelta );      // save net bandwidth      VectorCopy( self->r.currentOrigin, self->s.pos.trBase );      self->s.pos.trTime = level.time;      self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD; + +    if( g_modBuildableSpeed.integer > 0) +      self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD * 100 / g_modBuildableSpeed.integer; +    if( g_modStage3Strength.integer > 0) +    { +      int nt = self->nextthink - level.time; +      self->nextthink = level.time + nt * 100 / g_modStage3Strength.integer; +    }    }  } @@ -642,6 +662,12 @@ gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir )    bolt->clipmask = MASK_SHOT;    bolt->target_ent = self->target_ent; +  if( g_modBuildableSpeed.integer > 0) +  { +    bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD * 100 / g_modBuildableSpeed.integer; +    bolt->damage = bolt->damage * g_modBuildableSpeed.integer / 100; +  } +    bolt->s.pos.trType = TR_LINEAR;    bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;   // move a bit on the very first frame    VectorCopy( start, bolt->s.pos.trBase ); @@ -805,6 +831,9 @@ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )    VectorCopy( start, bolt->r.currentOrigin );    /*bolt->s.eFlags |= EF_BOUNCE;*/ +  if( g_modAlienRate.integer > 0 ) +    bolt->damage = bolt->damage * g_modAlienRate.integer / 100; +    return bolt;  } diff --git a/src/game/g_session.c b/src/game/g_session.c index ef78e8a..e1705ea 100644 --- a/src/game/g_session.c +++ b/src/game/g_session.c @@ -78,10 +78,10 @@ void G_ReadSessionData( gclient_t *client )    // bk001205 - format    int teamLeader; +  int invisible;    int spectatorState;    int sessionTeam;    int restartTeam; -  int invisible;    var = va( "session%i", client - level.clients );    trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index a9e04e6..4da6205 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -701,7 +701,8 @@ void teslaFire( gentity_t *ent )    if( !traceEnt->client )      return; -  if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS ) +  if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS && +     !traceEnt->client->pers.bleeder )      return;    //so the client side knows @@ -709,8 +710,13 @@ void teslaFire( gentity_t *ent )    if( traceEnt->takedamage )    { +    int dmg = TESLAGEN_DMG; + +    if( g_modStage3Strength.integer > 0 ) +      dmg = dmg * g_modStage3Strength.integer / 100; +      G_Damage( traceEnt, ent, ent, forward, tr.endpos, -      TESLAGEN_DMG, 0, MOD_TESLAGEN ); +      dmg, 0, MOD_TESLAGEN );    }    // snap the endpos to integers to save net bandwidth, but nudged towards the line @@ -781,6 +787,7 @@ void cancelBuildFire( gentity_t *ent )        traceEnt->health += HBUILD_HEALRATE;        ent->client->pers.statscounters.repairspoisons++; +      ent->client->pers.karma += 1;        level.humanStatsCounters.repairspoisons++;        if( traceEnt->health > bHealth ) @@ -814,7 +821,7 @@ void buildFire( gentity_t *ent, dynMenu_t menu )      if( G_BuildIfValid( ent, ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) )      { -      if( g_cheats.integer ) +      if( g_cheats.integer || g_instantBuild.integer )        {          ent->client->ps.stats[ STAT_MISC ] = 0;        } @@ -1184,6 +1191,8 @@ static void G_CreateNewZap( gentity_t *creator, gentity_t *target )        zap->used = qtrue;        zap->timeToLive = LEVEL2_AREAZAP_TIME; +      if( g_modAlienRate.integer > 0 ) +        zap->timeToLive = zap->timeToLive * g_modAlienRate.integer / 100;        zap->damageUsed = 0;        zap->creator = creator; @@ -1277,6 +1286,8 @@ void G_UpdateZaps( int msec )            {              G_Damage( target, source, zap->creator, forward, target->s.origin,                      damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP ); +            if( g_modAlienRate.integer > 0 ) +              damage = damage * 100 / g_modAlienRate.integer;              zap->damageUsed += damage;            }          } diff --git a/src/game/tremulous.h b/src/game/tremulous.h index 0d2d1c1..c007489 100644 --- a/src/game/tremulous.h +++ b/src/game/tremulous.h @@ -620,7 +620,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA                                           //do to increment the stage kill counters  // g_suddenDeathMode settings -#define SDMODE_BP                   0 +#define SDMODE_BP                   0   #define SDMODE_NO_BUILD             1  #define SDMODE_SELECTIVE            2 -#define SDMODE_NO_DECON             3  | 
