/* ================== CG_DrawBuildableStatus ================== */ void CG_DrawBuildableStatus( void ) { int i; centity_t *cent; entityState_t *es; int buildableList[ MAX_ENTITIES_IN_SNAPSHOT ]; int buildables = 0; switch( cg.predictedPlayerState.weapon ) { case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: case WP_HBUILD2: for( i = 0; i < cg.snap->numEntities; i++ ) { cent = &cg_entities[ cg.snap->entities[ i ].number ]; es = ¢->currentState; if( es->eType == ET_BUILDABLE && BG_FindTeamForBuildable( es->modelindex ) == BG_FindTeamForWeapon( cg.predictedPlayerState.weapon ) ) buildableList[ buildables++ ] = cg.snap->entities[ i ].number; } qsort( buildableList, buildables, sizeof( int ), CG_SortDistance ); for( i = 0; i < buildables; i++ ) CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ] ); break; default: break; } }
/* =============== areaZapFire =============== */ void areaZapFire( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *traceEnt; vec3_t mins, maxs; VectorSet( mins, -LEVEL2_AREAZAP_WIDTH, -LEVEL2_AREAZAP_WIDTH, -LEVEL2_AREAZAP_WIDTH ); VectorSet( maxs, LEVEL2_AREAZAP_WIDTH, LEVEL2_AREAZAP_WIDTH, LEVEL2_AREAZAP_WIDTH ); // set aiming directions AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePoint( ent, forward, right, up, muzzle ); VectorMA( muzzle, LEVEL2_AREAZAP_RANGE, forward, end ); trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); if( tr.surfaceFlags & SURF_NOIMPACT ) return; traceEnt = &g_entities[ tr.entityNum ]; if( ( ( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) || ( traceEnt->s.eType == ET_BUILDABLE && BG_FindTeamForBuildable( traceEnt->s.modelindex ) == BIT_HUMANS ) ) && traceEnt->health > 0 ) { G_CreateNewZap( ent, traceEnt ); } }
/* =============== G_FindNewZapTarget =============== */ static gentity_t *G_FindNewZapTarget( gentity_t *ent ) { int entityList[ MAX_GENTITIES ]; vec3_t range = { LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE }; vec3_t mins, maxs; int i, j, k, num; gentity_t *enemy; trace_t tr; VectorScale( range, 1.0f / M_ROOT3, range ); VectorAdd( ent->s.origin, range, maxs ); VectorSubtract( ent->s.origin, range, mins ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { enemy = &g_entities[ entityList[ i ] ]; if( ( ( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) || ( enemy->s.eType == ET_BUILDABLE && BG_FindTeamForBuildable( enemy->s.modelindex ) == BIT_HUMANS ) ) && enemy->health > 0 ) { qboolean foundOldTarget = qfalse; trap_Trace( &tr, muzzle, NULL, NULL, enemy->s.origin, ent->s.number, MASK_SHOT ); //can't see target from here if( tr.entityNum == ENTITYNUM_WORLD ) continue; for( j = 0; j < MAX_ZAPS; j++ ) { zap_t *zap = &zaps[ j ]; for( k = 0; k < zap->numTargets; k++ ) { if( zap->targets[ k ] == enemy ) { foundOldTarget = qtrue; break; } } if( foundOldTarget ) break; } // enemy is already targetted if( foundOldTarget ) continue; return enemy; } } return NULL; }
/* =============== CG_BuildableInRange =============== */ static qboolean CG_BuildableInRange( playerState_t *ps ) { vec3_t view, point; trace_t trace; entityState_t *es; AngleVectors( cg.refdefViewAngles, view, NULL, NULL ); VectorMA( cg.refdef.vieworg, 64, view, point ); CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL, point, ps->clientNum, MASK_SHOT ); es = &cg_entities[ trace.entityNum ].currentState; if( es->eType == ET_BUILDABLE && ps->stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) ) return qtrue; else return qfalse; }
/* ================== CG_BuildableParticleEffects ================== */ static void CG_BuildableParticleEffects( centity_t *cent ) { entityState_t *es = ¢->currentState; buildableTeam_t team = BG_FindTeamForBuildable( es->modelindex ); int health = es->generic1 & B_HEALTH_MASK; float healthFrac = (float)health / B_HEALTH_MASK; if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) return; if( team == BIT_HUMANS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } else if( team == BIT_ALIENS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } }
/* ================== CG_BuildableStatusDisplay ================== */ static void CG_BuildableStatusDisplay( centity_t *cent ) { entityState_t *es = ¢->currentState; vec3_t origin; float healthScale; int health; float x, y; vec4_t color; qboolean powered, marked; trace_t tr; float d; buildStat_t *bs; int i, j; int entNum; vec3_t trOrigin; vec3_t right; qboolean visible = qfalse; vec3_t mins, maxs; entityState_t *hit; if( BG_FindTeamForBuildable( es->modelindex ) == BIT_ALIENS ) bs = &cgs.alienBuildStat; else bs = &cgs.humanBuildStat; if( !bs->loaded ) return; d = Distance( cent->lerpOrigin, cg.refdef.vieworg ); if( d > STATUS_MAX_VIEW_DIST ) return; Vector4Copy( bs->foreColor, color ); // trace for center point BG_FindBBoxForBuildable( es->modelindex, mins, maxs ); VectorCopy( cent->lerpOrigin, origin ); // center point origin[ 2 ] += mins[ 2 ]; origin[ 2 ] += ( abs( mins[ 2 ] ) + abs( maxs[ 2 ] ) ) / 2; entNum = cg.predictedPlayerState.clientNum; // if first try fails, step left, step right for( j = 0; j < 3; j++ ) { VectorCopy( cg.refdef.vieworg, trOrigin ); switch( j ) { case 1: // step right AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, STATUS_PEEK_DIST, right, trOrigin ); break; case 2: // step left AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, -STATUS_PEEK_DIST, right, trOrigin ); break; default: break; } // look through up to 3 players and/or transparent buildables for( i = 0; i < 3; i++ ) { CG_Trace( &tr, trOrigin, NULL, NULL, origin, entNum, MASK_SHOT ); if( tr.entityNum == cent->currentState.number ) { visible = qtrue; break; } if( tr.entityNum == ENTITYNUM_WORLD ) break; hit = &cg_entities[ tr.entityNum ].currentState; if( tr.entityNum < MAX_CLIENTS || ( hit->eType == ET_BUILDABLE && ( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) || BG_FindTransparentTestForBuildable( hit->modelindex ) ) ) ) { entNum = tr.entityNum; VectorCopy( tr.endpos, trOrigin ); } else break; } } // hack to make the kit obscure view if( cg_drawGun.integer && visible && cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS && CG_WorldToScreen( origin, &x, &y ) ) { if( x > 450 && y > 290 ) visible = qfalse; } if( !visible && cent->buildableStatus.visible ) { cent->buildableStatus.visible = qfalse; cent->buildableStatus.lastTime = cg.time; } else if( visible && !cent->buildableStatus.visible ) { cent->buildableStatus.visible = qtrue; cent->buildableStatus.lastTime = cg.time; } // Fade up if( cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; } // Fade down if( !cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = 1.0f - (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; else return; } health = es->generic1 & B_HEALTH_MASK; healthScale = (float)health / B_HEALTH_MASK; if( health > 0 && healthScale < 0.01f ) healthScale = 0.01f; else if( healthScale < 0.0f ) healthScale = 0.0f; else if( healthScale > 1.0f ) healthScale = 1.0f; if( CG_WorldToScreen( origin, &x, &y ) ) { float picH = bs->frameHeight; float picW = bs->frameWidth; float picX = x; float picY = y; float scale; float subH, subY; vec4_t frameColor; // this is fudged to get the width/height in the cfg to be more realistic scale = ( picH / d ) * 3; powered = es->generic1 & B_POWERED_TOGGLEBIT; marked = es->generic1 & B_MARKED_TOGGLEBIT; picH *= scale; picW *= scale; picX -= ( picW * 0.5f ); picY -= ( picH * 0.5f ); // sub-elements such as icons and number subH = picH - ( picH * bs->verticalMargin ); subY = picY + ( picH * 0.5f ) - ( subH * 0.5f ); if( bs->frameShader ) { Vector4Copy( bs->backColor, frameColor ); frameColor[ 3 ] = color[ 3 ]; trap_R_SetColor( frameColor ); CG_DrawPic( picX, picY, picW, picH, bs->frameShader ); trap_R_SetColor( NULL ); } if( health > 0 ) { float hX, hY, hW, hH; vec4_t healthColor; hX = picX + ( bs->healthPadding * scale ); hY = picY + ( bs->healthPadding * scale ); hH = picH - ( bs->healthPadding * 2.0f * scale ); hW = picW * healthScale - ( bs->healthPadding * 2.0f * scale ); if( healthScale == 1.0f ) Vector4Copy( bs->healthLowColor, healthColor ); else if( healthScale >= 0.75f ) Vector4Copy( bs->healthGuardedColor, healthColor ); else if( healthScale >= 0.50f ) Vector4Copy( bs->healthElevatedColor, healthColor ); else if( healthScale >= 0.25f ) Vector4Copy( bs->healthHighColor, healthColor ); else Vector4Copy( bs->healthSevereColor, healthColor ); healthColor[ 3 ] = color[ 3 ]; trap_R_SetColor( healthColor ); CG_DrawPic( hX, hY, hW, hH, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } if( bs->overlayShader ) { float oW = bs->overlayWidth; float oH = bs->overlayHeight; float oX = x; float oY = y; oH *= scale; oW *= scale; oX -= ( oW * 0.5f ); oY -= ( oH * 0.5f ); trap_R_SetColor( frameColor ); CG_DrawPic( oX, oY, oW, oH, bs->overlayShader ); trap_R_SetColor( NULL ); } trap_R_SetColor( color ); if( !powered ) { float pX; pX = picX + ( subH * bs->horizontalMargin ); CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); } if( marked ) { float mX; mX = picX + picW - ( subH * bs->horizontalMargin ) - subH; CG_DrawPic( mX, subY, subH, subH, bs->markedShader ); } { float nX; int healthMax; int healthPoints; healthMax = BG_FindHealthForBuildable( es->modelindex ); healthPoints = (int)( healthScale * healthMax ); if( health > 0 && healthPoints < 1 ) healthPoints = 1; nX = picX + ( picW * 0.5f ) - 2.0f - ( ( subH * 4 ) * 0.5f ); if( healthPoints > 999 ) nX -= 0.0f; else if( healthPoints > 99 ) nX -= subH * 0.5f; else if( healthPoints > 9 ) nX -= subH * 1.0f; else nX -= subH * 1.5f; CG_DrawField( nX, subY, 4, subH, subH, healthPoints ); } trap_R_SetColor( NULL ); } }
/* =============== CG_InitBuildables Initialises the animation db =============== */ void CG_InitBuildables( void ) { char filename[ MAX_QPATH ]; char soundfile[ MAX_QPATH ]; char *buildableName; char *modelFile; int i; int j; fileHandle_t f; memset( cg_buildables, 0, sizeof( cg_buildables ) ); //default sounds for( j = BANIM_NONE + 1; j < MAX_BUILDABLE_ANIMATIONS; j++ ) { strcpy( soundfile, cg_buildableSoundNames[ j - 1 ] ); Com_sprintf( filename, sizeof( filename ), "sound/buildables/alien/%s", soundfile ); defaultAlienSounds[ j ] = trap_S_RegisterSound( filename, qfalse ); Com_sprintf( filename, sizeof( filename ), "sound/buildables/human/%s", soundfile ); defaultHumanSounds[ j ] = trap_S_RegisterSound( filename, qfalse ); } cg.buildablesFraction = 0.0f; for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ ) { buildableName = BG_FindNameForBuildable( i ); //animation.cfg Com_sprintf( filename, sizeof( filename ), "models/buildables/%s/animation.cfg", buildableName ); if ( !CG_ParseBuildableAnimationFile( filename, i ) ) Com_Printf( S_COLOR_YELLOW "WARNING: failed to load animation file %s\n", filename ); //sound.cfg Com_sprintf( filename, sizeof( filename ), "sound/buildables/%s/sound.cfg", buildableName ); if ( !CG_ParseBuildableSoundFile( filename, i ) ) Com_Printf( S_COLOR_YELLOW "WARNING: failed to load sound file %s\n", filename ); //models for( j = 0; j <= 3; j++ ) { if( ( modelFile = BG_FindModelsForBuildable( i, j ) ) ) cg_buildables[ i ].models[ j ] = trap_R_RegisterModel( modelFile ); } //sounds for( j = BANIM_NONE + 1; j < MAX_BUILDABLE_ANIMATIONS; j++ ) { strcpy( soundfile, cg_buildableSoundNames[ j - 1 ] ); Com_sprintf( filename, sizeof( filename ), "sound/buildables/%s/%s", buildableName, soundfile ); if( cg_buildables[ i ].sounds[ j ].enabled ) { if( trap_FS_FOpenFile( filename, &f, FS_READ ) > 0 ) { //file exists so close it trap_FS_FCloseFile( f ); cg_buildables[ i ].sounds[ j ].sound = trap_S_RegisterSound( filename, qfalse ); } else { //file doesn't exist - use default if( BG_FindTeamForBuildable( i ) == BIT_ALIENS ) cg_buildables[ i ].sounds[ j ].sound = defaultAlienSounds[ j ]; else cg_buildables[ i ].sounds[ j ].sound = defaultHumanSounds[ j ]; } } } cg.buildablesFraction = (float)i / (float)( BA_NUM_BUILDABLES - 1 ); trap_UpdateScreen( ); } cgs.media.teslaZapTS = CG_RegisterTrailSystem( "models/buildables/tesla/zap" ); }
/* ================== CG_Buildable ================== */ void CG_Buildable( centity_t *cent ) { refEntity_t ent; entityState_t *es = ¢->currentState; vec3_t angles; vec3_t surfNormal, xNormal, mins, maxs; vec3_t refNormal = { 0.0f, 0.0f, 1.0f }; float rotAngle; buildableTeam_t team = BG_FindTeamForBuildable( es->modelindex ); float scale; int health; float healthScale; //must be before EF_NODRAW check if( team == BIT_ALIENS ) CG_Creep( cent ); // if set to invisible, skip if( es->eFlags & EF_NODRAW ) { if( CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); return; } memset ( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( cent->lerpOrigin, ent.oldorigin ); VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); VectorCopy( es->origin2, surfNormal ); VectorCopy( es->angles, angles ); BG_FindBBoxForBuildable( es->modelindex, mins, maxs ); if( es->pos.trType == TR_STATIONARY ) CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, es->number, mins, maxs, ent.axis, ent.origin ); //offset on the Z axis if required VectorMA( ent.origin, BG_FindZOffsetForBuildable( es->modelindex ), surfNormal, ent.origin ); VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all VectorCopy( ent.origin, ent.lightingOrigin ); ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ]; if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) { sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; if( team == BIT_HUMANS ) { ent.customShader = cgs.media.humanSpawningShader; prebuildSound = cgs.media.humanBuildablePrebuild; } else if( team == BIT_ALIENS ) prebuildSound = cgs.media.alienBuildablePrebuild; trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound ); } CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp ); //rescale the model scale = BG_FindModelScaleForBuildable( es->modelindex ); if( scale != 1.0f ) { VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] ); VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] ); VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] ); ent.nonNormalizedAxes = qtrue; } else ent.nonNormalizedAxes = qfalse; //add to refresh list trap_R_AddRefEntityToScene( &ent ); CrossProduct( surfNormal, refNormal, xNormal ); VectorNormalize( xNormal ); rotAngle = RAD2DEG( acos( DotProduct( surfNormal, refNormal ) ) ); //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 1 ] ) { refEntity_t turretBarrel; vec3_t flatAxis[ 3 ]; memset( &turretBarrel, 0, sizeof( turretBarrel ) ); turretBarrel.hModel = cg_buildables[ es->modelindex ].models[ 1 ]; CG_PositionEntityOnTag( &turretBarrel, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretBarrel.lightingOrigin ); AnglesToAxis( es->angles2, flatAxis ); RotatePointAroundVector( turretBarrel.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretBarrel.oldframe = ent.oldframe; turretBarrel.frame = ent.frame; turretBarrel.backlerp = ent.backlerp; turretBarrel.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretBarrel.axis[ 0 ], scale, turretBarrel.axis[ 0 ] ); VectorScale( turretBarrel.axis[ 1 ], scale, turretBarrel.axis[ 1 ] ); VectorScale( turretBarrel.axis[ 2 ], scale, turretBarrel.axis[ 2 ] ); turretBarrel.nonNormalizedAxes = qtrue; } else turretBarrel.nonNormalizedAxes = qfalse; trap_R_AddRefEntityToScene( &turretBarrel ); } //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 2 ] ) { refEntity_t turretTop; vec3_t flatAxis[ 3 ]; vec3_t swivelAngles; memset( &turretTop, 0, sizeof( turretTop ) ); VectorCopy( es->angles2, swivelAngles ); swivelAngles[ PITCH ] = 0.0f; turretTop.hModel = cg_buildables[ es->modelindex ].models[ 2 ]; CG_PositionRotatedEntityOnTag( &turretTop, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretTop.lightingOrigin ); AnglesToAxis( swivelAngles, flatAxis ); RotatePointAroundVector( turretTop.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretTop.oldframe = ent.oldframe; turretTop.frame = ent.frame; turretTop.backlerp = ent.backlerp; turretTop.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretTop.axis[ 0 ], scale, turretTop.axis[ 0 ] ); VectorScale( turretTop.axis[ 1 ], scale, turretTop.axis[ 1 ] ); VectorScale( turretTop.axis[ 2 ], scale, turretTop.axis[ 2 ] ); turretTop.nonNormalizedAxes = qtrue; } else turretTop.nonNormalizedAxes = qfalse; trap_R_AddRefEntityToScene( &turretTop ); } //weapon effects for turrets if( es->eFlags & EF_FIRING ) { weaponInfo_t *weapon = &cg_weapons[ es->weapon ]; if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME || BG_FindProjTypeForBuildable( es->modelindex ) == WP_TESLAGEN ) { if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ) { trap_R_AddLightToScene( cent->lerpOrigin, 300 + ( rand( ) & 31 ), weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ); } } if( weapon->wim[ WPM_PRIMARY ].firingSound ) { trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->wim[ WPM_PRIMARY ].firingSound ); } else if( weapon->readySound ) trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound ); } health = es->generic1 & B_HEALTH_MASK; healthScale = (float)health / B_HEALTH_MASK; if( healthScale < cent->lastBuildableHealthScale && ( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) { if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) { if( team == BIT_HUMANS ) { int i = rand( ) % 4; trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); } else if( team == BIT_ALIENS ) trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); cent->lastBuildableDamageSoundTime = cg.time; } } cent->lastBuildableHealthScale = healthScale; //smoke etc for damaged buildables CG_BuildableParticleEffects( cent ); }
/* ================== CG_BuildableHealthBar ================== */ static void CG_BuildableHealthBar( centity_t *cent ) { vec3_t origin, origin2, down, right, back, downLength, rightLength; float rimWidth = HEALTH_BAR_HEIGHT / 15.0f; float doneWidth, leftWidth, progress; int health; qhandle_t shader; entityState_t *es; vec3_t mins, maxs; es = ¢->currentState; health = es->generic1 & ~( B_POWERED_TOGGLEBIT | B_DCCED_TOGGLEBIT | B_SPAWNED_TOGGLEBIT ); progress = (float)health / B_HEALTH_SCALE; if( progress < 0.0f ) progress = 0.0f; else if( progress > 1.0f ) progress = 1.0f; if( progress < 0.33f ) shader = cgs.media.redBuildShader; else shader = cgs.media.greenBuildShader; doneWidth = ( HEALTH_BAR_WIDTH - 2 * rimWidth ) * progress; leftWidth = ( HEALTH_BAR_WIDTH - 2 * rimWidth ) - doneWidth; VectorCopy( cg.refdef.viewaxis[ 2 ], down ); VectorInverse( down ); VectorCopy( cg.refdef.viewaxis[ 1 ], right ); VectorInverse( right ); VectorSubtract( cg.refdef.vieworg, cent->lerpOrigin, back ); VectorNormalize( back ); VectorCopy( cent->lerpOrigin, origin ); BG_FindBBoxForBuildable( es->modelindex, mins, maxs ); VectorMA( origin, 48.0f, es->origin2, origin ); VectorMA( origin, -HEALTH_BAR_WIDTH / 2.0f, right, origin ); VectorMA( origin, maxs[ 0 ] + 8.0f, back, origin ); VectorCopy( origin, origin2 ); VectorScale( right, rimWidth + doneWidth, rightLength ); VectorScale( down, HEALTH_BAR_HEIGHT, downLength ); CG_DrawPlane( origin2, downLength, rightLength, shader ); VectorMA( origin, rimWidth + doneWidth, right, origin2 ); VectorScale( right, leftWidth, rightLength ); VectorScale( down, rimWidth, downLength ); CG_DrawPlane( origin2, downLength, rightLength, shader ); VectorMA( origin, rimWidth + doneWidth, right, origin2 ); VectorMA( origin2, HEALTH_BAR_HEIGHT - rimWidth, down, origin2 ); VectorScale( right, leftWidth, rightLength ); VectorScale( down, rimWidth, downLength ); CG_DrawPlane( origin2, downLength, rightLength, shader ); VectorMA( origin, HEALTH_BAR_WIDTH - rimWidth, right, origin2 ); VectorScale( right, rimWidth, rightLength ); VectorScale( down, HEALTH_BAR_HEIGHT, downLength ); CG_DrawPlane( origin2, downLength, rightLength, shader ); if( !( es->generic1 & B_POWERED_TOGGLEBIT ) && BG_FindTeamForBuildable( es->modelindex ) == BIT_HUMANS ) { VectorMA( origin, 15.0f, right, origin2 ); VectorMA( origin2, HEALTH_BAR_HEIGHT + 5.0f, down, origin2 ); VectorScale( right, HEALTH_BAR_WIDTH / 2.0f - 5.0f, rightLength ); VectorScale( down, HEALTH_BAR_WIDTH / 2.0f - 5.0f, downLength ); CG_DrawPlane( origin2, downLength, rightLength, cgs.media.noPowerShader ); } }