/** * @brief Moves a tag to the parent entity's bounding box center. */ static void UpdateTagLocation( gentity_t *ent, gentity_t *parent ) { vec3_t mins, maxs, center; if ( !parent ) return; VectorCopy( parent->s.origin, center ); if ( parent->client ) { BG_ClassBoundingBox( parent->client->ps.stats[ STAT_CLASS ], mins, maxs, nullptr, nullptr, nullptr ); BG_MoveOriginToBBOXCenter( center, mins, maxs ); // Also update weapon for humans. if( parent->client->pers.team == TEAM_HUMANS ) { ent->s.bc_data = BG_GetPlayerWeapon( &parent->client->ps ); } } else if ( parent->s.eType == ET_BUILDABLE ) { BG_BuildableBoundingBox( parent->s.modelindex, mins, maxs ); BG_MoveOriginToBBOXCenter( center, mins, maxs ); } Move( ent, center ); }
/* ================== CG_GhostBuildable ================== */ void CG_GhostBuildable( buildable_t buildable ) { refEntity_t ent; playerState_t *ps; vec3_t angles, entity_origin; vec3_t mins, maxs; trace_t tr; float scale; ps = &cg.predictedPlayerState; memset( &ent, 0, sizeof( ent ) ); BG_BuildableBoundingBox( buildable, mins, maxs ); BG_PositionBuildableRelativeToPlayer( ps, mins, maxs, CG_Trace, entity_origin, angles, &tr ); CG_PositionAndOrientateBuildable( ps->viewangles, entity_origin, tr.plane.normal, ps->clientNum, mins, maxs, ent.axis, ent.origin ); //offset on the Z axis if required VectorMA( ent.origin, BG_BuildableConfig( buildable )->zOffset, tr.plane.normal, ent.origin ); VectorCopy( ent.origin, ent.lightingOrigin ); VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all ent.hModel = cg_buildables[ buildable ].models[ 0 ]; if( ps->stats[ STAT_BUILDABLE ] & SB_VALID_TOGGLEBIT ) ent.customShader = cgs.media.greenBuildShader; else ent.customShader = cgs.media.redBuildShader; //rescale the model scale = BG_BuildableConfig( buildable )->modelScale; 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 ); }
/* ================== 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; 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; int anim; if( BG_Buildable( es->modelindex )->team == TEAM_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_BuildableBoundingBox( es->modelindex, mins, maxs ); // hack for shrunken barricades anim = es->torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT ); if( es->modelindex == BA_A_BARRICADE && ( anim == BANIM_DESTROYED || !( es->eFlags & EF_B_SPAWNED ) ) ) maxs[ 2 ] = (int)( maxs[ 2 ] * BARRICADE_SHRINKPROP ); VectorCopy( cent->lerpOrigin, origin ); // hack for shrunken shield anim = es->torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT ); if( es->modelindex == BA_H_SHIELD && ( anim == BANIM_DESTROYED || !( es->eFlags & EF_B_SPAWNED ) ) ) maxs[ 2 ] = (int)( maxs[ 2 ] * SHIELD_SHRINKPROP ); 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->eFlags & EF_B_SPAWNED ) || BG_Buildable( hit->modelindex )->transparentTest ) ) ) { 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_TEAM ] == TEAM_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; healthScale = (float)health / BG_Buildable( es->modelindex )->health; 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; float clipX, clipY, clipW, clipH; vec4_t frameColor; // this is fudged to get the width/height in the cfg to be more realistic scale = ( picH / d ) * 3; powered = es->eFlags & EF_B_POWERED; 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 ); clipW = ( 640.0f * cg_viewsize.integer ) / 100.0f; clipH = ( 480.0f * cg_viewsize.integer ) / 100.0f; clipX = 320.0f - ( clipW * 0.5f ); clipY = 240.0f - ( clipH * 0.5f ); CG_SetClipRegion( clipX, clipY, clipW, clipH ); 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 ); trap_R_SetColor( NULL ); CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); } { float nX; int healthMax; int healthPoints; healthMax = BG_Buildable( es->modelindex )->health; 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_ClearClipRegion( ); } }
/* ================== 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; team_t team = BG_Buildable( es->modelindex )->team; float scale; int health; //must be before EF_NODRAW check if( team == TEAM_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_BuildableBoundingBox( es->modelindex, mins, maxs ); if( es->pos.trType == TR_STATIONARY ) { // Positioning a buildable involves potentially up to two traces, and // seeing as buildables rarely move, we cache the results and recalculate // only if the buildable moves or changes orientation if( VectorCompare( cent->buildableCache.cachedOrigin, cent->lerpOrigin ) && VectorCompare( cent->buildableCache.cachedNormal, surfNormal ) ) { VectorCopy( cent->buildableCache.axis[ 0 ], ent.axis[ 0 ] ); VectorCopy( cent->buildableCache.axis[ 1 ], ent.axis[ 1 ] ); VectorCopy( cent->buildableCache.axis[ 2 ], ent.axis[ 2 ] ); VectorCopy( cent->buildableCache.origin, ent.origin ); } else { CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, es->number, mins, maxs, ent.axis, ent.origin ); VectorCopy( ent.axis[ 0 ], cent->buildableCache.axis[ 0 ] ); VectorCopy( ent.axis[ 1 ], cent->buildableCache.axis[ 1 ] ); VectorCopy( ent.axis[ 2 ], cent->buildableCache.axis[ 2 ] ); VectorCopy( ent.origin, cent->buildableCache.origin ); VectorCopy( cent->lerpOrigin, cent->buildableCache.cachedOrigin ); VectorCopy( surfNormal, cent->buildableCache.cachedNormal ); } } //offset on the Z axis if required VectorMA( ent.origin, BG_BuildableConfig( es->modelindex )->zOffset, 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->eFlags & EF_B_SPAWNED ) ) { sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; if( team == TEAM_HUMANS ) { ent.customShader = cgs.media.humanSpawningShader; prebuildSound = cgs.media.humanBuildablePrebuild; } else if( team == TEAM_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_BuildableConfig( es->modelindex )->modelScale; 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; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) ent.customShader = cgs.media.redBuildShader; //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; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretBarrel.customShader = cgs.media.redBuildShader; 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; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretTop.customShader = cgs.media.redBuildShader; 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_Buildable( es->modelindex )->turretProjType == 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; if( health < cent->lastBuildableHealth && ( es->eFlags & EF_B_SPAWNED ) ) { if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) { if( team == TEAM_HUMANS ) { int i = rand( ) % 4; trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); } else if( team == TEAM_ALIENS ) trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); cent->lastBuildableDamageSoundTime = cg.time; } } cent->lastBuildableHealth = health; //smoke etc for damaged buildables CG_BuildableParticleEffects( cent ); }
/** * @brief Tags an entity. */ void Tag( gentity_t *ent, team_t team, bool permanent ) { int data; vec3_t origin, mins, maxs; bool dead, player; gentity_t *beacon, **attachment; team_t targetTeam; // Get the beacon attachment owned by the tagging team. switch( team ) { case TEAM_ALIENS: attachment = &ent->alienTag; break; case TEAM_HUMANS: attachment = &ent->humanTag; break; default: return; } // Just refresh existing tags. if ( *attachment ) { RefreshTag( *attachment, true ); return; } switch( ent->s.eType ) { case ET_BUILDABLE: targetTeam = ent->buildableTeam; data = ent->s.modelindex; dead = G_Dead( ent ); player = false; BG_BuildableBoundingBox( ent->s.modelindex, mins, maxs ); break; case ET_PLAYER: targetTeam = (team_t)ent->client->pers.team; dead = G_Dead( ent ); player = true; BG_ClassBoundingBox( ent->client->pers.classSelection, mins, maxs, nullptr, nullptr, nullptr ); // Set beacon data to class (aliens) or weapon (humans). switch( targetTeam ) { case TEAM_ALIENS: data = ent->client->ps.stats[ STAT_CLASS ]; break; case TEAM_HUMANS: data = BG_GetPlayerWeapon( &ent->client->ps ); break; default: return; } break; default: return; } // Don't tag dead targets. if ( dead ) return; // Set beacon origin to center of target bounding box. VectorCopy( ent->s.origin, origin ); BG_MoveOriginToBBOXCenter( origin, mins, maxs ); // Create new beacon and attach it. beacon = New( origin, BCT_TAG, data, team ); beacon->tagAttachment = attachment; *attachment = beacon; beacon->s.bc_target = ent - g_entities; // Reset the entity's tag score. ent->tagScore = 0; // Set flags. if( player ) beacon->s.eFlags |= EF_BC_TAG_PLAYER; if( team != targetTeam ) beacon->s.eFlags |= EF_BC_ENEMY; // Set expiration time. if( permanent ) beacon->s.bc_etime = 0; else RefreshTag( beacon, true ); // Update the base clusterings. if ( ent->s.eType == ET_BUILDABLE ) BaseClustering::Update( beacon ); Propagate( beacon ); }
/** * @brief Tags an entity. * @todo Don't create a new beacon entity if retagged since that triggers regeneration of base * clusterings. */ void Tag( gentity_t *ent, team_t team, int owner, qboolean permanent ) { int i, data; vec3_t origin, mins, maxs; qboolean dead, player; gentity_t *beacon, **attachment; team_t targetTeam; switch( ent->s.eType ) { case ET_BUILDABLE: targetTeam = ent->buildableTeam; BG_BuildableBoundingBox( ent->s.modelindex, mins, maxs ); data = ent->s.modelindex; dead = ( ent->health <= 0 ); player = qfalse; break; case ET_PLAYER: targetTeam = (team_t)ent->client->pers.team; BG_ClassBoundingBox( ent->client->pers.classSelection, mins, maxs, NULL, NULL, NULL ); dead = ( ent->client && ent->client->ps.stats[ STAT_HEALTH ] <= 0 ); player = qtrue; // data is the class (aliens) or the weapon number (humans) switch( targetTeam ) { case TEAM_ALIENS: data = ent->client->ps.stats[ STAT_CLASS ]; break; case TEAM_HUMANS: data = BG_GetPlayerWeapon( &ent->client->ps ); break; default: return; } break; default: return; } for( i = 0; i < 3; i++ ) origin[ i ] = ent->s.origin[ i ] + ( mins[ i ] + maxs[ i ] ) / 2.0; switch( team ) { case TEAM_ALIENS: attachment = &ent->alienTag; break; case TEAM_HUMANS: attachment = &ent->humanTag; break; default: return; } if ( *attachment ) Delete( *attachment ); beacon = New( origin, BCT_TAG, data, team, owner ); beacon->tagAttachment = attachment; beacon->s.otherEntityNum2 = ent - g_entities; ent->tagScore = 0; if( player ) beacon->s.eFlags |= EF_BC_TAG_PLAYER; if( team != targetTeam ) beacon->s.eFlags |= EF_BC_ENEMY; if( permanent ) beacon->s.time2 = 0; else RefreshTag( beacon, true ); if( dead ) Delete( beacon, true ); else { *attachment = beacon; if ( ent->s.eType == ET_BUILDABLE ) BaseClustering::Update( beacon ); } Propagate( beacon ); }