Exemplo n.º 1
0
//-----------------------------------------------------------------------------
// Purpose: Update this client's target entity
//-----------------------------------------------------------------------------
void C_HL2MP_Player::UpdateIDTarget()
{
	if ( !IsLocalPlayer() )
		return;

	// Clear old target and find a new one
	m_iIDEntIndex = 0;

	// don't show IDs in chase spec mode
	if ( GetObserverMode() == OBS_MODE_CHASE || 
		 GetObserverMode() == OBS_MODE_DEATHCAM )
		 return;

	trace_t tr;
	Vector vecStart, vecEnd;
	VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
	VectorMA( MainViewOrigin(), 10,   MainViewForward(), vecStart );
	UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );

	if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
	{
		C_BaseEntity *pEntity = tr.m_pEnt;

		if ( pEntity && (pEntity != this) )
		{
			m_iIDEntIndex = pEntity->entindex();
		}
	}
}
Exemplo n.º 2
0
//-----------------------------------------------------------------------------
// Purpose: Update this client's target entity
//-----------------------------------------------------------------------------
void C_SDKPlayer::UpdateIDTarget()
{
	if ( !IsLocalPlayer() )
		return;

	// Clear old target and find a new one
	m_iIDEntIndex = 0;

	// don't show id's in any state but active.
	if ( State_Get() != PLAYER_STATE_ACTIVE )
		return;

	trace_t tr;
	Vector vecStart, vecEnd;
	VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
	VectorMA( MainViewOrigin(), 10,   MainViewForward(), vecStart );
	UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );

	if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
	{
		C_BaseEntity *pEntity = tr.m_pEnt;

		if ( pEntity && (pEntity != this) )
		{
			m_iIDEntIndex = pEntity->entindex();
		}
	}
}
Exemplo n.º 3
0
//-----------------------------------------------------------------------------
// Actual Eye position + angles
//-----------------------------------------------------------------------------
Vector CBasePlayer::EyePosition( )
{
#ifdef CLIENT_DLL
	IClientVehicle *pVehicle = GetVehicle();
#else
	IServerVehicle *pVehicle = GetVehicle();
#endif
	if ( pVehicle )
	{
		Assert( pVehicle );
		int nRole = pVehicle->GetPassengerRole( this );

		Vector vecEyeOrigin;
		QAngle angEyeAngles;
		pVehicle->GetVehicleViewPosition( nRole, &vecEyeOrigin, &angEyeAngles );
		return vecEyeOrigin;
	}
	else
	{
#ifdef CLIENT_DLL
		if ( IsObserver() )
		{
			if ( m_iObserverMode == OBS_MODE_CHASE )
			{
				if ( IsLocalPlayer() )
				{
					return MainViewOrigin();
				}
			}
		}
#endif
		return BaseClass::EyePosition();
	}
}
void C_ParticleSmokeGrenade::ClientThink()
{
	if ( m_CurrentStage == 1 )
	{
		// Add our influence to the global smoke fog alpha.
		ASSERT_LOCAL_PLAYER_RESOLVABLE();
		int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
		float testDist = (MainViewOrigin(nSlot) - m_SmokeBasePos).Length();

		float fadeEnd = m_ExpandRadius;

		// The center of the smoke cloud that always gives full fog overlay
		float flCoreDistance = fadeEnd * 0.15;
		
		if(testDist < fadeEnd)
		{			
			if( testDist < flCoreDistance )
			{
				EngineGetSmokeFogOverlayAlpha() += m_FadeAlpha;
			}
			else
			{
				EngineGetSmokeFogOverlayAlpha() += (1 - ( testDist - flCoreDistance ) / ( fadeEnd - flCoreDistance ) ) * m_FadeAlpha;
			}
		}	
	}
}
Exemplo n.º 5
0
//-----------------------------------------------------------------------------
// Actual Eye position + angles
//-----------------------------------------------------------------------------
Vector CBasePlayer::EyePosition( )
{
	if ( GetVehicle() != NULL )
	{
		// Return the cached result
		CacheVehicleView();
		return m_vecVehicleViewOrigin;
	}
	else
	{
#ifdef CLIENT_DLL

		if ( IsObserver() )
		{
			if ( m_iObserverMode == OBS_MODE_CHASE )
			{
				if ( IsLocalPlayer() )
				{
					return MainViewOrigin();
				}
			}
		}
#endif
		return BaseClass::EyePosition();
	}
}
Exemplo n.º 6
0
//-----------------------------------------------------------------------------
// Purpose: Compute the size of the panel based upon the commander's zoom level
//-----------------------------------------------------------------------------
void CEntityPanel::ComputeAndSetSize( void )
{
	m_flScale = 1.0;

	// Scale the image
	// Use different scales in tactical / normal
	if ( IsLocalPlayerInTactical() )
	{
		CClientModeCommander *commander = ( CClientModeCommander * )ClientModeCommander();
		Assert( commander );
		float flZoom = commander->GetCommanderOverlayPanel()->GetZoom();

		// Scale our size
		m_flScale = 0.75 + (0.25 * (1.0 - flZoom)); // 1/2 size at max zoomed out, full size by half zoomed in
	}
	else if ( m_pBaseEntity )
	{
		// Get distance to entity
		float flDistance = (m_pBaseEntity->GetRenderOrigin() - MainViewOrigin()).Length();
		flDistance *= 2;
  		m_flScale = 0.25 + MAX( 0, 2.0 - (flDistance / 2048) );
	}

	// Update the size
	int w = m_iOrgWidth * m_flScale;
	int h = m_iOrgHeight * m_flScale;
	SetSize( w,h );

	// Update the offsets too
	m_OffsetX = m_iOrgOffsetX * m_flScale;
	m_OffsetY = m_iOrgOffsetY * m_flScale;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTFFreezePanelCallout *CTFFreezePanel::TestAndAddCallout( Vector &origin, Vector &vMins, Vector &vMaxs, CUtlVector<Vector> *vecCalloutsTL, 
			CUtlVector<Vector> *vecCalloutsBR, Vector &vecFreezeTL, Vector &vecFreezeBR, Vector &vecStatTL, Vector &vecStatBR, int *iX, int *iY )
{
	// This is the offset from the topleft of the callout to the arrow tip
	const int iXOffset = XRES(25);
	const int iYOffset = YRES(50);

	//if ( engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) && !engine->CullBox( vMins + origin, vMaxs + origin ) )
	{
		if ( GetVectorInScreenSpace( origin, *iX, *iY ) )
		{
			*iX -= iXOffset;
			*iY -= iYOffset;
			int iRight = *iX + CALLOUT_WIDE;
			int iBottom = *iY + CALLOUT_TALL;
			if ( *iX > 0 && *iY > 0 && (iRight < ScreenWidth()) && (iBottom < (ScreenHeight()-YRES(40))) )
			{
				// Make sure it wouldn't be over the top of the freezepanel or statpanel
				Vector vecCalloutTL( *iX, *iY, 0 );
				Vector vecCalloutBR( iRight, iBottom, 1 );
				if ( !QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecFreezeTL, vecFreezeBR ) &&
					 !QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecStatTL, vecStatBR ) )
				{
					// Make sure it doesn't intersect any other callouts
					bool bClear = true;
					for ( int iCall = 0; iCall < vecCalloutsTL->Count(); iCall++ )
					{
						if ( QuickBoxIntersectTest( vecCalloutTL, vecCalloutBR, vecCalloutsTL->Element(iCall), vecCalloutsBR->Element(iCall) ) )
						{
							bClear = false;
							break;
						}
					}

					if ( bClear )
					{
						// Verify that we have LOS to the gib
						trace_t	tr;
						UTIL_TraceLine( origin, MainViewOrigin(), MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr );
						bClear = ( tr.fraction >= 1.0f );
					}

					if ( bClear )
					{
						CTFFreezePanelCallout *pCallout = new CTFFreezePanelCallout( g_pClientMode->GetViewport(), "FreezePanelCallout" );
						m_pCalloutPanels.AddToTail( vgui::SETUP_PANEL(pCallout) );
						vecCalloutsTL->AddToTail( vecCalloutTL );
						vecCalloutsBR->AddToTail( vecCalloutBR );
						pCallout->SetVisible( true );
						pCallout->SetBounds( *iX, *iY, CALLOUT_WIDE, CALLOUT_TALL );
						return pCallout;
					}
				}
			}
		}
	}

	return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHLClient::View_Render( vrect_t *rect )
{
	VPROF( "View_Render" );
	// This is the main render entry point... 
	g_SmokeFogOverlayAlpha = 0;	// Reset the overlay alpha.

	view->Render( rect );

	// Draw an overlay to make it even harder to see inside smoke particle systems.
	g_SmokeFogOverlayColor = engine->GetLightForPoint(MainViewOrigin(), true);
	DrawSmokeFogOverlay();

//	Rope_ShowRSpeeds();
}
Exemplo n.º 9
0
static void GetPos( const CCommand &args, Vector &vecOrigin, QAngle &angles )
{
	vecOrigin = MainViewOrigin();
	angles = MainViewAngles();
	if ( args.ArgC() == 2 && atoi( args[1] ) == 2 )
	{
		C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
		if ( pPlayer )
		{
			vecOrigin = pPlayer->GetAbsOrigin();
			angles = pPlayer->GetAbsAngles();
		}
	}
}
void vParticleOperator_GravityWorld::Simulate( vParticle *parent )
{
	float dot_z = DotProduct( MainViewForward(), Vector( 0, 0, -1 ) );
	float rotation_dir = Sign( dot_z );

	int vx, vy, w, t;
	vgui::surface()->GetFullscreenViewport( vx, vy, w, t );

	Vector screen;
	if ( ScreenTransform( MainViewOrigin() + Vector( 0, 0, 100 ), screen ) )
		ScreenTransform( MainViewOrigin() - Vector( 0, 0, 100 ), screen );

	screen *= Vector( 0.5f, -0.5f, 0 );
	screen += Vector( 0.5f, 0.5f, 0 );

	Vector2D gravity_center( w * screen.x, t * screen.y );
	Vector2D delta = parent->vecPos - gravity_center;

	float screenLength = delta.NormalizeInPlace();
	delta *= rotation_dir * -1.0f;

	Vector2D accel = delta * vParticle::GetRelativeScale() * GetImpulse( parent ) * amt;

	float speedMult = 1.0f;

	if ( rotation_dir > 0 )
	{
		float drag = RemapValClamped( rotation_dir, 0.5f, 1, 1, 0.00001f );
		ScaleByFrametime( drag );

		speedMult = RemapValClamped( (screenLength/vParticle::GetRelativeScale()), 0, 1000, 0, 1 ) * (1.0f - drag) + drag;
	}

	parent->vecVelocity += accel;
	parent->vecVelocity *= speedMult;
}
Exemplo n.º 11
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &pos - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool EffectOccluded(const Vector &pos, pixelvis_handle_t *queryHandle)
{
    if (!queryHandle)
    {
        // NOTE: This is called by networking code before the current view is set up.
        // so use the main view instead
        trace_t	tr;
        UTIL_TraceLine(pos, MainViewOrigin(), MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr);

        return (tr.fraction < 1.0f) ? true : false;
    }
    pixelvis_queryparams_t params;
    params.Init(pos);

    return PixelVisibility_FractionVisible(params, queryHandle) > 0.0f ? false : true;
}
Exemplo n.º 12
0
//-----------------------------------------------------------------------------
// Actual Eye position + angles
//-----------------------------------------------------------------------------
Vector CBasePlayer::EyePosition( )
{
#ifdef CLIENT_DLL
	if ( IsObserver() )
	{
		if ( GetObserverMode() == OBS_MODE_CHASE )
		{
			if ( IsLocalPlayer() )
			{
				return MainViewOrigin();
			}
		}
	}
#endif
	return BaseClass::EyePosition();
}
Exemplo n.º 13
0
void CGodRaysEffect::GetLightPosition( Vector& vLightPos )
{
	// Default to world center.
	vLightPos = vec3_origin;
	
	if( m_pSun )
	{
		// Get the directory vector of the sun.
		Vector vDirection = m_pSun->m_vDirection;

		// Retrieve the origin of the current view.
		const Vector vCurOrigin = MainViewOrigin();

		// Construct a sunlight position.
		vLightPos = vCurOrigin + vDirection * view->GetViewSetup()->zFar * 0.999f;
	}
}
Exemplo n.º 14
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool C_FuncPhysicsRespawnZone::CanMovePropAt( Vector vecOrigin, const Vector &vecMins, const Vector &vecMaxs )
{
	float flDist = cl_phys_props_respawndist.GetFloat();

	// Do a distance check first. We don't want to move props when the player is near 'em.
	if ( (MainViewOrigin() - vecOrigin).LengthSqr() < (flDist*flDist) )
		return false;

	// Now make sure it's not in view
	if( engine->IsBoxInViewCluster( vecMins + vecOrigin, vecMaxs + vecOrigin) )
		return false;

	if( !engine->CullBox( vecMins + vecOrigin, vecMaxs + vecOrigin ) )
		return false;

	return true;
}
Exemplo n.º 15
0
/*
====================
//	if( r_drawtranslucentworld )
//	{
//		bSaveDrawTranslucentWorld = r_drawtranslucentworld->GetBool();
		// NOTE! : We use to set this to 0 for HDR.
		//		r_drawtranslucentworld->SetValue( 0 );
//	}
//	if( r_drawtranslucentrenderables )
R_TimeRefresh_f

For program optimization
====================
*/
void R_TimeRefresh_f (void)
{
	int			i;
	float		start, stop, time;
	CViewSetup		view;

	materials->Flush( true );

	Q_memset(&view, 0, sizeof(view));
	view.origin = MainViewOrigin();
	view.angles[0] = 0;
	view.angles[1] = 0;
	view.angles[2] = 0;
	view.x = 0;
	view.y = 0;
	view.width = videomode->GetModeWidth();
	view.height = videomode->GetModeHeight();
	view.fov = 75;
	view.fovViewmodel = 75;
	view.m_flAspectRatio = 1.0f;
	view.zNear = 4;
	view.zFar = MAX_COORD_FLOAT;
	view.zNearViewmodel = 4;
	view.zFarViewmodel = MAX_COORD_FLOAT;

	int savedeveloper = developer.GetInt();
	developer.SetValue( 0 );

	start = Sys_FloatTime ();
	for (i=0 ; i<128 ; i++)
	{
		view.angles[1] = i/128.0*360.0;
		g_ClientDLL->RenderView( view, VIEW_CLEAR_COLOR, RENDERVIEW_DRAWVIEWMODEL | RENDERVIEW_DRAWHUD );
		Shader_SwapBuffers();
	}

	materials->Flush( true );
	Shader_SwapBuffers();
	stop = Sys_FloatTime ();
	time = stop-start;

	developer.SetValue( savedeveloper );

	ConMsg ("%f seconds (%f fps)\n", time, 128/time);
}
Exemplo n.º 16
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_EnvStarfield::ClientThink( void )
{
	if ( !m_bOn || !m_flDensity ) 
		return;

	PMaterialHandle	hParticleMaterial = m_pEmitter->GetPMaterial( "effects/spark_noz" );

	// Find a start & end point for the particle
	// Start particles straight ahead of the client
	Vector vecViewOrigin = MainViewOrigin(engine->GetActiveSplitScreenPlayerSlot());

	// Determine the number of particles
	m_flNumParticles += 1.0 * (m_flDensity);
	int iNumParticles = floor(m_flNumParticles);
	m_flNumParticles -= iNumParticles;

	// Add particles
	for ( int i = 0; i < iNumParticles; i++ )
	{
		float flDiameter = cl_starfield_diameter.GetFloat();

		Vector vecStart = vecViewOrigin + (MainViewForward(engine->GetActiveSplitScreenPlayerSlot()) * cl_starfield_distance.GetFloat() );
		Vector vecEnd = vecViewOrigin + (MainViewRight(engine->GetActiveSplitScreenPlayerSlot()) * RandomFloat(-flDiameter,flDiameter)) + (MainViewUp(engine->GetActiveSplitScreenPlayerSlot()) * RandomFloat(-flDiameter,flDiameter));
		Vector vecDir = (vecEnd - vecStart);
		float flDistance = VectorNormalize( vecDir );
		float flTravelTime = 2.0;
		
		// Start a random amount along the path
		vecStart += vecDir * ( RandomFloat(0.1,0.3) * flDistance );

		TrailParticle *pParticle = (TrailParticle *) m_pEmitter->AddParticle( sizeof(TrailParticle), hParticleMaterial, vecStart );
		if ( pParticle )
		{
			pParticle->m_vecVelocity = vecDir * (flDistance / flTravelTime);
			pParticle->m_flDieTime = flTravelTime;
			pParticle->m_flLifetime = 0;
			pParticle->m_flWidth = RandomFloat( 1, 3 );
			pParticle->m_flLength = RandomFloat( 0.05, 0.4 );
			pParticle->m_color.r	= 255;
			pParticle->m_color.g	= 255;
			pParticle->m_color.b	= 255;
			pParticle->m_color.a	= 255;
		}
	}
}
Exemplo n.º 17
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : updateType - 
//-----------------------------------------------------------------------------
void C_EnvStarfield::OnDataChanged( DataUpdateType_t updateType )
{		
	if ( updateType == DATA_UPDATE_CREATED )
	{
		m_pEmitter = CTrailParticles::Create( "EnvStarfield" );
		Vector vecCenter = MainViewOrigin(engine->GetActiveSplitScreenPlayerSlot()) + (MainViewForward(engine->GetActiveSplitScreenPlayerSlot()) * cl_starfield_distance.GetFloat() );
		m_pEmitter->Setup( (Vector &) vecCenter, 
			NULL, 
			0.0, 
			0, 
			64, 
			0, 
			0, 
			bitsPARTICLE_TRAIL_VELOCITY_DAMPEN | bitsPARTICLE_TRAIL_FADE | bitsPARTICLE_TRAIL_FADE_IN );

		// Start thinking
		SetNextClientThink( CLIENT_THINK_ALWAYS );
	}

	BaseClass::OnDataChanged( updateType );
}
Exemplo n.º 18
0
void CDistanceMeter::Paint( void )
{
    int nShowDist = cl_showdist.GetInt();

    if ( !nShowDist )
        return;

    BaseClass::Paint();

    int vx, vy, vw, vh;
    vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh );

    // Draw it to the left and above the center of the screen.
    int x = ( vw / 2 ) + 20;
    int y = ( vh / 2 ) - 20;

    // Calculate the distance from player's eyes to his aiming point.
    Vector vecSrc, vecDir, vecEnd;

    vecSrc = MainViewOrigin();
    vecDir = MainViewForward();

    C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();

    if ( nShowDist == 2 )
    {
        if ( pPlayer )
        {
            pPlayer->EyePositionAndVectors( &vecSrc, &vecDir, NULL, NULL );
        }
    }

    vecEnd = vecSrc + vecDir * MAX_TRACE_LENGTH;

    trace_t tr;
    UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
    vecDir = tr.endpos - vecSrc;

    g_pMatSystemSurface->DrawColoredText( m_hFont, x, y, 255, 255, 255, 255, "%.02f", vecDir.Length() );
}
Exemplo n.º 19
0
static void GetPos( const CCommand &args, Vector &vecOrigin, QAngle &angles )
{
	int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
	vecOrigin = MainViewOrigin(nSlot);
	angles = MainViewAngles(nSlot);

#ifdef INFESTED_DLL
	C_ASW_Marine *pMarine = C_ASW_Marine::GetLocalMarine();
	if ( pMarine )
	{
		vecOrigin = pMarine->GetAbsOrigin();
		angles = pMarine->GetAbsAngles();
	}
#endif
	if ( ( args.ArgC() == 2 && atoi( args[1] ) == 2 ) || FStrEq( args[0], "getpos_exact" ) )
	{
		C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
		if ( pPlayer )
		{
			vecOrigin = pPlayer->GetAbsOrigin();
			angles = pPlayer->GetAbsAngles();
		}
	}
}
Exemplo n.º 20
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void FX_TFTracerSound( const Vector &start, const Vector &end, int iTracerType )
{
	// don't play on very short hits
	if ( ( start - end ).Length() < 200 )
		return;
	
	const char *pszSoundName = "Bullets.DefaultNearmiss";
	float flWhizDist = 64;
	Vector vecListenOrigin = MainViewOrigin();

	switch( iTracerType )
	{
	case TRACER_TYPE_DEFAULT:
		flWhizDist = 96;
		// fall through !

	default:
		{
			Ray_t bullet, listener;
			bullet.Init( start, end );

			Vector vecLower = vecListenOrigin;
			vecLower.z -= LISTENER_HEIGHT;
			listener.Init( vecListenOrigin,	vecLower );

			float s, t;
			IntersectRayWithRay( bullet, listener, s, t );
			t = clamp( t, 0, 1 );
			vecListenOrigin.z -= t * LISTENER_HEIGHT;
		}
		break;
	}

	static float flNextWhizTime = 0;

	// Is it time yet?
	float dt = flNextWhizTime - gpGlobals->curtime;
	if ( dt > 0 )
		return;

	// Did the thing pass close enough to our head?
	float vDist = CalcDistanceSqrToLineSegment( vecListenOrigin, start, end );
	if ( vDist >= (flWhizDist * flWhizDist) )
		return;

	CSoundParameters params;
	if( C_BaseEntity::GetParametersForSound( pszSoundName, params, NULL ) )
	{
		// Get shot direction
		Vector shotDir;
		VectorSubtract( end, start, shotDir );
		VectorNormalize( shotDir );

		CLocalPlayerFilter filter;

		enginesound->EmitSound(	filter, SOUND_FROM_WORLD, CHAN_STATIC, params.soundname, 
			params.volume, SNDLVL_TO_ATTN(params.soundlevel), 0, params.pitch, 0, &start, &shotDir, false);
	}

	// Don't play another bullet whiz for this client until this time has run out
	flNextWhizTime = gpGlobals->curtime + 0.1f;
}
Exemplo n.º 21
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : timeDelta - 
//-----------------------------------------------------------------------------
void C_Flare::Update( float timeDelta )
{
	if ( !IsVisible() )
		return;

	CSimpleEmitter::Update( timeDelta );

	//Make sure our stored resources are up to date
	RestoreResources();

	//Don't do this if the console is down
	if ( timeDelta <= 0.0f )
		return;

	//Check for LOS
	pixelvis_queryparams_t params;
	params.Init(GetAbsOrigin());
	params.proxySize = 8.0f; // Inches
	
	float visible = PixelVisibility_FractionVisible( params, &m_queryHandle );

	float	fColor;
#ifdef HL2_CLIENT_DLL
	float	baseScale = m_flScale;
#else
	// NOTE!!!  This is bigger by a factor of 1.2 to deal with fixing a bug from HL2.  See dlight_t.h
	float	baseScale = m_flScale * 1.2f;
#endif

	//Account for fading out
	if ( ( m_flTimeBurnOut != -1.0f ) && ( ( m_flTimeBurnOut - gpGlobals->curtime ) <= 10.0f ) )
	{
		baseScale *= ( ( m_flTimeBurnOut - gpGlobals->curtime ) / 10.0f );
	}

	bool bVisible = (baseScale < 0.01f || visible == 0.0f) ? false : true;
	//Clamp the scale if vanished
	if ( !bVisible )
	{
		if ( m_pParticle[0] != NULL )
		{	
			m_pParticle[0]->m_flDieTime		= gpGlobals->curtime;
			m_pParticle[0]->m_uchStartSize	= m_pParticle[0]->m_uchEndSize = 0;
			m_pParticle[0]->m_uchColor[0]	= 0;
			m_pParticle[0]->m_uchColor[1]	= 0;
			m_pParticle[0]->m_uchColor[2]	= 0;
		}

		if ( m_pParticle[1] != NULL )
		{	
			m_pParticle[1]->m_flDieTime		= gpGlobals->curtime;
			m_pParticle[1]->m_uchStartSize	= m_pParticle[1]->m_uchEndSize = 0;
			m_pParticle[1]->m_uchColor[0]	= 0;
			m_pParticle[1]->m_uchColor[1]	= 0;
			m_pParticle[1]->m_uchColor[2]	= 0;
		}
	}

	if ( baseScale < 0.01f )
		return;
	//
	// Dynamic light
	//

	if ( m_bLight )
	{
		dlight_t *dl= effects->CL_AllocDlight( index );

		

		if ( m_bPropFlare == false )
		{
			dl->origin	= GetAbsOrigin();
			dl->color.r = 255;
			dl->die		= gpGlobals->curtime + 0.1f;

			dl->radius	= baseScale * random->RandomFloat( 110.0f, 128.0f );
			dl->color.g = dl->color.b = random->RandomInt( 32, 64 );
		}
		else
		{
			if ( m_iAttachment == -1 )
			{
				m_iAttachment =  LookupAttachment( "fuse" );
			}

			if ( m_iAttachment != -1 )
			{
				Vector effect_origin;
				QAngle effect_angles;
				
				GetAttachment( m_iAttachment, effect_origin, effect_angles );

				//Raise the light a little bit away from the flare so it lights it up better.
				dl->origin	= effect_origin + Vector( 0, 0, 4 );
				dl->color.r = 255;
				dl->die		= gpGlobals->curtime + 0.1f;

				dl->radius	= baseScale * random->RandomFloat( 245.0f, 256.0f );
				dl->color.g = dl->color.b = random->RandomInt( 95, 128 );
		
				dlight_t *el= effects->CL_AllocElight( index );

				el->origin	= effect_origin;
				el->color.r = 255;
				el->color.g = dl->color.b = random->RandomInt( 95, 128 );
				el->radius	= baseScale * random->RandomFloat( 260.0f, 290.0f );
				el->die		= gpGlobals->curtime + 0.1f;
			}
		}
	}

	//
	// Smoke
	//

	float dt = timeDelta;

	if ( m_bSmoke )
	{
		while ( m_teSmokeSpawn.NextEvent( dt ) )
		{
			Vector	smokeOrg = GetAbsOrigin();

			Vector	flareScreenDir = ( smokeOrg - MainViewOrigin() );
			VectorNormalize( flareScreenDir );

			smokeOrg = smokeOrg + ( flareScreenDir * 2.0f );
			smokeOrg[2] += baseScale * 4.0f;

			SimpleParticle *sParticle = (SimpleParticle *) AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[1], smokeOrg );
				
			if ( sParticle == NULL )
				return;

			sParticle->m_flLifetime		= 0.0f;
			sParticle->m_flDieTime		= 1.0f;
			
			sParticle->m_vecVelocity	= Vector( random->RandomFloat( -16.0f, 16.0f ), random->RandomFloat( -16.0f, 16.0f ), random->RandomFloat( 8.0f, 16.0f ) + 32.0f );

			if ( m_bPropFlare )
			{
				sParticle->m_uchColor[0]	= 255;
				sParticle->m_uchColor[1]	= 100;
				sParticle->m_uchColor[2]	= 100;
			}
			else
			{
				sParticle->m_uchColor[0]	= 255;
				sParticle->m_uchColor[1]	= 48;
				sParticle->m_uchColor[2]	= 48;
			}

			sParticle->m_uchStartAlpha	= random->RandomInt( 64, 90 );
			sParticle->m_uchEndAlpha	= 0;
			sParticle->m_uchStartSize	= random->RandomInt( 2, 4 );
			sParticle->m_uchEndSize		= sParticle->m_uchStartSize * 8.0f;
			sParticle->m_flRoll			= random->RandomInt( 0, 2*M_PI );
			sParticle->m_flRollDelta	= random->RandomFloat( -(M_PI/6.0f), M_PI/6.0f );
		}
	}

	if ( !bVisible )
		return;

	//
	// Outer glow
	//

	Vector	offset;
	
	//Cause the base of the effect to shake
	offset.Random( -0.5f * baseScale, 0.5f * baseScale );
	offset += GetAbsOrigin();

	if ( m_pParticle[0] != NULL )
	{
		m_pParticle[0]->m_Pos			= offset;
		m_pParticle[0]->m_flLifetime	= 0.0f;
		m_pParticle[0]->m_flDieTime		= 2.0f;
		
		m_pParticle[0]->m_vecVelocity.Init();

		fColor = random->RandomInt( 100.0f, 128.0f ) * visible;

		m_pParticle[0]->m_uchColor[0]	= fColor;
		m_pParticle[0]->m_uchColor[1]	= fColor;
		m_pParticle[0]->m_uchColor[2]	= fColor;
		m_pParticle[0]->m_uchStartAlpha	= fColor;
		m_pParticle[0]->m_uchEndAlpha	= fColor;
		m_pParticle[0]->m_uchStartSize	= baseScale * (float) random->RandomInt( 32, 48 );
		m_pParticle[0]->m_uchEndSize	= m_pParticle[0]->m_uchStartSize;
		m_pParticle[0]->m_flRollDelta	= 0.0f;
		
		if ( random->RandomInt( 0, 4 ) == 3 )
		{
			m_pParticle[0]->m_flRoll	+= random->RandomInt( 2, 8 );
		}
	}

	//
	// Inner core
	//

	//Cause the base of the effect to shake
	offset.Random( -1.0f * baseScale, 1.0f * baseScale );
	offset += GetAbsOrigin();

	if ( m_pParticle[1] != NULL )
	{
		m_pParticle[1]->m_Pos			= offset;
		m_pParticle[1]->m_flLifetime	= 0.0f;
		m_pParticle[1]->m_flDieTime		= 2.0f;
		
		m_pParticle[1]->m_vecVelocity.Init();

		fColor = 255 * visible;

		m_pParticle[1]->m_uchColor[0]	= fColor;
		m_pParticle[1]->m_uchColor[1]	= fColor;
		m_pParticle[1]->m_uchColor[2]	= fColor;
		m_pParticle[1]->m_uchStartAlpha	= fColor;
		m_pParticle[1]->m_uchEndAlpha	= fColor;
		m_pParticle[1]->m_uchStartSize	= baseScale * (float) random->RandomInt( 2, 4 );
		m_pParticle[1]->m_uchEndSize	= m_pParticle[0]->m_uchStartSize;
		m_pParticle[1]->m_flRoll		= random->RandomInt( 0, 360 );
	}
}
void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType )
{
	const char *pszSoundName = NULL;
	float flWhizDist = TRACER_MAX_HEAR_DIST;
	float flMinWhizTime = TRACER_SOUND_TIME_MIN;
	float flMaxWhizTime = TRACER_SOUND_TIME_MAX;
	Vector vecListenOrigin = MainViewOrigin();
	switch( iTracerType )
	{
	case TRACER_TYPE_DEFAULT:
		{
			pszSoundName = "Bullets.DefaultNearmiss";
			flWhizDist = 24;

			Ray_t bullet, listener;
			bullet.Init( start, end );

			Vector vecLower = vecListenOrigin;
			vecLower.z -= LISTENER_HEIGHT;
			listener.Init( vecListenOrigin,	vecLower );

			float s, t;
			IntersectRayWithRay( bullet, listener, s, t );
			t = clamp( t, 0, 1 );
			vecListenOrigin.z -= t * LISTENER_HEIGHT;
		}
		break;

	case TRACER_TYPE_GUNSHIP:
		pszSoundName = "Bullets.GunshipNearmiss";
		break;

	case TRACER_TYPE_STRIDER:
		pszSoundName = "Bullets.StriderNearmiss";
		break;

	case TRACER_TYPE_WATERBULLET:
		pszSoundName = "Underwater.BulletImpact";
		flWhizDist = 48;
		flMinWhizTime = 0.3f;
		flMaxWhizTime = 0.6f;
		break;

	default:
		return;
	}

	if( !pszSoundName )
		return;

	// Is it time yet?
	float dt = g_BulletWhiz.m_nextWhizTime - gpGlobals->curtime;
	if ( dt > 0 )
		return;

	// Did the thing pass close enough to our head?
	float vDist = CalcDistanceSqrToLineSegment( vecListenOrigin, start, end );
	if ( vDist >= (flWhizDist * flWhizDist) )
		return;

	CSoundParameters params;
	if( C_BaseEntity::GetParametersForSound( pszSoundName, params, NULL ) )
	{
		// Get shot direction
		Vector shotDir;
		VectorSubtract( end, start, shotDir );
		VectorNormalize( shotDir );

		CLocalPlayerFilter filter;
		enginesound->EmitSound(	filter, SOUND_FROM_WORLD, CHAN_STATIC, params.soundname, 
			params.volume, SNDLVL_TO_ATTN(params.soundlevel), 0, params.pitch, &start, &shotDir, false);
	}

	// FIXME: This has a bad behavior when both bullet + strider shots are whizzing by at the same time
	// Could use different timers for the different types.

	// Don't play another bullet whiz for this client until this time has run out
	g_BulletWhiz.m_nextWhizTime = gpGlobals->curtime + random->RandomFloat( flMinWhizTime, flMaxWhizTime );
}
Exemplo n.º 23
0
//-----------------------------------------------------------------------------
// Purpose: Intercepts the blood spray message.
//-----------------------------------------------------------------------------
void TFBloodSprayCallback( Vector vecOrigin, Vector vecNormal, ClientEntityHandle_t hEntity )
{
	QAngle	vecAngles;
	VectorAngles( -vecNormal, vecAngles );

	// determine if the bleeding player is underwater
	bool bUnderwater = false;
	C_TFPlayer *pPlayer = dynamic_cast<C_TFPlayer*>( ClientEntityList().GetBaseEntityFromHandle( hEntity ) );
	if ( pPlayer && ( WL_Eyes == pPlayer->GetWaterLevel() )	)
	{
		bUnderwater = true;
	}
	 
	if ( !bUnderwater && TFGameRules() && TFGameRules()->IsBirthday() && RandomFloat(0,1) < 0.2 )
	{
		DispatchParticleEffect( "bday_blood", vecOrigin, vecAngles, pPlayer );
	}
	else
	{
		DispatchParticleEffect( bUnderwater ? "water_blood_impact_red_01" : "blood_impact_red_01", vecOrigin, vecAngles, pPlayer );
	}

	// if underwater, don't add additional spray
	if ( bUnderwater )
		return;

	// Now throw out a spray away from the view
	// Get the distance to the view
	float flDistance = (vecOrigin - MainViewOrigin()).Length();
	float flLODDistance = 0.25 * (flDistance / 512);

	Vector right, up;
	if (vecNormal != Vector(0, 0, 1) )
	{
		right = vecNormal.Cross( Vector(0, 0, 1) );
		up = right.Cross( vecNormal );
	}
	else
	{
		right = Vector(0, 0, 1);
		up = right.Cross( vecNormal );
	}

	// If the normal's too close to being along the view, push it out
	Vector vecForward, vecRight;
	AngleVectors( MainViewAngles(), &vecForward, &vecRight, NULL );
	float flDot = DotProduct( vecNormal, vecForward );
	if ( fabs(flDot) > 0.5 )
	{
		float flPush = random->RandomFloat(0.5, 1.5) + flLODDistance;
		float flRightDot = DotProduct( vecNormal, vecRight );

		// If we're up close, randomly move it around. If we're at a distance, always push it to the side
		// Up close, this can move it back towards the view, but the random chance still looks better
		if ( ( flDistance >= 512 && flRightDot > 0 ) || ( flDistance < 512 && RandomFloat(0,1) > 0.5 ) )
		{
			// Turn it to the right
			vecNormal += (vecRight * flPush);
		}
		else
		{
			// Turn it to the left
			vecNormal -= (vecRight * flPush);
		}
	}

	VectorAngles( vecNormal, vecAngles );

	if ( flDistance < 400 )
	{
		DispatchParticleEffect( "blood_spray_red_01", vecOrigin, vecAngles, pPlayer );
	}
	else
	{
		DispatchParticleEffect( "blood_spray_red_01_far", vecOrigin, vecAngles, pPlayer );
	}
}
Exemplo n.º 24
0
void C_FuncSmokeVolume::Update( float fTimeDelta )
{
	// Update our world space bbox if we've moved at all.
	// We do this manually because sometimes people make HUGE bboxes, and if they're constantly changing because their
	// particles wander outside the current bounds sometimes, it'll be linking them into all the leaves repeatedly.
	const Vector &curOrigin = GetAbsOrigin();
	const QAngle &curAngles = GetAbsAngles();
	if ( !VectorsAreEqual( curOrigin, m_vLastOrigin, 0.1 ) || 
		fabs( curAngles.x - m_vLastAngles.x ) > 0.1 || 
		fabs( curAngles.y - m_vLastAngles.y ) > 0.1 || 
		fabs( curAngles.z - m_vLastAngles.z ) > 0.1 ||
		m_bFirstUpdate )
	{
		m_bFirstUpdate = false;
		m_vLastAngles = curAngles;
		m_vLastOrigin = curOrigin;

		Vector vWorldMins, vWorldMaxs;
		CollisionProp()->WorldSpaceAABB( &vWorldMins, &vWorldMaxs );
		vWorldMins -= Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius );
		vWorldMaxs += Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius );

		m_ParticleEffect.SetBBox( vWorldMins, vWorldMaxs );
	}
		
	float minViewOrigingDistance = FLT_MAX;
	FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
	{
		float d = CollisionProp()->CalcDistanceFromPoint( MainViewOrigin(hh) );
		if ( d < minViewOrigingDistance )
			minViewOrigingDistance = d;
	}


	float targetDensity = m_Density;
	if ( m_maxDrawDistance > 0 && minViewOrigingDistance > m_maxDrawDistance )
	{
		targetDensity = 0.0f;
	}

	// lerp m_CurrentDensity towards m_Density at a rate of m_DensityRampSpeed
	if( m_CurrentDensity < targetDensity )
	{
		m_CurrentDensity += m_DensityRampSpeed * fTimeDelta;
		if( m_CurrentDensity > targetDensity )
		{
			m_CurrentDensity = targetDensity;
		}
	}
	else if( m_CurrentDensity > targetDensity )
	{
		m_CurrentDensity -= m_DensityRampSpeed * fTimeDelta;
		if( m_CurrentDensity < targetDensity )
		{
			m_CurrentDensity = targetDensity;
		}
	}

	if( m_CurrentDensity == 0.0f )
	{
		return;
	}
	
	// This is used to randomize the direction it chooses to move a particle in.

	int offsetLookup[3] = {-1,0,1};

	float tradeDurationMax = m_ParticleSpacingDistance / ( m_MovementSpeed + 0.1f );
	float tradeDurationMin = tradeDurationMax * 0.5f;

	if ( IS_NAN( tradeDurationMax ) || IS_NAN( tradeDurationMin ) )
		return;

