summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/bg_public.h10
-rw-r--r--src/game/g_buildable.c144
-rw-r--r--src/game/g_client.c3
-rw-r--r--src/game/g_cmds.c1
-rw-r--r--src/game/g_local.h4
-rw-r--r--src/game/g_main.c5
-rw-r--r--src/game/g_physics.c2
-rw-r--r--src/game/g_svcmds.c3
8 files changed, 171 insertions, 1 deletions
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index ba43b1f9..39dd1d80 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -429,6 +429,13 @@ typedef enum
BA_NUM_BUILDABLES
} buildable_t;
+typedef enum
+{
+ RMT_SPHERE,
+ RMT_SPHERICAL_CONE_64,
+ RMT_SPHERICAL_CONE_240
+} rangeMarkerType_t;
+
// entityState_t->event values
// entity events are for effects that take place relative
// to an existing entities origin. Very network efficient.
@@ -1176,7 +1183,8 @@ typedef enum
ET_PLAYER,
ET_ITEM,
- ET_BUILDABLE, // buildable type
+ ET_BUILDABLE,
+ ET_RANGE_MARKER,
ET_LOCATION,
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index 3b237379..34136909 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -789,6 +789,7 @@ void AGeneric_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, i
else
self->nextthink = level.time; //blast immediately
+ G_RemoveRangeMarkerFrom( self );
G_LogDestruction( self, attacker, mod );
}
@@ -1661,6 +1662,7 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->nextthink = level.time; //blast immediately
}
+ G_RemoveRangeMarkerFrom( self );
G_LogDestruction( self, attacker, mod );
}
@@ -1747,6 +1749,7 @@ static void HRepeater_Die( gentity_t *self, gentity_t *inflictor, gentity_t *att
self->nextthink = level.time; //blast immediately
}
+ G_RemoveRangeMarkerFrom( self );
G_LogDestruction( self, attacker, mod );
if( self->usesBuildPointZone )
@@ -3020,6 +3023,7 @@ void G_FreeMarkedBuildables( gentity_t *deconner, char *readable, int rsize,
if( nums )
Q_strcat( nums, nsize, va( " %d", (int)( ent - g_entities ) ) );
+ G_RemoveRangeMarkerFrom( ent );
G_FreeEntity( ent );
}
@@ -3491,6 +3495,55 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
/*
================
+G_AddRangeMarkerForBuildable
+================
+*/
+static void G_AddRangeMarkerForBuildable( gentity_t *self )
+{
+ gentity_t *rm;
+
+ switch( self->s.modelindex )
+ {
+ case BA_A_SPAWN:
+ case BA_A_OVERMIND:
+ case BA_A_ACIDTUBE:
+ case BA_A_TRAPPER:
+ case BA_A_HIVE:
+ case BA_H_MGTURRET:
+ case BA_H_TESLAGEN:
+ case BA_H_DCC:
+ case BA_H_REACTOR:
+ case BA_H_REPEATER:
+ break;
+ default:
+ return;
+ }
+
+ rm = G_Spawn();
+ rm->classname = "buildablerangemarker";
+ rm->r.svFlags = SVF_BROADCAST | SVF_CLIENTMASK;
+ rm->s.eType = ET_RANGE_MARKER;
+ rm->s.modelindex = self->s.modelindex;
+
+ self->rangeMarker = rm;
+}
+
+/*
+================
+G_RemoveRangeMarkerFrom
+================
+*/
+void G_RemoveRangeMarkerFrom( gentity_t *self )
+{
+ if( self->rangeMarker )
+ {
+ G_FreeEntity( self->rangeMarker );
+ self->rangeMarker = NULL;
+ }
+}
+
+/*
+================
G_Build
Spawns a buildable
@@ -3719,6 +3772,8 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
if( log )
G_BuildLogSet( log, built );
+ G_AddRangeMarkerForBuildable( built );
+
return built;
}
@@ -3840,6 +3895,7 @@ static gentity_t *G_FinishSpawningBuildable( gentity_t *ent, qboolean force )
{
G_Printf( S_COLOR_YELLOW "G_FinishSpawningBuildable: %s startsolid at %s\n",
built->classname, vtos( built->s.origin ) );
+ G_RemoveRangeMarkerFrom( built );
G_FreeEntity( built );
return NULL;
}
@@ -4309,6 +4365,7 @@ void G_BuildLogRevert( int id )
if( ent->s.eType == ET_BUILDABLE )
G_LogPrintf( "revert: remove %d %s\n",
(int)( ent - g_entities ), BG_Buildable( ent->s.modelindex )->name );
+ G_RemoveRangeMarkerFrom( ent );
G_FreeEntity( ent );
break;
}
@@ -4370,3 +4427,90 @@ void G_BuildLogRevert( int id )
}
}
+/*
+================
+G_UpdateBuildableRangeMarkers
+================
+*/
+void G_UpdateBuildableRangeMarkers( void )
+{
+ // is the entity 64-bit client-masking extension available?
+ qboolean maskingExtension = ( trap_Cvar_VariableIntegerValue( "sv_gppExtension" ) >= 1 );
+
+ gentity_t *e;
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ buildable_t bType;
+ team_t bTeam;
+ int i;
+
+ if( e->s.eType != ET_BUILDABLE || !e->rangeMarker )
+ continue;
+
+ bType = e->s.modelindex;
+ bTeam = BG_Buildable( bType )->team;
+
+ e->rangeMarker->s.pos = e->s.pos;
+ if( bType == BA_A_HIVE || bType == BA_H_TESLAGEN )
+ VectorMA( e->s.pos.trBase, e->r.maxs[ 2 ], e->s.origin2, e->rangeMarker->s.pos.trBase );
+ else if( bType == BA_A_TRAPPER || bType == BA_H_MGTURRET )
+ vectoangles( e->s.origin2, e->rangeMarker->s.apos.trBase );
+
+ e->rangeMarker->r.singleClient = 0;
+ e->rangeMarker->r.hack.generic1 = 0;
+
+ // remove any previously added NOCLIENT flags from the hack below
+ e->rangeMarker->r.svFlags &= ~SVF_NOCLIENT;
+
+ for( i = 0; i < level.maxclients; ++i )
+ {
+ gclient_t *client;
+ team_t team;
+ qboolean weaponDisplays, wantsToSee;
+
+ client = &level.clients[ i ];
+ if( client->pers.connected != CON_CONNECTED )
+ continue;
+
+ if( i >= 32 && !maskingExtension )
+ {
+ // resort to not sending range markers at all
+ if( !trap_Cvar_VariableIntegerValue( "g_rangeMarkerWarningGiven" ) )
+ {
+ trap_SendServerCommand( -1, "print \"" S_COLOR_YELLOW "WARNING: There is no "
+ "support for entity 64-bit client-masking on this server. Please update "
+ "your server executable. Until then, range markers will not be displayed "
+ "while there are clients with client numbers above 31 in the game.\n\"" );
+ trap_Cvar_Set( "g_rangeMarkerWarningGiven", "1" );
+ }
+
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->s.eType == ET_BUILDABLE && e->rangeMarker )
+ e->rangeMarker->r.svFlags |= SVF_NOCLIENT;
+ }
+
+ return;
+ }
+
+ team = client->pers.teamSelection;
+ if( team != TEAM_NONE )
+ {
+ weaponDisplays = ( BG_InventoryContainsWeapon( WP_HBUILD, client->ps.stats ) ||
+ client->ps.weapon == WP_ABUILD || client->ps.weapon == WP_ABUILD2 );
+ }
+ wantsToSee = !!( client->pers.buildableRangeMarkerMask & ( 1 << bType ) );
+
+ if( ( team == TEAM_NONE || ( team == bTeam && weaponDisplays ) ) && wantsToSee )
+ {
+ if( i >= 32 )
+ e->rangeMarker->r.hack.generic1 |= 1 << ( i - 32 );
+ else
+ e->rangeMarker->r.singleClient |= 1 << i;
+ }
+ }
+
+ trap_LinkEntity( e->rangeMarker );
+ }
+}
+
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 548ec963..61ff80a3 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -969,6 +969,9 @@ char *ClientUserinfoChanged( int clientNum, qboolean forceName )
else
client->pers.disableBlueprintErrors = qfalse;
+ client->pers.buildableRangeMarkerMask =
+ atoi( Info_ValueForKey( userinfo, "cg_buildableRangeMarkerMask" ) );
+
// teamInfo
s = Info_ValueForKey( userinfo, "teamoverlay" );
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index caa5f990..c6ac1b5b 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -1979,6 +1979,7 @@ void Cmd_Destroy_f( gentity_t *ent )
}
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
traceEnt->health, 0, MOD_DECONSTRUCT );
+ G_RemoveRangeMarkerFrom( traceEnt );
G_FreeEntity( traceEnt );
}
}
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 081c47b8..45926560 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -190,6 +190,7 @@ struct gentity_s
team_t buildableTeam; // buildable item team
gentity_t *parentNode; // for creep and defence/spawn dependencies
+ gentity_t *rangeMarker;
qboolean active; // for power repeater, but could be useful elsewhere
qboolean locked; // used for turret tracking
qboolean powered; // for human buildables
@@ -303,6 +304,7 @@ typedef struct
int teamInfo; // level.time of team overlay update (disabled = 0)
float flySpeed; // for spectator/noclip moves
qboolean disableBlueprintErrors; // should the buildable blueprint never be hidden from the players?
+ int buildableRangeMarkerMask;
class_t classSelection; // player class (copied to ent->client->ps.stats[ STAT_CLASS ] once spawned)
float evolveHealthFraction;
@@ -806,6 +808,8 @@ buildLog_t *G_BuildLogNew( gentity_t *actor, buildFate_t fate );
void G_BuildLogSet( buildLog_t *log, gentity_t *ent );
void G_BuildLogAuto( gentity_t *actor, gentity_t *buildable, buildFate_t fate );
void G_BuildLogRevert( int id );
+void G_RemoveRangeMarkerFrom( gentity_t *self );
+void G_UpdateBuildableRangeMarkers( void );
//
// g_utils.c
diff --git a/src/game/g_main.c b/src/game/g_main.c
index b3d36bad..c8e0af41 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -597,6 +597,10 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
// we're done with g_mapConfigs, so reset this for the next map
trap_Cvar_Set( "g_mapConfigsLoaded", "0" );
+ // set this cvar to 0 if it exists, but otherwise avoid its creation
+ if( trap_Cvar_VariableIntegerValue( "g_rangeMarkerWarningGiven" ) )
+ trap_Cvar_Set( "g_rangeMarkerWarningGiven", "0" );
+
G_RegisterCommands( );
G_admin_readconfig( NULL );
G_LoadCensors( );
@@ -2397,6 +2401,7 @@ void G_RunFrame( int levelTime )
G_CalculateAvgPlayers( );
G_UpdateZaps( msec );
}
+ G_UpdateBuildableRangeMarkers( );
// see if it is time to end the level
CheckExitRules( );
diff --git a/src/game/g_physics.c b/src/game/g_physics.c
index 455d3a6e..9e5b23d5 100644
--- a/src/game/g_physics.c
+++ b/src/game/g_physics.c
@@ -153,6 +153,8 @@ void G_Physics( gentity_t *ent, int msec )
contents = trap_PointContents( ent->r.currentOrigin, -1 );
if( contents & CONTENTS_NODROP )
{
+ if( ent->s.eType == ET_BUILDABLE )
+ G_RemoveRangeMarkerFrom( ent );
G_FreeEntity( ent );
return;
}
diff --git a/src/game/g_svcmds.c b/src/game/g_svcmds.c
index ae67e291..262f8744 100644
--- a/src/game/g_svcmds.c
+++ b/src/game/g_svcmds.c
@@ -58,6 +58,9 @@ void Svcmd_EntityList_f( void )
case ET_BUILDABLE:
G_Printf( "ET_BUILDABLE " );
break;
+ case ET_RANGE_MARKER:
+ G_Printf( "ET_RANGE_MARKER " );
+ break;
case ET_LOCATION:
G_Printf( "ET_LOCATION " );
break;