summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author/dev/humancontroller <devhc@example.com>2014-07-13 18:12:01 +0200
committer/dev/humancontroller <devhc@example.com>2017-03-09 13:51:11 +0100
commit10121326fbf07b701038893ecdb19f672c29c11e (patch)
treef14b006721b4835f7dc04a3ef3ed75b73556d621
parent8391e8997fad1bdbd6b9d189c6afca22d3e4324c (diff)
implement the range marker functionality, use it to create buildable range markers
use the entity 64-bit client-masking extension when available
-rw-r--r--src/cgame/cg_buildable.c70
-rw-r--r--src/cgame/cg_draw.c62
-rw-r--r--src/cgame/cg_drawtools.c158
-rw-r--r--src/cgame/cg_ents.c26
-rw-r--r--src/cgame/cg_local.h71
-rw-r--r--src/cgame/cg_main.c185
-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
14 files changed, 742 insertions, 2 deletions
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c
index ca170fd6..b859732e 100644
--- a/src/cgame/cg_buildable.c
+++ b/src/cgame/cg_buildable.c
@@ -392,6 +392,42 @@ void CG_InitBuildables( void )
}
/*
+================
+CG_BuildableRangeMarkerProperties
+================
+*/
+qboolean CG_GetBuildableRangeMarkerProperties( buildable_t bType, rangeMarkerType_t *rmType, float *range, vec3_t rgb )
+{
+ shaderColorEnum_t shc;
+
+ switch( bType )
+ {
+ case BA_A_SPAWN: *range = CREEP_BASESIZE; shc = SHC_LIGHT_GREEN; break;
+ case BA_A_OVERMIND: *range = CREEP_BASESIZE; shc = SHC_DARK_GREEN; break;
+ case BA_A_ACIDTUBE: *range = ACIDTUBE_RANGE; shc = SHC_RED; break;
+ case BA_A_TRAPPER: *range = TRAPPER_RANGE; shc = SHC_PINK; break;
+ case BA_A_HIVE: *range = HIVE_SENSE_RANGE; shc = SHC_YELLOW; break;
+ case BA_H_MGTURRET: *range = MGTURRET_RANGE; shc = SHC_ORANGE; break;
+ case BA_H_TESLAGEN: *range = TESLAGEN_RANGE; shc = SHC_VIOLET; break;
+ case BA_H_DCC: *range = DC_RANGE; shc = SHC_GREEN_CYAN; break;
+ case BA_H_REACTOR: *range = REACTOR_BASESIZE; shc = SHC_DARK_BLUE; break;
+ case BA_H_REPEATER: *range = REPEATER_BASESIZE; shc = SHC_LIGHT_BLUE; break;
+ default: return qfalse;
+ }
+
+ if( bType == BA_A_TRAPPER )
+ *rmType = RMT_SPHERICAL_CONE_64;
+ else if( bType == BA_H_MGTURRET )
+ *rmType = RMT_SPHERICAL_CONE_240;
+ else
+ *rmType = RMT_SPHERE;
+
+ VectorCopy( cg_shaderColors[ shc ], rgb );
+
+ return qtrue;
+}
+
+/*
===============
CG_SetBuildableLerpFrameAnimation
@@ -597,6 +633,37 @@ static void CG_PositionAndOrientateBuildable( const vec3_t angles, const vec3_t
}
/*
+================
+CG_GhostBuildableRangeMarker
+================
+*/
+static void CG_GhostBuildableRangeMarker( buildable_t buildable, const vec3_t origin, const vec3_t normal )
+{
+ qboolean drawS, drawI, drawF;
+ float so, lo, th;
+ rangeMarkerType_t rmType;
+ float range;
+ vec3_t rgb;
+
+ if( CG_GetRangeMarkerPreferences( &drawS, &drawI, &drawF, &so, &lo, &th ) &&
+ CG_GetBuildableRangeMarkerProperties( buildable, &rmType, &range, rgb ) )
+ {
+ vec3_t localOrigin, angles;
+
+ if( buildable == BA_A_HIVE || buildable == BA_H_TESLAGEN )
+ VectorMA( origin, BG_BuildableConfig( buildable )->maxs[ 2 ], normal, localOrigin );
+ else
+ VectorCopy( origin, localOrigin );
+
+ if( rmType != RMT_SPHERE )
+ vectoangles( normal, angles );
+
+ CG_DrawRangeMarker( rmType, localOrigin, ( rmType != RMT_SPHERE ? angles : NULL ),
+ range, drawS, drawI, drawF, rgb, so, lo, th );
+ }
+}
+
+/*
==================
CG_GhostBuildable
==================
@@ -618,6 +685,9 @@ void CG_GhostBuildable( buildable_t buildable )
BG_PositionBuildableRelativeToPlayer( ps, mins, maxs, CG_Trace, entity_origin, angles, &tr );
+ if( cg_rangeMarkerForBlueprint.integer && tr.entityNum != ENTITYNUM_NONE )
+ CG_GhostBuildableRangeMarker( buildable, entity_origin, tr.plane.normal );
+
CG_PositionAndOrientateBuildable( ps->viewangles, entity_origin, tr.plane.normal, ps->clientNum,
mins, maxs, ent.axis, ent.origin );
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index f59c11aa..c334490d 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -3590,6 +3590,66 @@ void CG_ResetPainBlend( void )
}
/*
+================
+CG_DrawBinaryShadersFinalPhases
+================
+*/
+static void CG_DrawBinaryShadersFinalPhases( void )
+{
+ float ss, f, l, u;
+ polyVert_t verts[ 4 ] = {
+ { { 0, 0, 0 }, { 0, 0 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 1, 0 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 1, 1 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 0, 1 }, { 255, 255, 255, 255 } }
+ };
+ int i, j, k;
+
+ if( !cg.numBinaryShadersUsed )
+ return;
+
+ ss = cg_binaryShaderScreenScale.value;
+ if( ss <= 0.0f )
+ {
+ cg.numBinaryShadersUsed = 0;
+ return;
+ }
+ else if( ss > 1.0f )
+ ss = 1.0f;
+
+ ss = sqrt( ss );
+
+ f = 1.01f; // FIXME: is this a good choice to avoid near-clipping?
+ l = f * tan( DEG2RAD( cg.refdef.fov_x / 2 ) ) * ss;
+ u = f * tan( DEG2RAD( cg.refdef.fov_y / 2 ) ) * ss;
+
+ VectorMA( cg.refdef.vieworg, f, cg.refdef.viewaxis[ 0 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, l, cg.refdef.viewaxis[ 1 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, u, cg.refdef.viewaxis[ 2 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, -2*l, cg.refdef.viewaxis[ 1 ], verts[ 1 ].xyz );
+ VectorMA( verts[ 1 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 2 ].xyz );
+ VectorMA( verts[ 0 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 3 ].xyz );
+
+ trap_R_AddPolyToScene( cgs.media.binaryAlpha1Shader, 4, verts );
+
+ for( i = 0; i < cg.numBinaryShadersUsed; ++i )
+ {
+ for( j = 0; j < 4; ++j )
+ {
+ for( k = 0; k < 3; ++k )
+ verts[ j ].modulate[ k ] = cg.binaryShaderSettings[ i ].color[ k ];
+ }
+
+ if( cg.binaryShaderSettings[ i ].drawFrontline )
+ trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].f3, 4, verts );
+ if( cg.binaryShaderSettings[ i ].drawIntersection )
+ trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].b3, 4, verts );
+ }
+
+ cg.numBinaryShadersUsed = 0;
+}
+
+/*
=====================
CG_DrawActive
@@ -3631,6 +3691,8 @@ void CG_DrawActive( stereoFrame_t stereoView )
VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ],
cg.refdef.vieworg );
+ CG_DrawBinaryShadersFinalPhases( );
+
// draw 3D view
trap_R_RenderScene( &cg.refdef );
diff --git a/src/cgame/cg_drawtools.c b/src/cgame/cg_drawtools.c
index f9d7999b..a47d39d7 100644
--- a/src/cgame/cg_drawtools.c
+++ b/src/cgame/cg_drawtools.c
@@ -437,3 +437,161 @@ char CG_GetColorCharForHealth( int clientnum )
health_char = '3';
return health_char;
}
+
+/*
+================
+CG_DrawSphere
+================
+*/
+void CG_DrawSphere( const vec3_t center, float radius, int customShader, const float *shaderRGBA )
+{
+ refEntity_t re;
+ memset( &re, 0, sizeof( re ) );
+
+ re.reType = RT_MODEL;
+ re.hModel = cgs.media.sphereModel;
+ re.customShader = customShader;
+ re.renderfx = RF_NOSHADOW;
+ if( shaderRGBA != NULL )
+ {
+ int i;
+ for( i = 0; i < 4; ++i )
+ re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ];
+ }
+
+ VectorCopy( center, re.origin );
+
+ radius *= 0.01f;
+ VectorSet( re.axis[ 0 ], radius, 0, 0 );
+ VectorSet( re.axis[ 1 ], 0, radius, 0 );
+ VectorSet( re.axis[ 2 ], 0, 0, radius );
+ re.nonNormalizedAxes = qtrue;
+
+ trap_R_AddRefEntityToScene( &re );
+}
+
+/*
+================
+CG_DrawSphericalCone
+================
+*/
+void CG_DrawSphericalCone( const vec3_t tip, const vec3_t rotation, float radius,
+ qboolean a240, int customShader, const float *shaderRGBA )
+{
+ refEntity_t re;
+ memset( &re, 0, sizeof( re ) );
+
+ re.reType = RT_MODEL;
+ re.hModel = a240 ? cgs.media.sphericalCone240Model : cgs.media.sphericalCone64Model;
+ re.customShader = customShader;
+ re.renderfx = RF_NOSHADOW;
+ if( shaderRGBA != NULL )
+ {
+ int i;
+ for( i = 0; i < 4; ++i )
+ re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ];
+ }
+
+ VectorCopy( tip, re.origin );
+
+ radius *= 0.01f;
+ AnglesToAxis( rotation, re.axis );
+ VectorScale( re.axis[ 0 ], radius, re.axis[ 0 ] );
+ VectorScale( re.axis[ 1 ], radius, re.axis[ 1 ] );
+ VectorScale( re.axis[ 2 ], radius, re.axis[ 2 ] );
+ re.nonNormalizedAxes = qtrue;
+
+ trap_R_AddRefEntityToScene( &re );
+}
+
+/*
+================
+CG_DrawRangeMarker
+================
+*/
+void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range,
+ qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline,
+ const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness )
+{
+ if( drawSurface )
+ {
+ qhandle_t pcsh;
+ vec4_t rgba;
+
+ pcsh = cgs.media.plainColorShader;
+ VectorCopy( rgb, rgba );
+ rgba[ 3 ] = surfaceOpacity;
+
+ switch( rmType )
+ {
+ case RMT_SPHERE:
+ CG_DrawSphere( origin, range, pcsh, rgba );
+ break;
+ case RMT_SPHERICAL_CONE_64:
+ CG_DrawSphericalCone( origin, angles, range, qfalse, pcsh, rgba );
+ break;
+ case RMT_SPHERICAL_CONE_240:
+ CG_DrawSphericalCone( origin, angles, range, qtrue, pcsh, rgba );
+ break;
+ }
+ }
+
+ if( drawIntersection || drawFrontline )
+ {
+ const cgMediaBinaryShader_t *mbsh;
+ cgBinaryShaderSetting_t *bshs;
+ int i;
+
+ if( cg.numBinaryShadersUsed >= NUM_BINARY_SHADERS )
+ return;
+ mbsh = &cgs.media.binaryShaders[ cg.numBinaryShadersUsed ];
+
+ if( rmType == RMT_SPHERE )
+ {
+ if( range > lineThickness / 2 )
+ {
+ if( drawIntersection )
+ CG_DrawSphere( origin, range - lineThickness / 2, mbsh->b1, NULL );
+ CG_DrawSphere( origin, range - lineThickness / 2, mbsh->f2, NULL );
+ }
+
+ if( drawIntersection )
+ CG_DrawSphere( origin, range + lineThickness / 2, mbsh->b2, NULL );
+ CG_DrawSphere( origin, range + lineThickness / 2, mbsh->f1, NULL );
+ }
+ else if( rmType == RMT_SPHERICAL_CONE_64 || rmType == RMT_SPHERICAL_CONE_240 )
+ {
+ qboolean a240;
+ float f, r;
+ vec3_t forward, tip;
+
+ a240 = ( rmType == RMT_SPHERICAL_CONE_240 );
+ f = lineThickness * ( a240 ? 0.26f : 0.8f );
+ r = f + lineThickness * ( a240 ? 0.23f : 0.43f );
+ AngleVectors( angles, forward, NULL, NULL );
+
+ if( range > r )
+ {
+ VectorMA( origin, f, forward, tip );
+ if( drawIntersection )
+ CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->b1, NULL );
+ CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->f2, NULL );
+ }
+
+ VectorMA( origin, -f, forward, tip );
+ if( drawIntersection )
+ CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->b2, NULL );
+ CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->f1, NULL );
+ }
+
+ bshs = &cg.binaryShaderSettings[ cg.numBinaryShadersUsed ];
+
+ for( i = 0; i < 3; ++i )
+ bshs->color[ i ] = 255 * lineOpacity * rgb[ i ];
+ bshs->drawIntersection = drawIntersection;
+ bshs->drawFrontline = drawFrontline;
+
+ ++cg.numBinaryShadersUsed;
+ }
+}
+
diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c
index d4e0f342..48545715 100644
--- a/src/cgame/cg_ents.c
+++ b/src/cgame/cg_ents.c
@@ -978,6 +978,28 @@ static void CG_CalcEntityLerpPositions( centity_t *cent )
/*
+================
+CG_RangeMarker
+================
+*/
+void CG_RangeMarker( centity_t *cent )
+{
+ qboolean drawS, drawI, drawF;
+ float so, lo, th;
+ rangeMarkerType_t rmType;
+ float range;
+ vec3_t rgb;
+
+ if( CG_GetRangeMarkerPreferences( &drawS, &drawI, &drawF, &so, &lo, &th ) &&
+ CG_GetBuildableRangeMarkerProperties( cent->currentState.modelindex, &rmType, &range, rgb ) )
+ {
+ CG_DrawRangeMarker( rmType, cent->lerpOrigin, ( rmType > 0 ? cent->lerpAngles : NULL ),
+ range, drawS, drawI, drawF, rgb, so, lo, th );
+ }
+}
+
+
+/*
===============
CG_CEntityPVSEnter
@@ -1089,6 +1111,10 @@ static void CG_AddCEntity( centity_t *cent )
CG_Buildable( cent );
break;
+ case ET_RANGE_MARKER:
+ CG_RangeMarker( cent );
+ break;
+
case ET_MISSILE:
CG_Missile( cent );
break;
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 404351f5..5e4fde09 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -922,6 +922,15 @@ typedef struct
typedef struct
{
+ byte color[ 3 ];
+ qboolean drawIntersection;
+ qboolean drawFrontline;
+} cgBinaryShaderSetting_t;
+
+#define NUM_BINARY_SHADERS 256
+
+typedef struct
+{
int clientFrame; // incremented each frame
int clientNum;
@@ -1156,6 +1165,9 @@ typedef struct
int nearUsableBuildable;
int nextWeaponClickTime;
+
+ int numBinaryShadersUsed;
+ cgBinaryShaderSetting_t binaryShaderSettings[ NUM_BINARY_SHADERS ];
} cg_t;
@@ -1163,6 +1175,17 @@ typedef struct
// loaded at gamestate time are stored in cgMedia_t
// Other media that can be tied to clients, weapons, or items are
// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t
+
+typedef struct
+{
+ qhandle_t f1;
+ qhandle_t f2;
+ qhandle_t f3;
+ qhandle_t b1;
+ qhandle_t b2;
+ qhandle_t b3;
+} cgMediaBinaryShader_t;
+
typedef struct
{
qhandle_t charsetShader;
@@ -1197,6 +1220,14 @@ typedef struct
qhandle_t redBuildShader;
qhandle_t humanSpawningShader;
+ qhandle_t sphereModel;
+ qhandle_t sphericalCone64Model;
+ qhandle_t sphericalCone240Model;
+
+ qhandle_t plainColorShader;
+ qhandle_t binaryAlpha1Shader;
+ cgMediaBinaryShader_t binaryShaders[ NUM_BINARY_SHADERS ];
+
// disconnect
qhandle_t disconnectPS;
qhandle_t disconnectSound;
@@ -1411,6 +1442,22 @@ typedef struct
void ( *function )( void );
} consoleCommand_t;
+typedef enum
+{
+ SHC_DARK_BLUE,
+ SHC_LIGHT_BLUE,
+ SHC_GREEN_CYAN,
+ SHC_VIOLET,
+ SHC_YELLOW,
+ SHC_ORANGE,
+ SHC_LIGHT_GREEN,
+ SHC_DARK_GREEN,
+ SHC_RED,
+ SHC_PINK,
+ SHC_GREY,
+ SHC_NUM_SHADER_COLORS
+} shaderColorEnum_t;
+
//==============================================================================
extern cgs_t cgs;
@@ -1423,6 +1470,8 @@ extern upgradeInfo_t cg_upgrades[ 32 ];
extern buildableInfo_t cg_buildables[ BA_NUM_BUILDABLES ];
+extern const vec3_t cg_shaderColors[ SHC_NUM_SHADER_COLORS ];
+
extern markPoly_t cg_markPolys[ MAX_MARK_POLYS ];
extern vmCvar_t cg_teslaTrailTime;
@@ -1509,6 +1558,16 @@ extern vmCvar_t cg_disableCommandDialogs;
extern vmCvar_t cg_disableScannerPlane;
extern vmCvar_t cg_tutorial;
+extern vmCvar_t cg_rangeMarkerDrawSurface;
+extern vmCvar_t cg_rangeMarkerDrawIntersection;
+extern vmCvar_t cg_rangeMarkerDrawFrontline;
+extern vmCvar_t cg_rangeMarkerSurfaceOpacity;
+extern vmCvar_t cg_rangeMarkerLineOpacity;
+extern vmCvar_t cg_rangeMarkerLineThickness;
+extern vmCvar_t cg_rangeMarkerForBlueprint;
+extern vmCvar_t cg_rangeMarkerBuildableTypes;
+extern vmCvar_t cg_binaryShaderScreenScale;
+
extern vmCvar_t cg_painBlendUpRate;
extern vmCvar_t cg_painBlendDownRate;
extern vmCvar_t cg_painBlendMax;
@@ -1567,6 +1626,10 @@ void CG_BuildSpectatorString( void );
qboolean CG_FileExists( char *filename );
void CG_RemoveNotifyLine( void );
void CG_AddNotifyText( void );
+qboolean CG_GetRangeMarkerPreferences( qboolean *drawSurface, qboolean *drawIntersection,
+ qboolean *drawFrontline, float *surfaceOpacity,
+ float *lineOpacity, float *lineThickness );
+void CG_UpdateBuildableRangeMarkerMask( void );
//
@@ -1609,6 +1672,12 @@ void CG_DrawTopBottom(float x, float y, float w, float h, float size);
qboolean CG_WorldToScreen( vec3_t point, float *x, float *y );
char *CG_KeyBinding( const char *bind );
char CG_GetColorCharForHealth( int clientnum );
+void CG_DrawSphere( const vec3_t center, float radius, int customShader, const float *shaderRGBA );
+void CG_DrawSphericalCone( const vec3_t tip, const vec3_t rotation, float radius,
+ qboolean a240, int customShader, const float *shaderRGBA );
+void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range,
+ qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline,
+ const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness );
//
// cg_draw.c
@@ -1659,6 +1728,7 @@ void CG_DrawBuildableStatus( void );
void CG_InitBuildables( void );
void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir );
void CG_AlienBuildableExplosion( vec3_t origin, vec3_t dir );
+qboolean CG_GetBuildableRangeMarkerProperties( buildable_t bType, rangeMarkerType_t *rmType, float *range, vec3_t rgb );
//
// cg_animation.c
@@ -1709,6 +1779,7 @@ void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *pare
qhandle_t parentModel, char *tagName );
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
qhandle_t parentModel, char *tagName );
+void CG_RangeMarker( centity_t *cent );
//
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index 25935b8b..4b7b1758 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -191,6 +191,16 @@ vmCvar_t cg_disableCommandDialogs;
vmCvar_t cg_disableScannerPlane;
vmCvar_t cg_tutorial;
+vmCvar_t cg_rangeMarkerDrawSurface;
+vmCvar_t cg_rangeMarkerDrawIntersection;
+vmCvar_t cg_rangeMarkerDrawFrontline;
+vmCvar_t cg_rangeMarkerSurfaceOpacity;
+vmCvar_t cg_rangeMarkerLineOpacity;
+vmCvar_t cg_rangeMarkerLineThickness;
+vmCvar_t cg_rangeMarkerForBlueprint;
+vmCvar_t cg_rangeMarkerBuildableTypes;
+vmCvar_t cg_binaryShaderScreenScale;
+
vmCvar_t cg_painBlendUpRate;
vmCvar_t cg_painBlendDownRate;
vmCvar_t cg_painBlendMax;
@@ -308,6 +318,18 @@ static cvarTable_t cvarTable[ ] =
{ &cg_disableCommandDialogs, "cg_disableCommandDialogs", "0", CVAR_ARCHIVE },
{ &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE },
{ &cg_tutorial, "cg_tutorial", "1", CVAR_ARCHIVE },
+
+ { &cg_rangeMarkerDrawSurface, "cg_rangeMarkerDrawSurface", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerDrawIntersection, "cg_rangeMarkerDrawIntersection", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerDrawFrontline, "cg_rangeMarkerDrawFrontline", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerSurfaceOpacity, "cg_rangeMarkerSurfaceOpacity", "0.08", CVAR_ARCHIVE },
+ { &cg_rangeMarkerLineOpacity, "cg_rangeMarkerLineOpacity", "0.4", CVAR_ARCHIVE },
+ { &cg_rangeMarkerLineThickness, "cg_rangeMarkerLineThickness", "4.0", CVAR_ARCHIVE },
+ { &cg_rangeMarkerForBlueprint, "cg_rangeMarkerForBlueprint", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerBuildableTypes, "cg_rangeMarkerBuildableTypes", "support", CVAR_ARCHIVE },
+ { NULL, "cg_buildableRangeMarkerMask", "", CVAR_USERINFO },
+ { &cg_binaryShaderScreenScale, "cg_binaryShaderScreenScale", "1.0", CVAR_ARCHIVE },
+
{ &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
{ &cg_hudFilesEnable, "cg_hudFilesEnable", "0", CVAR_ARCHIVE},
{ NULL, "cg_alienConfig", "", CVAR_ARCHIVE },
@@ -419,6 +441,106 @@ static void CG_SetUIVars( void )
}
/*
+================
+CG_UpdateBuildableRangeMarkerMask
+================
+*/
+void CG_UpdateBuildableRangeMarkerMask( void )
+{
+ static int mc = 0;
+
+ if( cg_rangeMarkerBuildableTypes.modificationCount != mc )
+ {
+ int brmMask;
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ char *p, *q;
+ buildable_t buildable;
+
+ brmMask = 0;
+
+ if( !cg_rangeMarkerBuildableTypes.string[ 0 ] )
+ goto empty;
+
+ Q_strncpyz( buffer, cg_rangeMarkerBuildableTypes.string, sizeof( buffer ) );
+ p = &buffer[ 0 ];
+
+ for(;;)
+ {
+ q = strchr( p, ',' );
+ if( q )
+ *q = '\0';
+
+ while( *p == ' ' )
+ ++p;
+
+ buildable = BG_BuildableByName( p )->number;
+
+ if( buildable != BA_NONE )
+ {
+ brmMask |= 1 << buildable;
+ }
+ else if( !Q_stricmp( p, "all" ) )
+ {
+ brmMask |= ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE ) |
+ ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN );
+ }
+ else
+ {
+ char *pp;
+ int only;
+
+ if( !Q_stricmpn( p, "alien", 5 ) )
+ {
+ pp = p + 5;
+ only = ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE );
+ }
+ else if( !Q_stricmpn( p, "human", 5 ) )
+ {
+ pp = p + 5;
+ only = ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN );
+ }
+ else
+ {
+ pp = p;
+ only = ~0;
+ }
+
+ if( pp != p && !*pp )
+ {
+ brmMask |= only;
+ }
+ else if( !Q_stricmp( pp, "support" ) )
+ {
+ brmMask |= only & ( ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) );
+ }
+ else if( !Q_stricmp( pp, "offensive" ) )
+ {
+ brmMask |= only & ( ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN ) );
+ }
+ else
+ Com_Printf( S_COLOR_YELLOW "WARNING: unknown buildable or group: %s\n", p );
+ }
+
+ if( q )
+ p = q + 1;
+ else
+ break;
+ }
+
+ empty:
+ trap_Cvar_Set( "cg_buildableRangeMarkerMask", va( "%i", brmMask ) );
+
+ mc = cg_rangeMarkerBuildableTypes.modificationCount;
+ }
+}
+
+/*
=================
CG_UpdateCvars
=================
@@ -435,7 +557,7 @@ void CG_UpdateCvars( void )
// check for modications here
CG_SetUIVars( );
-
+ CG_UpdateBuildableRangeMarkerMask();
}
@@ -811,6 +933,22 @@ static void CG_RegisterGraphics( void )
cgs.media.alienBleedPS = CG_RegisterParticleSystem( "alienBleedPS" );
cgs.media.humanBleedPS = CG_RegisterParticleSystem( "humanBleedPS" );
+ cgs.media.sphereModel = trap_R_RegisterModel( "models/generic/sphere" );
+ cgs.media.sphericalCone64Model = trap_R_RegisterModel( "models/generic/sphericalCone64" );
+ cgs.media.sphericalCone240Model = trap_R_RegisterModel( "models/generic/sphericalCone240" );
+
+ cgs.media.plainColorShader = trap_R_RegisterShader( "gfx/plainColor" );
+ cgs.media.binaryAlpha1Shader = trap_R_RegisterShader( "gfx/binary/alpha1" );
+ for( i = 0; i < NUM_BINARY_SHADERS; ++i )
+ {
+ cgs.media.binaryShaders[ i ].f1 = trap_R_RegisterShader( va( "gfx/binary/%03i_F1", i ) );
+ cgs.media.binaryShaders[ i ].f2 = trap_R_RegisterShader( va( "gfx/binary/%03i_F2", i ) );
+ cgs.media.binaryShaders[ i ].f3 = trap_R_RegisterShader( va( "gfx/binary/%03i_F3", i ) );
+ cgs.media.binaryShaders[ i ].b1 = trap_R_RegisterShader( va( "gfx/binary/%03i_B1", i ) );
+ cgs.media.binaryShaders[ i ].b2 = trap_R_RegisterShader( va( "gfx/binary/%03i_B2", i ) );
+ cgs.media.binaryShaders[ i ].b3 = trap_R_RegisterShader( va( "gfx/binary/%03i_B3", i ) );
+ }
+
CG_BuildableStatusParse( "ui/assets/human/buildstat.cfg", &cgs.humanBuildStat );
CG_BuildableStatusParse( "ui/assets/alien/buildstat.cfg", &cgs.alienBuildStat );
@@ -1911,3 +2049,48 @@ static char *CG_VoIPString( void )
return voipString;
}
+const vec3_t cg_shaderColors[ SHC_NUM_SHADER_COLORS ] =
+{
+ { 0.0f, 0.0f, 0.75f }, // dark blue
+ { 0.3f, 0.35f, 0.625f }, // light blue
+ { 0.0f, 0.625f, 0.563f }, // green-cyan
+ { 0.313f, 0.0f, 0.625f }, // violet
+ { 0.625f, 0.625f, 0.0f }, // yellow
+ { 0.875f, 0.313f, 0.0f }, // orange
+ { 0.375f, 0.625f, 0.375f }, // light green
+ { 0.0f, 0.438f, 0.0f }, // dark green
+ { 1.0f, 0.0f, 0.0f }, // red
+ { 0.625f, 0.375f, 0.4f }, // pink
+ { 0.313f, 0.313f, 0.313f } // grey
+};
+
+/*
+================
+CG_RangeMarkerPreferences
+================
+*/
+qboolean CG_GetRangeMarkerPreferences( qboolean *drawSurface, qboolean *drawIntersection,
+ qboolean *drawFrontline, float *surfaceOpacity,
+ float *lineOpacity, float *lineThickness )
+{
+ *drawSurface = !!cg_rangeMarkerDrawSurface.integer;
+ *drawIntersection = !!cg_rangeMarkerDrawIntersection.integer;
+ *drawFrontline = !!cg_rangeMarkerDrawFrontline.integer;
+ *surfaceOpacity = cg_rangeMarkerSurfaceOpacity.value;
+ *lineOpacity = cg_rangeMarkerLineOpacity.value;
+ *lineThickness = cg_rangeMarkerLineThickness.value;
+
+ if( ( *drawSurface && *surfaceOpacity > 0.0f ) ||
+ ( ( *drawIntersection || *drawFrontline ) && *lineOpacity > 0.0f &&
+ *lineThickness > 0.0f && cg_binaryShaderScreenScale.value > 0.0f ) )
+ {
+ if( *surfaceOpacity > 1.0f )
+ *surfaceOpacity = 1.0f;
+ if( *lineOpacity > 1.0f )
+ *lineOpacity = 1.0f;
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
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;