summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cgame/cg_buildable.c55
-rw-r--r--src/cgame/cg_servercmds.c12
-rw-r--r--src/cgame/cg_tutorial.c26
-rw-r--r--src/game/bg_misc.c1
-rw-r--r--src/game/bg_public.h3
-rw-r--r--src/game/g_active.c15
-rw-r--r--src/game/g_buildable.c118
-rw-r--r--src/game/g_weapon.c8
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 = &cent->currentState;
+ cent = &cg_entities[ cg.snap->entities[ i ].number ];
+ es = &cent->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 ];