//	Warning( "tradeDuration: [%f,%f]\n", tradeDurationMin, tradeDurationMax );
	
	// Update all the moving traders and establish new ones.
	int nTotal = m_xCount * m_yCount * m_zCount;
	for( int i=0; i < nTotal; i++ )
	{
		SmokeParticleInfo *pInfo = &m_pSmokeParticleInfos[i];

		if(!pInfo->m_pParticle)
			continue;
	
		if(pInfo->m_TradeIndex == -1)
		{
			pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha;
			pInfo->m_pParticle->m_Color[0] = pInfo->m_Color[0];
			pInfo->m_pParticle->m_Color[1] = pInfo->m_Color[1];
			pInfo->m_pParticle->m_Color[2] = pInfo->m_Color[2];

			// Is there an adjacent one that's not trading?
			int x, y, z;
			GetParticleInfoXYZ(i, x, y, z);

			int xCountOffset = rand();
			int yCountOffset = rand();
			int zCountOffset = rand();

			bool bFound = false;
			for(int xCount=0; xCount < 3 && !bFound; xCount++)
			{
				for(int yCount=0; yCount < 3 && !bFound; yCount++)
				{
					for(int zCount=0; zCount < 3; zCount++)
					{
						int testX = x + offsetLookup[(xCount+xCountOffset) % 3];
						int testY = y + offsetLookup[(yCount+yCountOffset) % 3];
						int testZ = z + offsetLookup[(zCount+zCountOffset) % 3];

						if(testX == x && testY == y && testZ == z)
							continue;

						if(IsValidXYZCoords(testX, testY, testZ))
						{
							SmokeParticleInfo *pOther = GetSmokeParticleInfo(testX, testY, testZ);
							if(pOther->m_pParticle && pOther->m_TradeIndex == -1)
							{
								// Ok, this one is looking to trade also.
								pInfo->m_TradeIndex = GetSmokeParticleIndex(testX, testY, testZ);
								pOther->m_TradeIndex = i;
								pInfo->m_TradeClock = pOther->m_TradeClock = 0;
								pOther->m_TradeDuration = pInfo->m_TradeDuration = FRand( tradeDurationMin, tradeDurationMax );
								
								bFound = true;
								break;
							}
						}
					}
				}
			}
		}
		else
		{
			SmokeParticleInfo *pOther = &m_pSmokeParticleInfos[pInfo->m_TradeIndex];
			assert(pOther->m_TradeIndex == i);
			
			// This makes sure the trade only gets updated once per frame.
			if(pInfo < pOther)
			{
				// Increment the trade clock..
				pInfo->m_TradeClock = (pOther->m_TradeClock += fTimeDelta);
				int x, y, z;
				GetParticleInfoXYZ(i, x, y, z);
				Vector myPos = GetSmokeParticlePos(x, y, z);
				
				int otherX, otherY, otherZ;
				GetParticleInfoXYZ(pInfo->m_TradeIndex, otherX, otherY, otherZ);
				Vector otherPos = GetSmokeParticlePos(otherX, otherY, otherZ);

				// Is the trade finished?
				if(pInfo->m_TradeClock >= pInfo->m_TradeDuration)
				{
					pInfo->m_TradeIndex = pOther->m_TradeIndex = -1;
					
					pInfo->m_pParticle->m_Pos = otherPos;
					pOther->m_pParticle->m_Pos = myPos;

					SmokeGrenadeParticle *temp = pInfo->m_pParticle;
					pInfo->m_pParticle = pOther->m_pParticle;
					pOther->m_pParticle = temp;
				}
				else
				{			
					// Ok, move them closer.
					float percent = (float)cos(pInfo->m_TradeClock * 2 * 1.57079632f / pInfo->m_TradeDuration);
					percent = percent * 0.5 + 0.5;
					
					pInfo->m_pParticle->m_FadeAlpha  = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * (1 - percent);
					pOther->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * percent;

					InterpColor(pInfo->m_pParticle->m_Color,  pInfo->m_Color, pOther->m_Color, 1-percent);
					InterpColor(pOther->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, percent);

					pInfo->m_pParticle->m_Pos  = myPos + (otherPos - myPos) * (1 - percent);
					pOther->m_pParticle->m_Pos = myPos + (otherPos - myPos) * percent;
				}
			}
		}
	}
}
Exemplo n.º 25
0
void CMumbleSystem::PostRender()
{
#ifndef NO_STEAM
	if ( !g_pMumbleMemory || !sv_mumble_positionalaudio.GetBool() )
		return;

	if ( g_pMumbleMemory->uiVersion != 2 )
	{
		V_wcscpy_safe( g_pMumbleMemory->name, L"Source engine: " );
		wchar_t wcsGameDir[MAX_PATH];
		Q_UTF8ToUnicode( COM_GetModDirectory(), wcsGameDir, sizeof(wcsGameDir) );
		V_wcscat_safe( g_pMumbleMemory->name, wcsGameDir );

		V_wcscpy_safe( g_pMumbleMemory->description, L"Links Source engine games to Mumble." );
		g_pMumbleMemory->uiVersion = 2;
	}

	g_pMumbleMemory->uiTick++;

	Vector vecOriginPlayer, vecOriginCamera = MainViewOrigin();
	QAngle anglesPlayer, anglesCamera = MainViewAngles();

	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
	if ( pPlayer )
	{
		vecOriginPlayer = pPlayer->EyePosition();
		anglesPlayer = pPlayer->GetAbsAngles();
	}
	else
	{
		vecOriginPlayer = vecOriginCamera;
		anglesPlayer = anglesCamera;
	}

	anglesPlayer.x = 0;

	Vector vecPlayerForward, vecPlayerUp, vecCameraForward, vecCameraUp;
	AngleVectors( anglesPlayer, &vecPlayerForward, NULL, &vecPlayerUp );
	AngleVectors( anglesCamera, &vecCameraForward, NULL, &vecCameraUp );

	// 1 Source unit is about one inch
	// 1 mumble unit = 1 meter
	vecOriginPlayer *= METERS_PER_INCH;
	vecOriginCamera *= METERS_PER_INCH;

	VectorToMumbleFloatArray( vecPlayerForward, g_pMumbleMemory->fAvatarFront );
	VectorToMumbleFloatArray( vecPlayerUp, g_pMumbleMemory->fAvatarTop );
	VectorToMumbleFloatArray( vecOriginPlayer, g_pMumbleMemory->fAvatarPosition );

	VectorToMumbleFloatArray( vecCameraForward, g_pMumbleMemory->fCameraFront );
	VectorToMumbleFloatArray( vecCameraUp, g_pMumbleMemory->fCameraTop );
	VectorToMumbleFloatArray( vecOriginCamera, g_pMumbleMemory->fCameraPosition );

	if ( pPlayer && m_bHasSetPlayerUniqueId && m_nTeamSetInUniqueId != pPlayer->GetTeamNumber() )
	{
		// Player changed team since we set the unique ID. Set it again.
		m_bHasSetPlayerUniqueId = false;
	}

	if ( !m_bHasSetPlayerUniqueId && steamapicontext && steamapicontext->SteamUser() )
	{
		CSteamID steamid = steamapicontext->SteamUser()->GetSteamID();
		if ( steamid.IsValid() )
		{
			int unTeam = pPlayer ? pPlayer->GetTeamNumber() : 0;
			char szSteamId[256];
			V_sprintf_safe( szSteamId, "universe:%u;account_type:%u;id:%u;instance:%u;team:%d", steamid.GetEUniverse(), steamid.GetEAccountType(), steamid.GetAccountID(), steamid.GetUnAccountInstance(), unTeam );

			wchar_t wcsSteamId[256];
			Q_UTF8ToUnicode( szSteamId, wcsSteamId, sizeof(wcsSteamId) );

			// Identifier which uniquely identifies a certain player in a context.
			V_wcscpy_safe( g_pMumbleMemory->identity, wcsSteamId );

			m_bHasSetPlayerUniqueId = true;
			m_nTeamSetInUniqueId = unTeam;
		}
	}

	// Context should be equal for players which should be able to hear each other positional and
	// differ for those who shouldn't (e.g. it could contain the server+port and team)
	memcpy( g_pMumbleMemory->context, &m_szSteamIDCurrentServer, m_cubSteamIDCurrentServer );
	g_pMumbleMemory->context_len = m_cubSteamIDCurrentServer;
#endif // NO_STEAM
}