diff options
-rw-r--r-- | src/cgame/cg_buildable.c | 55 | ||||
-rw-r--r-- | src/cgame/cg_servercmds.c | 12 | ||||
-rw-r--r-- | src/cgame/cg_tutorial.c | 26 | ||||
-rw-r--r-- | src/game/bg_misc.c | 1 | ||||
-rw-r--r-- | src/game/bg_public.h | 3 | ||||
-rw-r--r-- | src/game/g_active.c | 15 | ||||
-rw-r--r-- | src/game/g_buildable.c | 118 | ||||
-rw-r--r-- | src/game/g_weapon.c | 8 |
8 files changed, 169 insertions, 69 deletions
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c index d34a8e1f..7f2b2edf 100644 --- a/src/cgame/cg_buildable.c +++ b/src/cgame/cg_buildable.c @@ -1169,7 +1169,7 @@ static int CG_SortDistance( const void *a, const void *b ) CG_PlayerIsBuilder ================== */ -static qboolean CG_PlayerIsBuilder( void ) +static qboolean CG_PlayerIsBuilder( buildable_t buildable ) { switch( cg.predictedPlayerState.weapon ) { @@ -1177,7 +1177,8 @@ static qboolean CG_PlayerIsBuilder( void ) case WP_ABUILD2: case WP_HBUILD: case WP_HBUILD2: - return qtrue; + return BG_FindTeamForBuildable( buildable ) == + BG_FindTeamForWeapon( cg.predictedPlayerState.weapon ); default: return qfalse; @@ -1186,6 +1187,28 @@ static qboolean CG_PlayerIsBuilder( void ) /* ================== +CG_BuildableRemovalPending +================== +*/ +static qboolean CG_BuildableRemovalPending( int entityNum ) +{ + int i; + playerState_t *ps = &cg.snap->ps; + + if( !( ps->stats[ STAT_BUILDABLE ] & SB_VALID_TOGGLEBIT ) ) + return qfalse; + + for( i = 0; i < MAX_MISC; i++ ) + { + if( ps->misc[ i ] == entityNum ) + return qtrue; + } + + return qfalse; +} + +/* +================== CG_DrawBuildableStatus ================== */ @@ -1197,22 +1220,18 @@ void CG_DrawBuildableStatus( void ) int buildableList[ MAX_ENTITIES_IN_SNAPSHOT ]; int buildables = 0; - if( CG_PlayerIsBuilder( ) ) + for( i = 0; i < cg.snap->numEntities; i++ ) { - for( i = 0; i < cg.snap->numEntities; i++ ) - { - cent = &cg_entities[ cg.snap->entities[ i ].number ]; - es = ¢->currentState; + cent = &cg_entities[ cg.snap->entities[ i ].number ]; + es = ¢->currentState; - if( es->eType == ET_BUILDABLE && - BG_FindTeamForBuildable( es->modelindex ) == - BG_FindTeamForWeapon( cg.predictedPlayerState.weapon ) ) - buildableList[ buildables++ ] = cg.snap->entities[ i ].number; - } - qsort( buildableList, buildables, sizeof( int ), CG_SortDistance ); - for( i = 0; i < buildables; i++ ) - CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ] ); + if( es->eType == ET_BUILDABLE && CG_PlayerIsBuilder( es->modelindex ) ) + buildableList[ buildables++ ] = cg.snap->entities[ i ].number; } + + qsort( buildableList, buildables, sizeof( int ), CG_SortDistance ); + for( i = 0; i < buildables; i++ ) + CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ] ); } #define BUILDABLE_SOUND_PERIOD 500 @@ -1302,7 +1321,7 @@ void CG_Buildable( centity_t *cent ) else ent.nonNormalizedAxes = qfalse; - if( CG_PlayerIsBuilder( ) && ( es->generic1 & B_REMOVED_TOGGLEBIT ) ) + if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) ent.customShader = cgs.media.redBuildShader; //add to refresh list @@ -1347,7 +1366,7 @@ void CG_Buildable( centity_t *cent ) else turretBarrel.nonNormalizedAxes = qfalse; - if( CG_PlayerIsBuilder( ) && ( es->generic1 & B_REMOVED_TOGGLEBIT ) ) + if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretBarrel.customShader = cgs.media.redBuildShader; trap_R_AddRefEntityToScene( &turretBarrel ); @@ -1392,7 +1411,7 @@ void CG_Buildable( centity_t *cent ) else turretTop.nonNormalizedAxes = qfalse; - if( CG_PlayerIsBuilder( ) && ( es->generic1 & B_REMOVED_TOGGLEBIT ) ) + if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretTop.customShader = cgs.media.redBuildShader; trap_R_AddRefEntityToScene( &turretTop ); diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index 811733e7..f9822241 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -950,18 +950,6 @@ static void CG_ServerCommand( void ) return; } - if( !strcmp( cmd, "weaponswitch" ) ) - { - CG_Printf( "client weaponswitch\n" ); - if( trap_Argc( ) == 2 ) - { - cg.weaponSelect = atoi( CG_Argv( 1 ) ); - cg.weaponSelectTime = cg.time; - } - - return; - } - // server requests a ptrc if( !strcmp( cmd, "ptrcrequest" ) ) { diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c index 888e9e1a..9997abcd 100644 --- a/src/cgame/cg_tutorial.c +++ b/src/cgame/cg_tutorial.c @@ -351,8 +351,9 @@ CG_HumanCkitText */ static void CG_HumanCkitText( char *text, playerState_t *ps ) { - buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT; - float health; + buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT; + entityState_t *es; + float health; if( buildable > BA_NONE ) { @@ -380,7 +381,28 @@ static void CG_HumanCkitText( char *text, playerState_t *ps ) va( "Hold %s to repair this structure\n", CG_KeyNameForCommand( "+button5" ) ) ); } + } + } + if( ( es = CG_BuildableInRange( ps, NULL ) ) ) + { + if( cgs.markDeconstruct ) + { + if( es->generic1 & B_MARKED_TOGGLEBIT ) + { + Q_strcat( text, MAX_TUTORIAL_TEXT, + va( "Press %s to unmark this structure\n", + CG_KeyNameForCommand( "deconstruct" ) ) ); + } + else + { + Q_strcat( text, MAX_TUTORIAL_TEXT, + va( "Press %s to mark this structure\n", + CG_KeyNameForCommand( "deconstruct" ) ) ); + } + } + else + { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to destroy this structure\n", CG_KeyNameForCommand( "deconstruct" ) ) ); diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index 2319f31f..85c1f47a 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -5166,6 +5166,7 @@ void BG_PositionBuildableRelativeToPlayer( const playerState_t *ps, //so buildings drop to floor VectorMA( targetOrigin, -128, playerNormal, targetOrigin ); + // The mask is MASK_DEADSOLID on purpose to avoid collisions with other entities (*trace)( tr, entityOrigin, mins, maxs, targetOrigin, ps->clientNum, MASK_DEADSOLID ); VectorCopy( tr->endpos, entityOrigin ); VectorMA( entityOrigin, 0.1f, playerNormal, outOrigin ); diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 68217cf0..ca87da50 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -433,10 +433,9 @@ typedef enum BIT_NUM_TEAMS } buildableTeam_t; -#define B_HEALTH_BITS 11 +#define B_HEALTH_BITS 12 #define B_HEALTH_MASK ((1<<B_HEALTH_BITS)-1) -#define B_REMOVED_TOGGLEBIT 0x00000800 #define B_MARKED_TOGGLEBIT 0x00001000 #define B_SPAWNED_TOGGLEBIT 0x00002000 #define B_POWERED_TOGGLEBIT 0x00004000 diff --git a/src/game/g_active.c b/src/game/g_active.c index ce60daff..17f66352 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -513,6 +513,7 @@ void ClientTimerActions( gentity_t *ent, int msec ) gclient_t *client; usercmd_t *ucmd; int aForward, aRight; + int i; ucmd = &ent->client->pers.cmd; @@ -675,6 +676,20 @@ void ClientTimerActions( gentity_t *ent, int msec ) client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT; else client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT; + + // Let the client know which buildables will be removed by building + for( i = 0; i < MAX_MISC; i++ ) + { + if( i < level.numBuildablesForRemoval ) + client->ps.misc[ i ] = level.markedBuildables[ i ]->s.number; + else + client->ps.misc[ i ] = 0; + } + } + else + { + for( i = 0; i < MAX_MISC; i++ ) + client->ps.misc[ i ] = 0; } //update build timer diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 22a37455..2b9fa394 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -2462,15 +2462,6 @@ void G_BuildableThink( gentity_t *ent, int msec ) if( ent->deconstruct ) ent->s.generic1 |= B_MARKED_TOGGLEBIT; - for( i = 0; i < level.numBuildablesForRemoval; i++ ) - { - if( ent->s.number == level.markedBuildables[ i ]->s.number ) - { - ent->s.generic1 |= B_REMOVED_TOGGLEBIT; - break; - } - } - ent->time1000 += msec; if( ent->time1000 >= 1000 ) @@ -2687,18 +2678,22 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, qboolean collision = qfalse; int collisionCount = 0; itemBuildError_t bpError; + buildable_t spawn; + int spawnCount = 0; if( team == BIT_ALIENS ) { remainingBP = level.alienBuildPoints; remainingSpawns = level.numAlienSpawns; bpError = IBE_NOASSERT; + spawn = BA_A_SPAWN; } else if( team == BIT_HUMANS ) { remainingBP = level.humanBuildPoints; remainingSpawns = level.numHumanSpawns; bpError = IBE_NOPOWER; + spawn = BA_H_SPAWN; } else Com_Error( ERR_FATAL, "team is %d\n", team ); @@ -2708,8 +2703,18 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, { if( remainingBP - buildPoints < 0 ) return bpError; - else - return IBE_NONE; + + // Check for buildable<->buildable collisions + for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( G_BuildablesIntersect( buildable, origin, ent->s.modelindex, ent->s.origin ) ) + return IBE_NOROOM; + } + + return IBE_NONE; } // Set buildPoints to the number extra that are required @@ -2720,9 +2725,25 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, // Build a list of buildable entities for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { - if( ( collision = G_BuildablesIntersect( buildable, origin, - ent->s.modelindex, ent->s.origin ) ) ) + if( ent->s.eType != ET_BUILDABLE ) + continue; + + collision = G_BuildablesIntersect( buildable, origin, ent->s.modelindex, ent->s.origin ); + + if( collision ) + { + // Don't allow replacements at all + if( g_markDeconstruct.integer == 1 ) + return IBE_NOROOM; + + // Only allow replacements of the same type + if( g_markDeconstruct.integer == 2 && ent->s.modelindex != buildable ) + return IBE_NOROOM; + + // Any other setting means anything goes + collisionCount++; + } if( !ent->inuse ) continue; @@ -2737,13 +2758,6 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, if( ent->biteam != team ) continue; - // Prevent destruction of the last spawn - if( remainingSpawns <= 1 ) - { - if( ent->s.modelindex == BA_A_SPAWN || ent->s.modelindex == BA_H_SPAWN ) - continue; - } - // If it's a unique buildable, it can only be replaced by the same type if( BG_FindUniqueTestForBuildable( ent->s.modelindex ) && ent->s.modelindex != buildable ) @@ -2753,15 +2767,20 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, { level.markedBuildables[ numBuildables++ ] = ent; - // Collided with something, so we definitely have to remove it - // It will always end up at the front of the removal list, so - // incrementing numBuildablesForRemoval is sufficient if( collision ) { + // Collided with something, so we definitely have to remove it + // It will always end up at the front of the removal list, so + // incrementing numBuildablesForRemoval is sufficient collisionCount--; pointsYielded += BG_FindBuildPointsForBuildable( ent->s.modelindex ); level.numBuildablesForRemoval++; } + else if( BG_FindUniqueTestForBuildable( ent->s.modelindex ) ) + { + pointsYielded += BG_FindBuildPointsForBuildable( ent->s.modelindex ); + level.numBuildablesForRemoval++; + } } } @@ -2787,15 +2806,44 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, pointsYielded += BG_FindBuildPointsForBuildable( ent->s.modelindex ); } + for( i = 0; i < level.numBuildablesForRemoval; i++ ) + { + if( level.markedBuildables[ i ]->s.modelindex == spawn ) + spawnCount++; + } + + // Make sure we're not removing the last spawn + if( ( remainingSpawns - spawnCount ) < 1 ) + return bpError; + // Not enough points yielded if( pointsYielded < buildPoints ) - { - level.numBuildablesForRemoval = 0; return bpError; - } else - { return IBE_NONE; +} + +/* +================ +G_SetBuildableLinkState + +Links or unlinks all the buildable entities +================ +*/ +static void G_SetBuildableLinkState( qboolean link ) +{ + int i; + gentity_t *ent; + + for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( link ) + trap_LinkEntity( ent ); + else + trap_UnlinkEntity( ent ); } } @@ -2821,12 +2869,14 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance playerState_t *ps = &ent->client->ps; int buildPoints; + // Stop all buildables from interacting with traces + G_SetBuildableLinkState( qfalse ); + BG_FindBBoxForBuildable( buildable, mins, maxs ); BG_PositionBuildableRelativeToPlayer( ps, mins, maxs, trap_Trace, entity_origin, angles, &tr1 ); - - trap_Trace( &tr2, entity_origin, mins, maxs, entity_origin, ent->s.number, MASK_DEADSOLID ); - trap_Trace( &tr3, ps->origin, NULL, NULL, entity_origin, ent->s.number, MASK_DEADSOLID ); + trap_Trace( &tr2, entity_origin, mins, maxs, entity_origin, ent->s.number, MASK_PLAYERSOLID ); + trap_Trace( &tr3, ps->origin, NULL, NULL, entity_origin, ent->s.number, MASK_PLAYERSOLID ); VectorCopy( entity_origin, origin ); @@ -2997,7 +3047,13 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance //this item does not fit here if( reason == IBE_NONE && ( tr2.fraction < 1.0 || tr3.fraction < 1.0 ) ) - return IBE_NOROOM; + reason = IBE_NOROOM; + + if( reason != IBE_NONE ) + level.numBuildablesForRemoval = 0; + + // Relink buildables + G_SetBuildableLinkState( qtrue ); return reason; } diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index 9d015e8b..cfc7cc1f 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -42,8 +42,8 @@ void G_ForceWeaponChange( gentity_t *ent, weapon_t weapon ) { ent->client->ps.pm_flags |= PMF_WEAPON_SWITCH; - if( weapon == WP_NONE - || !BG_InventoryContainsWeapon( weapon, ent->client->ps.stats )) + if( weapon == WP_NONE || + !BG_InventoryContainsWeapon( weapon, ent->client->ps.stats )) { //switch to the first non blaster weapon for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ ) @@ -64,8 +64,8 @@ void G_ForceWeaponChange( gentity_t *ent, weapon_t weapon ) } else ent->client->ps.persistant[ PERS_NEWWEAPON ] = weapon; - - // force this here to prevent flamer effect from continuing + + // force this here to prevent flamer effect from continuing ent->client->ps.generic1 = WPM_NOTFIRING; ent->client->ps.weapon = ent->client->ps.persistant[ PERS_NEWWEAPON ]; |