Index: src/server/server.h =================================================================== --- src/server/server.h (revision 819) +++ src/server/server.h (working copy) @@ -227,6 +227,8 @@ extern cvar_t *sv_rconPassword; extern cvar_t *sv_privatePassword; extern cvar_t *sv_allowDownload; +extern cvar_t *sv_wwwDownload; +extern cvar_t *sv_wwwBaseURL; extern cvar_t *sv_maxclients; extern cvar_t *sv_privateClients; Index: src/server/sv_init.c =================================================================== --- src/server/sv_init.c (revision 819) +++ src/server/sv_init.c (working copy) @@ -561,6 +561,10 @@ sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP ); sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO); + sv_wwwDownload = Cvar_Get ("sv_wwwDownload", "1", + CVAR_SYSTEMINFO|CVAR_ARCHIVE); + sv_wwwBaseURL = Cvar_Get ("sv_wwwBaseURL", "", + CVAR_SYSTEMINFO|CVAR_ARCHIVE); sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 ); sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE ); sv_master[2] = Cvar_Get ("sv_master3", "", CVAR_ARCHIVE ); Index: src/server/sv_main.c =================================================================== --- src/server/sv_main.c (revision 819) +++ src/server/sv_main.c (working copy) @@ -33,6 +33,8 @@ cvar_t *sv_rconPassword; // password for remote server commands cvar_t *sv_privatePassword; // password for the privateClient slots cvar_t *sv_allowDownload; +cvar_t *sv_wwwBaseURL; +cvar_t *sv_wwwDownload; cvar_t *sv_maxclients; cvar_t *sv_privateClients; // number of clients reserved for password @@ -129,6 +131,10 @@ // return; // } + // we don't need to send commands to a client until they enter the game + if( client->state < CS_PRIMED ) + return; + client->reliableSequence++; // if we would be losing an old command that hasn't been acknowledged, // we must drop the connection @@ -186,9 +192,6 @@ // send the data to all relevent clients for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) { - if ( client->state < CS_PRIMED ) { - continue; - } SV_AddServerCommand( client, (char *)message ); } } Index: src/game/bg_misc.c =================================================================== --- src/game/bg_misc.c (revision 819) +++ src/game/bg_misc.c (working copy) @@ -67,7 +67,8 @@ qfalse, //qboolean creepTest; ASPAWN_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_A_BARRICADE, //int buildNum; @@ -102,7 +103,7 @@ qtrue, //qboolean creepTest; BARRICADE_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; }, { BA_A_BOOSTER, //int buildNum; @@ -137,7 +138,8 @@ qtrue, //qboolean creepTest; BOOSTER_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_A_ACIDTUBE, //int buildNum; @@ -172,7 +174,8 @@ qtrue, //qboolean creepTest; ACIDTUBE_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_A_HIVE, //int buildNum; @@ -207,7 +210,8 @@ qtrue, //qboolean creepTest; HIVE_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_A_TRAPPER, //int buildNum; @@ -242,7 +246,8 @@ qtrue, //qboolean creepTest; TRAPPER_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_A_OVERMIND, //int buildNum; @@ -277,7 +282,8 @@ qfalse, //qboolean creepTest; OVERMIND_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qtrue //qboolean reactorTest; + qtrue, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_A_HOVEL, //int buildNum; @@ -312,7 +318,8 @@ qtrue, //qboolean creepTest; HOVEL_CREEPSIZE, //int creepSize; qfalse, //qboolean dccTest; - qtrue //qboolean reactorTest; + qtrue, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_H_SPAWN, //int buildNum; @@ -347,7 +354,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_H_MEDISTAT, //int buildNum; @@ -382,7 +390,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_H_MGTURRET, //int buildNum; @@ -419,7 +428,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_H_TESLAGEN, //int buildNum; @@ -454,7 +464,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qtrue, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qfalse, //qboolean replacable; }, { BA_H_DCC, //int buildNum; @@ -489,7 +500,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_H_ARMOURY, //int buildNum; @@ -524,7 +536,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_H_REACTOR, //int buildNum; @@ -559,7 +572,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qtrue //qboolean reactorTest; + qtrue, //qboolean reactorTest; + qtrue, //qboolean replacable; }, { BA_H_REPEATER, //int buildNum; @@ -594,7 +608,8 @@ qfalse, //qboolean creepTest; 0, //int creepSize; qfalse, //qboolean dccTest; - qfalse //qboolean reactorTest; + qfalse, //qboolean reactorTest; + qtrue, //qboolean replacable; } }; @@ -1290,6 +1305,25 @@ /* ============== +BG_FindReplaceableTestForBuildable +============== +*/ +qboolean BG_FindReplaceableTestForBuildable( int bclass ) +{ + int i; + + for( i = 0; i < bg_numBuildables; i++ ) + { + if( bg_buildableList[ i ].buildNum == bclass ) + { + return bg_buildableList[ i ].replaceable; + } + } + return qfalse; +} + +/* +============== BG_FindOverrideForBuildable ============== */ Index: src/game/tremulous.h =================================================================== --- src/game/tremulous.h (revision 819) +++ src/game/tremulous.h (working copy) @@ -306,8 +306,8 @@ #define ALIENSENSE_RANGE 1000.0f -#define ALIEN_POISON_TIME 10000 -#define ALIEN_POISON_DMG 30 +#define ALIEN_POISON_TIME 5000 +#define ALIEN_POISON_DMG 5 #define ALIEN_POISON_DIVIDER (1.0f/1.32f) //about 1.0/(time`th root of damage) #define ALIEN_SPAWN_REPEAT_TIME 10000 @@ -427,9 +427,11 @@ */ #define LIGHTARMOUR_PRICE 70 +#define LIGHTARMOUR_POISON_PROTECTION 1 #define HELMET_PRICE 90 #define HELMET_RANGE 1000.0f +#define HELMET_POISON_PROTECTION 2 #define MEDKIT_PRICE 0 @@ -443,6 +445,7 @@ #define JETPACK_DISABLE_CHANCE 0.3f #define BSUIT_PRICE 400 +#define BSUIT_POISON_PROTECTION 4 #define MGCLIP_PRICE 0 @@ -450,7 +453,7 @@ #define GAS_PRICE 0 -#define MEDKIT_POISON_IMMUNITY_TIME 30000 +#define MEDKIT_POISON_IMMUNITY_TIME 0 #define MEDKIT_STARTUP_TIME 4000 #define MEDKIT_STARTUP_SPEED 5 @@ -588,3 +591,8 @@ #define DAMAGE_FRACTION_FOR_KILL 0.5f //how much damage players (versus structures) need to //do to increment the stage kill counters + +// g_suddenDeathMode settings +#define SDMODE_BP 0 +#define SDMODE_NO_BUILD 1 +#define SDMODE_SELECTIVE 2 Index: src/game/g_svcmds.c =================================================================== --- src/game/g_svcmds.c (revision 819) +++ src/game/g_svcmds.c (working copy) @@ -600,6 +600,11 @@ G_Printf( "cp: %s\n", ConcatArgs( 1 ) ); return qtrue; } + else if( !Q_stricmp( cmd, "m" ) ) + { + G_PrivateMessage( NULL ); + return qtrue; + } G_Printf( "unknown command: %s\n", cmd ); return qtrue; Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 819) +++ src/game/g_local.h (working copy) @@ -609,6 +609,9 @@ pTeam_t lastWin; + int suddenDeathABuildPoints; + int suddenDeathHBuildPoints; + qboolean suddenDeath; timeWarning_t suddenDeathWarning; timeWarning_t timelimitWarning; @@ -651,6 +654,7 @@ void G_DecolorString( char *in, char *out ); void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam ); void G_SanitiseName( char *in, char *out ); +void G_PrivateMessage( gentity_t *ent ); // // g_physics.c @@ -1044,6 +1048,7 @@ extern vmCvar_t g_timelimit; extern vmCvar_t g_suddenDeathTime; +extern vmCvar_t g_suddenDeathMode; extern vmCvar_t g_friendlyFire; extern vmCvar_t g_friendlyFireHumans; extern vmCvar_t g_friendlyFireAliens; @@ -1111,6 +1116,9 @@ extern vmCvar_t g_adminNameProtect; extern vmCvar_t g_adminTempBan; +extern vmCvar_t g_privateMessages; +extern vmCvar_t g_dretchPunt; + void trap_Printf( const char *fmt ); void trap_Error( const char *fmt ); int trap_Milliseconds( void ); Index: src/game/g_combat.c =================================================================== --- src/game/g_combat.c (revision 819) +++ src/game/g_combat.c (working copy) @@ -141,6 +141,7 @@ char *killerName, *obit; float totalDamage = 0.0f; gentity_t *player; + qboolean tk = qfalse; if( self->client->ps.pm_type == PM_DEAD ) @@ -169,7 +170,11 @@ killer = attacker->s.number; if( attacker->client ) + { killerName = attacker->client->pers.netname; + tk = ( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] + == self->client->ps.stats[ STAT_PTEAM ] ); + } else killerName = ""; } @@ -202,11 +207,24 @@ BG_DeactivateUpgrade( i, self->client->ps.stats ); // broadcast the death event to everyone - ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); - ent->s.eventParm = meansOfDeath; - ent->s.otherEntityNum = self->s.number; - ent->s.otherEntityNum2 = killer; - ent->r.svFlags = SVF_BROADCAST; // send to everyone + if( !tk ) + { + ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); + ent->s.eventParm = meansOfDeath; + ent->s.otherEntityNum = self->s.number; + ent->s.otherEntityNum2 = killer; + ent->r.svFlags = SVF_BROADCAST; // send to everyone + } + else + { + // tjw: obviously this is a hack and belongs in the client, but + // this works as a temporary fix. + trap_SendServerCommand( -1, + va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s\n\"", + self->client->pers.netname, attacker->client->pers.netname ) ); + trap_SendServerCommand( attacker - g_entities, + va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) ); + } self->enemy = attacker; @@ -1021,8 +1039,20 @@ // if the attacker was on the same team if( targ != attacker && OnSameTeam( targ, attacker ) ) { - if( !g_friendlyFire.integer ) + if( g_dretchPunt.integer && + targ->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL0 ) { + vec3_t dir, push; + + VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, dir ); + VectorNormalizeFast( dir ); + VectorScale( dir, ( damage * 10.0f ), push ); + push[2] = 64.0f; + VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity ); + return; + } + else if( !g_friendlyFire.integer ) + { if( !g_friendlyFireHumans.integer && targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { @@ -1097,8 +1127,8 @@ //if boosted poison every attack if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) { - if( !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) && - !BG_InventoryContainsUpgrade( UP_BATTLESUIT, targ->client->ps.stats ) && + if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && + !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) && mod != MOD_LEVEL2_ZAP && targ->client->poisonImmunityTime < level.time ) { Index: src/game/g_active.c =================================================================== --- src/game/g_active.c (revision 819) +++ src/game/g_active.c (working copy) @@ -683,22 +683,17 @@ //client is poisoned if( client->ps.stats[ STAT_STATE ] & SS_POISONED ) { - int i; - int seconds = ( ( level.time - client->lastPoisonTime ) / 1000 ) + 1; - int damage = ALIEN_POISON_DMG, damage2 = 0; + int damage = ALIEN_POISON_DMG; + + if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) ) + damage -= BSUIT_POISON_PROTECTION; + if( BG_InventoryContainsUpgrade( UP_HELMET, client->ps.stats ) ) + damage -= HELMET_POISON_PROTECTION; + if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) + damage -= LIGHTARMOUR_POISON_PROTECTION; - for( i = 0; i < seconds; i++ ) - { - if( i == seconds - 1 ) - damage2 = damage; - - damage *= ALIEN_POISON_DIVIDER; - } - - damage = damage2 - damage; - - G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, NULL, - damage, 0, MOD_POISON ); + G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, + 0, damage, 0, MOD_POISON ); } //replenish alien health Index: src/game/g_buildable.c =================================================================== --- src/game/g_buildable.c (revision 819) +++ src/game/g_buildable.c (working copy) @@ -23,6 +23,9 @@ #include "g_local.h" +// from g_combat.c +extern char *modNames[ ]; + /* ================ G_setBuildableAnim @@ -166,7 +169,6 @@ int distance = 0; int minDistance = 10000; vec3_t temp_v; - qboolean foundPower = qfalse; if( self->biteam != BIT_HUMANS ) return qfalse; @@ -194,26 +196,22 @@ { VectorSubtract( self->s.origin, ent->s.origin, temp_v ); distance = VectorLength( temp_v ); - if( distance < minDistance && ent->powered ) - { - closestPower = ent; - minDistance = distance; - foundPower = qtrue; + + if( distance < minDistance && ent->powered && + ( ( ent->s.modelindex == BA_H_REACTOR && + distance <= REACTOR_BASESIZE ) || + ( ent->s.modelindex == BA_H_REPEATER && + distance <= REPEATER_BASESIZE ) ) ) { + + closestPower = ent; + minDistance = distance; } } } //if there were no power items nearby give up - if( !foundPower ) - return qfalse; - - //bleh - if( ( closestPower->s.modelindex == BA_H_REACTOR && ( minDistance <= REACTOR_BASESIZE ) ) || - ( closestPower->s.modelindex == BA_H_REPEATER && ( minDistance <= REPEATER_BASESIZE ) && - closestPower->powered ) ) - { + if( closestPower ) { self->parentNode = closestPower; - return qtrue; } else @@ -633,12 +631,27 @@ self->s.eFlags &= ~EF_FIRING; //prevent any firing effects - if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + if( attacker && attacker->client ) { - if( self->s.modelindex == BA_A_OVERMIND ) - G_AddCreditToClient( attacker->client, OVERMIND_VALUE, qtrue ); - else if( self->s.modelindex == BA_A_SPAWN ) - G_AddCreditToClient( attacker->client, ASPAWN_VALUE, qtrue ); + if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + { + if( self->s.modelindex == BA_A_OVERMIND ) + G_AddCreditToClient( attacker->client, OVERMIND_VALUE, qtrue ); + else if( self->s.modelindex == BA_A_SPAWN ) + G_AddCreditToClient( attacker->client, ASPAWN_VALUE, qtrue ); + } + else + { + G_TeamCommand( PTE_ALIENS, + va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"", + BG_FindHumanNameForBuildable( self->s.modelindex ), + attacker->client->pers.netname ) ); + } + G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n", + attacker->client->ps.clientNum, self->s.modelindex, mod, + attacker->client->pers.netname, + BG_FindNameForBuildable( self->s.modelindex ), + modNames[ mod ] ); } } @@ -845,6 +858,22 @@ self->nextthink = level.time + 5000; else self->nextthink = level.time; //blast immediately + + if( attacker && attacker->client ) + { + if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + { + G_TeamCommand( PTE_ALIENS, + va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"", + BG_FindHumanNameForBuildable( self->s.modelindex ), + attacker->client->pers.netname ) ); + } + G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n", + attacker->client->ps.clientNum, self->s.modelindex, mod, + attacker->client->pers.netname, + BG_FindNameForBuildable( self->s.modelindex ), + modNames[ mod ] ); + } } /* @@ -1257,6 +1286,22 @@ self->r.contents = 0; //stop collisions... trap_LinkEntity( self ); //...requires a relink + + if( attacker && attacker->client ) + { + if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + { + G_TeamCommand( PTE_ALIENS, + va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"", + BG_FindHumanNameForBuildable( self->s.modelindex ), + attacker->client->pers.netname ) ); + } + G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n", + attacker->client->ps.clientNum, self->s.modelindex, mod, + attacker->client->pers.netname, + BG_FindNameForBuildable( self->s.modelindex ), + modNames[ mod ] ); + } } @@ -2198,12 +2243,27 @@ self->nextthink = level.time; //blast immediately } - if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + if( attacker && attacker->client ) { - if( self->s.modelindex == BA_H_REACTOR ) - G_AddCreditToClient( attacker->client, REACTOR_VALUE, qtrue ); - else if( self->s.modelindex == BA_H_SPAWN ) - G_AddCreditToClient( attacker->client, HSPAWN_VALUE, qtrue ); + if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + { + if( self->s.modelindex == BA_H_REACTOR ) + G_AddCreditToClient( attacker->client, REACTOR_VALUE, qtrue ); + else if( self->s.modelindex == BA_H_SPAWN ) + G_AddCreditToClient( attacker->client, HSPAWN_VALUE, qtrue ); + } + else + { + G_TeamCommand( PTE_HUMANS, + va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"", + BG_FindHumanNameForBuildable( self->s.modelindex ), + attacker->client->pers.netname ) ); + } + G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n", + attacker->client->ps.clientNum, self->s.modelindex, mod, + attacker->client->pers.netname, + BG_FindNameForBuildable( self->s.modelindex ), + modNames[ mod ] ); } } Index: src/game/g_main.c =================================================================== --- src/game/g_main.c (revision 819) +++ src/game/g_main.c (working copy) @@ -42,6 +42,7 @@ vmCvar_t g_fraglimit; vmCvar_t g_timelimit; vmCvar_t g_suddenDeathTime; +vmCvar_t g_suddenDeathMode; vmCvar_t g_capturelimit; vmCvar_t g_friendlyFire; vmCvar_t g_friendlyFireAliens; @@ -121,6 +122,10 @@ vmCvar_t g_adminNameProtect; vmCvar_t g_adminTempBan; +vmCvar_t g_privateMessages; + +vmCvar_t g_dretchPunt; + static cvarTable_t gameCvarTable[ ] = { // don't override the cheat state set by the system @@ -141,6 +146,7 @@ // change anytime vars { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_suddenDeathTime, "g_suddenDeathTime", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, + { &g_suddenDeathMode, "g_suddenDeathMode", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse }, @@ -226,6 +232,10 @@ { &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_adminTempBan, "g_adminTempBan", "120", CVAR_ARCHIVE, 0, qfalse }, + { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse }, + + { &g_dretchPunt, "g_dretchPunt", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_rankings, "g_rankings", "0", 0, 0, qfalse} }; @@ -981,14 +991,32 @@ if( !level.warmupTime && ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) ) { - localHTP = 0; - localATP = 0; - - //warn about sudden death - if( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 && - level.suddenDeathWarning < TW_PASSED ) + // begin sudden death + if( level.suddenDeathWarning < TW_PASSED ) { trap_SendServerCommand( -1, "cp \"Sudden Death!\"" ); + localHTP = 0; + localATP = 0; + if( g_suddenDeathMode.integer == SDMODE_SELECTIVE ) { + for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( BG_FindReplaceableTestForBuildable( ent->s.modelindex ) ) + { + int t = BG_FindTeamForBuildable( ent->s.modelindex ); + + if( t == BIT_HUMANS ) + localHTP += BG_FindBuildPointsForBuildable( ent->s.modelindex ); + else if( t == BIT_ALIENS ) + localATP += BG_FindBuildPointsForBuildable( ent->s.modelindex ); + } + } + } + level.suddenDeathHBuildPoints = localHTP; + level.suddenDeathABuildPoints = localATP; + level.suddenDeath = qtrue; level.suddenDeathWarning = TW_PASSED; } } @@ -1003,6 +1031,12 @@ } } } + + if( level.suddenDeath ) + { + localHTP = level.suddenDeathHBuildPoints; + localATP = level.suddenDeathABuildPoints; + } else { localHTP = g_humanBuildPoints.integer; @@ -1033,17 +1067,19 @@ if( buildable == BA_A_OVERMIND && ent->spawned && ent->health > 0 ) level.overmindPresent = qtrue; - if( BG_FindTeamForBuildable( buildable ) == BIT_HUMANS ) + if( !level.suddenDeath || BG_FindReplaceableTestForBuildable( buildable ) ) { - level.humanBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); - - if( ent->powered ) - level.humanBuildPointsPowered -= BG_FindBuildPointsForBuildable( buildable ); + if( BG_FindTeamForBuildable( buildable ) == BIT_HUMANS ) + { + level.humanBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); + if( ent->powered ) + level.humanBuildPointsPowered -= BG_FindBuildPointsForBuildable( buildable ); + } + else + { + level.alienBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); + } } - else - { - level.alienBuildPoints -= BG_FindBuildPointsForBuildable( buildable ); - } } } Index: src/game/bg_pmove.c =================================================================== --- src/game/bg_pmove.c (revision 819) +++ src/game/bg_pmove.c (working copy) @@ -510,12 +510,19 @@ pm->ps->weapon != WP_ALEVEL3_UPG ) return qfalse; - if( pm->cmd.buttons & BUTTON_ATTACK2 ) + // we were pouncing, but we've landed + if( pm->ps->groundEntityNum != ENTITYNUM_NONE + && ( pm->ps->pm_flags & PMF_CHARGE ) ) { + pm->ps->weaponTime += LEVEL3_POUNCE_TIME; pm->ps->pm_flags &= ~PMF_CHARGE; - return qfalse; } + // we're building up for a pounce + if( pm->cmd.buttons & BUTTON_ATTACK2 ) + return qfalse; + + // already a pounce in progress if( pm->ps->pm_flags & PMF_CHARGE ) return qfalse; @@ -2705,7 +2712,15 @@ return; } - // make weapon function + + // no bite during pounce + if( ( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG ) + && ( pm->cmd.buttons & BUTTON_ATTACK ) + && ( pm->ps->pm_flags & PMF_CHARGE ) ) + { + return; + } + if( pm->ps->weaponTime > 0 ) pm->ps->weaponTime -= pml.msec; Index: src/game/g_weapon.c =================================================================== --- src/game/g_weapon.c (revision 819) +++ src/game/g_weapon.c (working copy) @@ -1323,7 +1323,6 @@ G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE ); - ent->client->ps.weaponTime += LEVEL3_POUNCE_TIME; ent->client->allowedToPounce = qfalse; return qtrue; Index: src/game/bg_public.h =================================================================== --- src/game/bg_public.h (revision 819) +++ src/game/bg_public.h (working copy) @@ -1017,6 +1017,7 @@ qboolean dccTest; qboolean reactorTest; + qboolean replaceable; } buildableAttributes_t; typedef struct @@ -1142,6 +1143,7 @@ int BG_FindCreepSizeForBuildable( int bclass ); int BG_FindDCCTestForBuildable( int bclass ); int BG_FindUniqueTestForBuildable( int bclass ); +qboolean BG_FindReplaceableTestForBuildable( int bclass ); void BG_InitBuildableOverrides( void ); int BG_FindClassNumForName( char *name ); Index: src/game/g_cmds.c =================================================================== --- src/game/g_cmds.c (revision 819) +++ src/game/g_cmds.c (working copy) @@ -819,14 +819,14 @@ { default: case SAY_ALL: - G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); + G_LogPrintf( "say: %s^7: %s\n", ent->client->pers.netname, chatText ); Com_sprintf( name, sizeof( name ), "%s%s%c%c"EC": ", prefix, ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; case SAY_TEAM: - G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); + G_LogPrintf( "sayteam: %s^7: %s\n", ent->client->pers.netname, chatText ); if( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) Com_sprintf( name, sizeof( name ), EC"(%s%c%c"EC") (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); @@ -857,10 +857,6 @@ return; } - // echo the text to the console - if( g_dedicated.integer ) - G_Printf( "%s%s\n", name, text); - // send it to all the apropriate clients for( j = 0; j < level.maxclients; j++ ) { @@ -883,12 +879,28 @@ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { char *p; + char *args; if( ent->client->pers.muted ) { return; } + // support parsing /m out of say text since some people have a hard + // time figuring out what the console is. + if( g_privateMessages.integer ) + { + args = G_SayConcatArgs(0); + if( !Q_stricmpn( args, "say /m ", 7 ) || + !Q_stricmpn( args, "say_team /m ", 12 ) || + !Q_stricmpn( args, "say /mt ", 8 ) || + !Q_stricmpn( args, "say_team /mt ", 13 ) ) + { + G_PrivateMessage( ent ); + return; + } + } + if( trap_Argc( ) < 2 && !arg0 ) return; @@ -927,7 +939,8 @@ p = ConcatArgs( 2 ); - G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); + G_LogPrintf( "tell: %s^7 to %s^7: %s\n", ent->client->pers.netname, + target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot @@ -1104,6 +1117,8 @@ trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " called a vote\n\"", ent->client->pers.netname ) ); + G_Printf( "'%s' called a vote for '%s'\n", ent->client->pers.netname, + level.voteString ) ; ent->client->pers.voteCount++; @@ -1342,6 +1357,8 @@ trap_SendServerCommand( i, va("print \"%s " S_COLOR_WHITE "called a team vote\n\"", ent->client->pers.netname ) ); } + G_Printf( "'%s' called a teamvote for '%s'\n", ent->client->pers.netname, + level.teamVoteString[ cs_offset ] ) ; // start the voting, the caller autoamtically votes yes level.teamVoteTime[ cs_offset ] = level.time; @@ -1714,10 +1731,16 @@ return; // Don't allow destruction of buildables that cannot be rebuilt - if( g_suddenDeathTime.integer && ( level.time - level.startTime >= - g_suddenDeathTime.integer * 60000 ) && - BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) + if( level.suddenDeath && traceEnt->health > 0 && + ( ( g_suddenDeathMode.integer == SDMODE_SELECTIVE && + !BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) || + ( g_suddenDeathMode.integer == SDMODE_BP && + BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) || + g_suddenDeathMode.integer == SDMODE_NO_BUILD ) ) { + trap_SendServerCommand( ent-g_entities, + "print \"During Sudden Death you can only decon buildings that " + "can be rebuilt\n\"" ); return; } @@ -1726,6 +1749,18 @@ G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum ); return; } + if( traceEnt->health > 0 ) + { + G_TeamCommand( ent->client->pers.teamSelection, + va( "print \"%s ^3DECONSTRUCTED^7 by %s^7\n\"", + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ), + ent->client->pers.netname ) ); + } + G_LogPrintf("Decon: %i %i 0: %s^7 deconstructed %s\n", + ent->client->ps.clientNum, + traceEnt->s.modelindex, + ent->client->pers.netname, + BG_FindNameForBuildable( traceEnt->s.modelindex ) ); if( !deconstruct && CheatsOk( ent ) ) G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10000, 0, MOD_SUICIDE ); @@ -1895,10 +1930,11 @@ if( buyingEnergyAmmo ) { //no armoury nearby - if( ( !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REACTOR ) && - !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REPEATER ) ) ) + if( !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REACTOR ) && + !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REPEATER ) && + !G_BuildableRange( ent->client->ps.origin, 100, BA_H_ARMOURY ) ) { - trap_SendServerCommand( ent-g_entities, va( "print \"You must be near a reactor or repeater\n\"" ) ); + trap_SendServerCommand( ent-g_entities, va( "print \"You must be near a reactor, repeater, or armoury\n\"" ) ); return; } } @@ -2219,6 +2255,23 @@ trap_Argv( 1, s, sizeof( s ) ); buildable = BG_FindBuildNumForName( s ); + + if( level.suddenDeath ) + { + if( g_suddenDeathMode.integer == SDMODE_SELECTIVE && + !BG_FindReplaceableTestForBuildable( buildable ) ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Only essential buildings can be rebuilt in Sudden Death\n\"" ); + return; + } + else if( g_suddenDeathMode.integer == SDMODE_NO_BUILD ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Building is not allowed during Sudden Death\n\"" ); + } + } + team = ent->client->ps.stats[ STAT_PTEAM ]; if( buildable != BA_NONE && @@ -2616,6 +2669,12 @@ Cmd_Tell_f( ent ); return; } + + if( !Q_stricmp( cmd, "m" ) || !Q_stricmp( cmd, "mt" ) ) + { + G_PrivateMessage( ent ); + return; + } if( Q_stricmp( cmd, "score" ) == 0 ) { @@ -2808,3 +2867,105 @@ } *out = '\0'; } + + +void G_PrivateMessage( gentity_t *ent ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ]; + char netname[ MAX_NAME_LENGTH ]; + char cmd[ 12 ]; + char str[ MAX_STRING_CHARS ]; + char *msg; + char color; + int pcount, count = 0; + int i; + int skipargs = 0; + qboolean teamonly = qfalse; + gentity_t *tmpent; + + if( !g_privateMessages.integer && ent ) + return; + + G_SayArgv( 0, cmd, sizeof( cmd ) ); + if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) ) + { + skipargs = 1; + G_SayArgv( 1, cmd, sizeof( cmd ) ); + } + if( G_SayArgc( ) < 3+skipargs ) + { + ADMP( va( "usage: %s [name|slot#] [message]\n", cmd ) ); + return; + } + + if( !Q_stricmp( cmd, "mt" ) || !Q_stricmp( cmd, "/mt" ) ) + teamonly = qtrue; + + G_SayArgv( 1+skipargs, name, sizeof( name ) ); + msg = G_SayConcatArgs( 2+skipargs ); + pcount = G_ClientNumbersFromString( name, pids ); + + if( ent ) + { + Q_strncpyz( netname, ent->client->pers.netname, sizeof( netname ) ); + if( teamonly ) + { + for( i=0; i < pcount; i++ ) + { + if( !OnSameTeam( ent, &g_entities[ pids[ i ] ] ) ) + continue; + pids[ count ] = pids[ i ]; + count++; + } + pcount = count; + } + } + else + { + Q_strncpyz( netname, "console", sizeof( name ) ); + } + + color = teamonly ? COLOR_CYAN : COLOR_YELLOW; + + Q_strncpyz( str, + va( "^%csent to %i player%s: ^7", color, pcount, + ( pcount == 1 ) ? "" : "s" ), + sizeof( str ) ); + + for( i=0; i < pcount; i++ ) + { + tmpent = &g_entities[ pids[ i ] ]; + + if( i > 0 ) + Q_strcat( str, sizeof( str ), "^7, " ); + Q_strcat( str, sizeof( str ), tmpent->client->pers.netname ); + CPx( pids[ i ], va( + "chat \"%s^%c -> ^7%s^7: (%d recipients): ^%c%s^7\" %i", + netname, + color, + name, + pcount, + color, + msg, + ent ? ent-g_entities : -1 ) ); + CPx( pids[ i ], va("cp \"^%cprivate message from ^7%s^7\"", + color, + netname ) ); + } + + if( !pcount ) + ADMP( va( "^3No player matching ^7\'%s^7\' ^3to send message to.\n", + name ) ); + else + { + ADMP( va( "^%cPrivate message: ^7%s\n", color, msg ) ); + ADMP(va("%s\n", str)); + + if( teamonly ) + G_LogPrintf( "tprivmsg: %s^7: %s^7: %s\n", netname, name, msg ); + else + G_LogPrintf( "privmsg: %s^7: %s^7: %s\n", netname, name, msg ); + } +} + Index: src/cgame/cg_event.c =================================================================== --- src/cgame/cg_event.c (revision 819) +++ src/cgame/cg_event.c (working copy) @@ -106,7 +106,7 @@ message = "was in the wrong place"; break; case MOD_HSPAWN: - message = "should have ran further"; + message = "should have run further"; break; case MOD_ASPAWN: message = "shouldn't have trod in the acid";