//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "env_headcrabcanister_shared.h" #include "mapdata_shared.h" #include "sharedInterface.h" #include "mathlib/vmatrix.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define ROTATION_SPEED 90.0f BEGIN_SIMPLE_DATADESC( CEnvHeadcrabCanisterShared ) DEFINE_FIELD( m_vecStartPosition, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_vecEnterWorldPosition, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_vecDirection, FIELD_VECTOR ), DEFINE_FIELD( m_vecStartAngles, FIELD_VECTOR ), DEFINE_KEYFIELD( m_flLaunchHeight, FIELD_FLOAT, "StartingHeight" ), DEFINE_KEYFIELD( m_flFlightSpeed, FIELD_FLOAT, "FlightSpeed" ), DEFINE_KEYFIELD( m_flFlightTime, FIELD_FLOAT, "FlightTime" ), DEFINE_FIELD( m_flLaunchTime, FIELD_TIME ), DEFINE_FIELD( m_flWorldEnterTime, FIELD_FLOAT ), DEFINE_FIELD( m_flInitialZSpeed, FIELD_FLOAT ), DEFINE_FIELD( m_flZAcceleration, FIELD_FLOAT ), DEFINE_FIELD( m_flHorizSpeed, FIELD_FLOAT ), DEFINE_FIELD( m_bLaunchedFromWithinWorld, FIELD_BOOLEAN ), DEFINE_FIELD( m_vecSkyboxOrigin, FIELD_VECTOR ), DEFINE_FIELD( m_vecParabolaDirection, FIELD_VECTOR ), DEFINE_FIELD( m_flSkyboxScale, FIELD_FLOAT ), DEFINE_FIELD( m_bInSkybox, FIELD_BOOLEAN ), END_DATADESC() BEGIN_NETWORK_TABLE_NOBASE( CEnvHeadcrabCanisterShared, DT_EnvHeadcrabCanisterShared ) #if !defined( CLIENT_DLL ) SendPropFloat ( SENDINFO( m_flFlightSpeed ), 0, SPROP_NOSCALE ), SendPropTime ( SENDINFO( m_flLaunchTime ) ), SendPropVector ( SENDINFO( m_vecParabolaDirection ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flFlightTime ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flWorldEnterTime ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flInitialZSpeed ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flZAcceleration ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flHorizSpeed ), 0, SPROP_NOSCALE ), SendPropBool ( SENDINFO( m_bLaunchedFromWithinWorld ) ), SendPropVector ( SENDINFO( m_vecStartPosition ), 0, SPROP_NOSCALE ), SendPropVector ( SENDINFO( m_vecEnterWorldPosition ), 0, SPROP_NOSCALE ), SendPropVector ( SENDINFO( m_vecDirection ), 0, SPROP_NOSCALE ), SendPropVector ( SENDINFO( m_vecStartAngles ), 0, SPROP_NOSCALE ), SendPropVector ( SENDINFO( m_vecSkyboxOrigin ), 0, SPROP_NOSCALE ), SendPropFloat ( SENDINFO( m_flSkyboxScale ), 0, SPROP_NOSCALE ), SendPropBool ( SENDINFO( m_bInSkybox ) ), #else RecvPropFloat ( RECVINFO( m_flFlightSpeed ) ), RecvPropTime ( RECVINFO( m_flLaunchTime ) ), RecvPropVector ( RECVINFO( m_vecParabolaDirection ) ), RecvPropFloat ( RECVINFO( m_flFlightTime ) ), RecvPropFloat ( RECVINFO( m_flWorldEnterTime ) ), RecvPropFloat ( RECVINFO( m_flInitialZSpeed ) ), RecvPropFloat ( RECVINFO( m_flZAcceleration ) ), RecvPropFloat ( RECVINFO( m_flHorizSpeed ) ), RecvPropBool ( RECVINFO( m_bLaunchedFromWithinWorld ) ), RecvPropVector ( RECVINFO( m_vecStartPosition ) ), RecvPropVector ( RECVINFO( m_vecEnterWorldPosition ) ), RecvPropVector ( RECVINFO( m_vecDirection ) ), RecvPropVector ( RECVINFO( m_vecStartAngles ) ), RecvPropVector ( RECVINFO( m_vecSkyboxOrigin ) ), RecvPropFloat ( RECVINFO( m_flSkyboxScale ) ), RecvPropBool ( RECVINFO( m_bInSkybox ) ), #endif END_NETWORK_TABLE() //============================================================================= // // HeadcrabCanister Functions. // //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CEnvHeadcrabCanisterShared::CEnvHeadcrabCanisterShared() { m_vecStartPosition.Init(); m_vecDirection.Init(); m_flFlightSpeed = 0.0f; // This tells the client DLL to not draw trails, etc. m_flLaunchTime = -1.0f; m_flWorldEnterTime = 0.0f; m_flFlightTime = 0.0f; m_bInSkybox = false; } //----------------------------------------------------------------------------- // Creates a headcrab canister in the world //----------------------------------------------------------------------------- void CEnvHeadcrabCanisterShared::InitInWorld( float flLaunchTime, const Vector &vecStartPosition, const QAngle &vecStartAngles, const Vector &vecDirection, const Vector &vecImpactPosition, bool bLaunchedFromWithinWorld ) { Vector vecActualStartPosition = vecStartPosition; if ( !bLaunchedFromWithinWorld ) { // Move the start position inward if it's too close Vector vecDelta; VectorSubtract( vecStartPosition, vecImpactPosition, vecDelta ); VectorNormalize( vecDelta ); VectorMA( vecImpactPosition, m_flFlightTime * m_flFlightSpeed, vecDelta, vecActualStartPosition ); } // Setup initial parametric state. m_flLaunchTime = flLaunchTime; m_vecStartPosition = vecActualStartPosition; m_vecEnterWorldPosition = vecActualStartPosition; m_vecDirection = vecDirection; m_vecStartAngles = vecStartAngles; m_flWorldEnterTime = 0.0f; m_bInSkybox = false; m_bLaunchedFromWithinWorld = bLaunchedFromWithinWorld; if ( m_bLaunchedFromWithinWorld ) { m_flSkyboxScale = 1; m_vecSkyboxOrigin = vec3_origin; float flLength = m_vecDirection.Get().AsVector2D().Length(); VectorSubtract(vecImpactPosition, vecStartPosition, m_vecParabolaDirection.GetForModify()); m_vecParabolaDirection.GetForModify().z = 0; float flTotalDistance = VectorNormalize( m_vecParabolaDirection.GetForModify() ); m_vecDirection.GetForModify().x = flLength * m_vecParabolaDirection.Get().x; m_vecDirection.GetForModify().y = flLength * m_vecParabolaDirection.Get().y; m_flHorizSpeed = flTotalDistance / m_flFlightTime; m_flWorldEnterTime = 0; float flFinalZSpeed = m_vecDirection.Get().z * m_flHorizSpeed; m_flFlightSpeed = sqrt( m_flHorizSpeed * m_flHorizSpeed + flFinalZSpeed * flFinalZSpeed ); m_flInitialZSpeed = (2.0f * ( vecImpactPosition.z - vecStartPosition.z ) - flFinalZSpeed * m_flFlightTime) / m_flFlightTime; m_flZAcceleration = (flFinalZSpeed - m_flInitialZSpeed) / m_flFlightTime; } } //----------------------------------------------------------------------------- // Creates a headcrab canister in the skybox //----------------------------------------------------------------------------- void CEnvHeadcrabCanisterShared::InitInSkybox( float flLaunchTime, const Vector &vecStartPosition, const QAngle &vecStartAngles, const Vector &vecDirection, const Vector &vecImpactPosition, const Vector &vecSkyboxOrigin, float flSkyboxScale ) { // Compute a horizontal speed (constant) m_vecParabolaDirection.Init( vecDirection.x, vecDirection.y, 0.0f ); float flLength = VectorNormalize( m_vecParabolaDirection.GetForModify() ); m_flHorizSpeed = flLength * m_flFlightSpeed; // compute total distance to travel float flTotalDistance = m_flFlightTime * m_flHorizSpeed; flTotalDistance -= vecStartPosition.AsVector2D().DistTo( vecImpactPosition.AsVector2D() ); if ( flTotalDistance <= 0.0f ) { InitInWorld( flLaunchTime, vecStartPosition, vecStartAngles, vecDirection, vecImpactPosition ); return; } // Setup initial parametric state. m_flLaunchTime = flLaunchTime; m_flWorldEnterTime = flTotalDistance / m_flHorizSpeed; m_vecSkyboxOrigin = vecSkyboxOrigin; m_flSkyboxScale = flSkyboxScale; m_vecEnterWorldPosition = vecStartPosition; m_vecDirection = vecDirection; m_vecStartAngles = vecStartAngles; m_bInSkybox = true; m_bLaunchedFromWithinWorld = false; // Compute parabolic course // Assume the x velocity remains constant. // Z moves ballistically, as if under gravity // zf + lh = zo // vf = vo + a*t // zf = zo + vo*t + 0.5 * a * t*t // a*t = vf - vo // zf = zo + vo*t + 0.5f * (vf - vo) * t // zf - zo = 0.5f *vo*t + 0.5f * vf * t // -lh - 0.5f * vf * t = 0.5f * vo * t // vo = -2.0f * lh / t - vf // a = (vf - vo) / t m_flHorizSpeed /= flSkyboxScale; VectorMA( vecSkyboxOrigin, 1.0f / m_flSkyboxScale, vecStartPosition, m_vecStartPosition.GetForModify() ); VectorMA( m_vecStartPosition.Get(), -m_flHorizSpeed * m_flWorldEnterTime, m_vecParabolaDirection, m_vecStartPosition.GetForModify() ); float flLaunchHeight = m_flLaunchHeight / flSkyboxScale; float flFinalZSpeed = m_vecDirection.Get().z * m_flFlightSpeed / flSkyboxScale; m_vecStartPosition.GetForModify().z += flLaunchHeight; m_flZAcceleration = 2.0f * ( flLaunchHeight + flFinalZSpeed * m_flWorldEnterTime ) / ( m_flWorldEnterTime * m_flWorldEnterTime ); m_flInitialZSpeed = flFinalZSpeed - m_flZAcceleration * m_flWorldEnterTime; } //----------------------------------------------------------------------------- // Convert from skybox to world //----------------------------------------------------------------------------- void CEnvHeadcrabCanisterShared::ConvertFromSkyboxToWorld() { Assert( m_bInSkybox ); m_bInSkybox = false; } //----------------------------------------------------------------------------- // Returns the time at which it enters the world //----------------------------------------------------------------------------- float CEnvHeadcrabCanisterShared::GetEnterWorldTime() const { return m_flWorldEnterTime; } //----------------------------------------------------------------------------- // Did we impact? //----------------------------------------------------------------------------- bool CEnvHeadcrabCanisterShared::DidImpact( float flTime ) const { return (flTime - m_flLaunchTime) >= m_flFlightTime; } //----------------------------------------------------------------------------- // Computes the position of the canister //----------------------------------------------------------------------------- void CEnvHeadcrabCanisterShared::GetPositionAtTime( float flTime, Vector &vecPosition, QAngle &vecAngles ) { float flDeltaTime = flTime - m_flLaunchTime; if ( flDeltaTime > m_flFlightTime ) { flDeltaTime = m_flFlightTime; } VMatrix initToWorld; if ( m_bLaunchedFromWithinWorld || m_bInSkybox ) { VectorMA( m_vecStartPosition, flDeltaTime * m_flHorizSpeed, m_vecParabolaDirection, vecPosition ); vecPosition.z += m_flInitialZSpeed * flDeltaTime + 0.5f * m_flZAcceleration * flDeltaTime * flDeltaTime; Vector vecLeft; CrossProduct( m_vecParabolaDirection, Vector( 0, 0, 1 ), vecLeft ); Vector vecForward; VectorMultiply( m_vecParabolaDirection, -1.0f, vecForward ); vecForward.z = -(m_flInitialZSpeed + m_flZAcceleration * flDeltaTime) / m_flHorizSpeed; // This is -dz/dx. VectorNormalize( vecForward ); Vector vecUp; CrossProduct( vecForward, vecLeft, vecUp ); initToWorld.SetBasisVectors( vecForward, vecLeft, vecUp ); } else { flDeltaTime -= m_flWorldEnterTime; Vector vecVelocity; VectorMultiply( m_vecDirection, m_flFlightSpeed, vecVelocity ); VectorMA( m_vecEnterWorldPosition, flDeltaTime, vecVelocity, vecPosition ); MatrixFromAngles( m_vecStartAngles.Get(), initToWorld ); } VMatrix rotation; MatrixBuildRotationAboutAxis( rotation, Vector( 1, 0, 0 ), flDeltaTime * ROTATION_SPEED ); VMatrix newAngles; MatrixMultiply( initToWorld, rotation, newAngles ); MatrixToAngles( newAngles, vecAngles ); } //----------------------------------------------------------------------------- // Are we in the skybox? //----------------------------------------------------------------------------- bool CEnvHeadcrabCanisterShared::IsInSkybox( ) { // Check to see if we are always in the world! return m_bInSkybox; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CEnvHeadcrabCanisterShared::CalcEnterTime( const Vector &vecTriggerMins, const Vector &vecTriggerMaxs ) { /* #define HEADCRABCANISTER_TRIGGER_EPSILON 0.001f // Initialize the enter/exit fractions. float flEnterFrac = 0.0f; float flExitFrac = 1.0f; // Create an arbitrarily large end position. Vector vecEndPosition; VectorMA( m_vecStartPosition, 32000.0f, m_vecDirection, vecEndPosition ); float flFrac, flDistStart, flDistEnd; for( int iAxis = 0; iAxis < 3; iAxis++ ) { // Negative Axis flDistStart = -m_vecStartPosition[iAxis] + vecTriggerMins[iAxis]; flDistEnd = -vecEndPosition[iAxis] + vecTriggerMins[iAxis]; if ( ( flDistStart > 0.0f ) && ( flDistEnd < 0.0f ) ) { flFrac = ( flDistStart - HEADCRABCANISTER_TRIGGER_EPSILON ) / ( flDistStart - flDistEnd ); if ( flFrac > flEnterFrac ) { flEnterFrac = flFrac; } } if ( ( flDistStart < 0.0f ) && ( flDistEnd > 0.0f ) ) { flFrac = ( flDistStart + HEADCRABCANISTER_TRIGGER_EPSILON ) / ( flDistStart - flDistEnd ); if( flFrac < flExitFrac ) { flExitFrac = flFrac; } } if ( ( flDistStart > 0.0f ) && ( flDistEnd > 0.0f ) ) return; // Positive Axis flDistStart = m_vecStartPosition[iAxis] - vecTriggerMaxs[iAxis]; flDistEnd = vecEndPosition[iAxis] - vecTriggerMaxs[iAxis]; if ( ( flDistStart > 0.0f ) && ( flDistEnd < 0.0f ) ) { flFrac = ( flDistStart - HEADCRABCANISTER_TRIGGER_EPSILON ) / ( flDistStart - flDistEnd ); if ( flFrac > flEnterFrac ) { flEnterFrac = flFrac; } } if ( ( flDistStart < 0.0f ) && ( flDistEnd > 0.0f ) ) { flFrac = ( flDistStart + HEADCRABCANISTER_TRIGGER_EPSILON ) / ( flDistStart - flDistEnd ); if( flFrac < flExitFrac ) { flExitFrac = flFrac; } } if ( ( flDistStart > 0.0f ) && ( flDistEnd > 0.0f ) ) return; } // Check for intersection. if ( flExitFrac >= flEnterFrac ) { // Check to see if we start in the world or the skybox! if ( flEnterFrac == 0.0f ) { m_nLocation = HEADCRABCANISTER_LOCATION_WORLD; } else { m_nLocation = HEADCRABCANISTER_LOCATION_SKYBOX; } // Calculate the enter/exit times. Vector vecEnterPoint, vecExitPoint, vecDeltaPosition; VectorSubtract( vecEndPosition, m_vecStartPosition, vecDeltaPosition ); VectorScale( vecDeltaPosition, flEnterFrac, vecEnterPoint ); VectorScale( vecDeltaPosition, flExitFrac, vecExitPoint ); m_flWorldEnterTime = vecEnterPoint.Length() / m_flFlightSpeed; m_flWorldEnterTime += m_flLaunchTime; } */ #undef HEADCRABCANISTER_TRIGGER_EPSILON }