commit 6d7c2feb441d950bc833498bb1571e80f46ba74c Author: devhc Date: Wed Jul 13 00:30:10 2011 +0200 range marker functionality, buildable range markers diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c index 207c6b4..818471f 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, int *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 = 1; + else if( bType == BA_H_MGTURRET ) + *rmType = 2; + else + *rmType = 0; + + 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; + int 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 > 0 ) + vectoangles( normal, angles ); + + CG_DrawRangeMarker( rmType, localOrigin, ( rmType > 0 ? 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 8e1d214..126ad63 100644 --- a/src/cgame/cg_draw.c +++ b/src/cgame/cg_draw.c @@ -3607,6 +3607,65 @@ 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; + + 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 ) + { + VectorCopy( cg.binaryShaderSettings[ i ].color, verts[ 0 ].modulate ); + VectorCopy( cg.binaryShaderSettings[ i ].color, verts[ 1 ].modulate ); + VectorCopy( cg.binaryShaderSettings[ i ].color, verts[ 2 ].modulate ); + VectorCopy( cg.binaryShaderSettings[ i ].color, verts[ 3 ].modulate ); + + 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 @@ -3648,6 +3707,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 3fb721a..2d6c9b6 100644 --- a/src/cgame/cg_drawtools.c +++ b/src/cgame/cg_drawtools.c @@ -433,3 +433,150 @@ 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 ) + { + VectorScale( shaderRGBA, 255, re.shaderRGBA ); + re.shaderRGBA[ 3 ] = 255 * shaderRGBA[ 3 ]; + } + + 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 ) + { + VectorScale( shaderRGBA, 255, re.shaderRGBA ); + re.shaderRGBA[ 3 ] = 255 * shaderRGBA[ 3 ]; + } + + 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( int 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; + + if( rmType == 0 ) + CG_DrawSphere( origin, range, pcsh, rgba ); + else if( rmType == 1 ) + CG_DrawSphericalCone( origin, angles, range, qfalse, pcsh, rgba ); + else if( rmType == 2 ) + CG_DrawSphericalCone( origin, angles, range, qtrue, pcsh, rgba ); + } + + if( drawIntersection || drawFrontline ) + { + const cgMediaBinaryShader_t *mbsh; + cgBinaryShaderSetting_t *bshs; + + if( cg.numBinaryShadersUsed >= NUM_BINARY_SHADERS ) + return; + mbsh = &cgs.media.binaryShaders[ cg.numBinaryShadersUsed ]; + + if( rmType == 0 ) + { + 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 == 1 || rmType == 2 ) + { + qboolean t2; + float f, r; + vec3_t forward, tip; + + t2 = ( rmType == 2 ); + f = lineThickness * ( t2 ? 0.26f : 0.8f ); + r = f + lineThickness * ( t2 ? 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, t2, mbsh->b1, NULL ); + CG_DrawSphericalCone( tip, angles, range - r, t2, mbsh->f2, NULL ); + } + + VectorMA( origin, -f, forward, tip ); + if( drawIntersection ) + CG_DrawSphericalCone( tip, angles, range + r, t2, mbsh->b2, NULL ); + CG_DrawSphericalCone( tip, angles, range + r, t2, mbsh->f1, NULL ); + } + + bshs = &cg.binaryShaderSettings[ cg.numBinaryShadersUsed ]; + VectorScale( rgb, 255 * lineOpacity, bshs->color ); + bshs->drawIntersection = drawIntersection; + bshs->drawFrontline = drawFrontline; + + ++cg.numBinaryShadersUsed; + } +} + diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c index 7b27579..2137966 100644 --- a/src/cgame/cg_ents.c +++ b/src/cgame/cg_ents.c @@ -979,6 +979,28 @@ static void CG_CalcEntityLerpPositions( centity_t *cent ) /* +================ +CG_RangeMarker +================ +*/ +void CG_RangeMarker( centity_t *cent ) +{ + qboolean drawS, drawI, drawF; + float so, lo, th; + int 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 @@ -1090,6 +1112,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 b708731..695597f 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -915,6 +915,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; @@ -1152,6 +1161,9 @@ typedef struct int nearUsableBuildable; int nextWeaponClickTime; + + int numBinaryShadersUsed; + cgBinaryShaderSetting_t binaryShaderSettings[ NUM_BINARY_SHADERS ]; } cg_t; @@ -1159,6 +1171,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; @@ -1193,6 +1216,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; @@ -1408,6 +1439,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; @@ -1420,6 +1467,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; @@ -1506,6 +1555,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; @@ -1563,6 +1622,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 ); // @@ -1605,6 +1668,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( int 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 @@ -1655,6 +1724,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, int *rmType, float *range, vec3_t rgb ); // // cg_animation.c @@ -1705,6 +1775,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 b33dd50..aee3053 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 }, @@ -422,6 +444,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 ================= @@ -438,7 +560,7 @@ void CG_UpdateCvars( void ) // check for modications here CG_SetUIVars( ); - + CG_UpdateBuildableRangeMarkerMask(); } @@ -814,6 +936,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 ); @@ -1916,3 +2054,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 6ba46f6..c12ca12 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -1181,7 +1181,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 d7a5146..b1af50c 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -799,6 +799,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 ); } @@ -1677,6 +1678,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 ); } @@ -1763,6 +1765,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 ) @@ -3035,6 +3038,7 @@ void G_FreeMarkedBuildables( gentity_t *deconner, char *readable, int rsize, if( nums ) Q_strcat( nums, nsize, va( " %lu", (long unsigned)( ent-g_entities ) ) ); + G_RemoveRangeMarkerFrom( ent ); G_FreeEntity( ent ); } @@ -3507,6 +3511,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 @@ -3762,6 +3815,8 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, if( log ) G_BuildLogSet( log, built ); + G_AddRangeMarkerForBuildable( built ); + return built; } @@ -3874,6 +3929,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; } @@ -4342,6 +4398,7 @@ void G_BuildLogRevert( int id ) if( ent->s.eType == ET_BUILDABLE ) G_LogPrintf( "revert: remove %d %s\n", ent - g_entities, BG_Buildable( ent->s.modelindex )->name ); + G_RemoveRangeMarkerFrom( ent ); G_FreeEntity( ent ); break; } @@ -4402,3 +4459,63 @@ void G_BuildLogRevert( int id ) } } +/* +================ +G_UpdateBuildableRangeMarkers +================ +*/ +void G_UpdateBuildableRangeMarkers( void ) +{ + 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.hiMask = 0; + + 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; + + 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.hiMask |= 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 30834ff..23eaa5d 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -976,6 +976,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 ad97ed7..f600fc0 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1947,6 +1947,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 7ea873c..07938c3 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -195,6 +195,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 @@ -313,6 +314,7 @@ typedef struct qboolean teamInfo; // send team overlay updates? 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; @@ -812,6 +814,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 1805e45..1a6efe4 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -2480,6 +2480,7 @@ void G_RunFrame( int levelTime ) G_SpawnClients( TEAM_HUMANS ); 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 e522569..8be3860 100644 --- a/src/game/g_physics.c +++ b/src/game/g_physics.c @@ -158,6 +158,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 16cb6b8..42a16ab 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;