diff options
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 |
commit | 10121326fbf07b701038893ecdb19f672c29c11e (patch) | |
tree | f14b006721b4835f7dc04a3ef3ed75b73556d621 | |
parent | 8391e8997fad1bdbd6b9d189c6afca22d3e4324c (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.c | 70 | ||||
-rw-r--r-- | src/cgame/cg_draw.c | 62 | ||||
-rw-r--r-- | src/cgame/cg_drawtools.c | 158 | ||||
-rw-r--r-- | src/cgame/cg_ents.c | 26 | ||||
-rw-r--r-- | src/cgame/cg_local.h | 71 | ||||
-rw-r--r-- | src/cgame/cg_main.c | 185 | ||||
-rw-r--r-- | src/game/bg_public.h | 10 | ||||
-rw-r--r-- | src/game/g_buildable.c | 144 | ||||
-rw-r--r-- | src/game/g_client.c | 3 | ||||
-rw-r--r-- | src/game/g_cmds.c | 1 | ||||
-rw-r--r-- | src/game/g_local.h | 4 | ||||
-rw-r--r-- | src/game/g_main.c | 5 | ||||
-rw-r--r-- | src/game/g_physics.c | 2 | ||||
-rw-r--r-- | src/game/g_svcmds.c | 3 |
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; |