/** * @param vRayStart Start point of an infinite ray. * @param vRayOffset Direction of the ray, parametric t multiplier. * @param fT0 Filled in with time of first intersection (if present). * @param fT1 Filled in with time of second intersection (if present). * @return The number of intersections found. */ UINT32 CCircle::GetLineIntersectionTimes(CFVec2Arg vRayStart, CFVec2Arg vRayOffset, GDE::FLOAT32 &fT0, GDE::FLOAT32 &fT1) const { const CFVec2 vToRay = vRayStart - m_vCentre; // TODO: reformulate without the square root? const FLOAT32 fExtentsRecip = 1.0f/vRayOffset.Magnitude(); const FLOAT32 fRayDotToRay = vToRay.DotProduct( vRayOffset ) * fExtentsRecip; const FLOAT32 fDiscr = fRayDotToRay*fRayDotToRay - (vToRay.SquareMagnitude()-m_fRadius*m_fRadius); if ( fDiscr < 0.0f ) { return 0; // no intersections. } if ( fDiscr == 0.0f ) { fT0 = -fRayDotToRay * fExtentsRecip; return 1; // one intersection, just touching } else { const FLOAT32 fRoot = sqrtf( fDiscr ); fT0 = (-fRayDotToRay - fRoot) * fExtentsRecip; fT1 = (-fRayDotToRay + fRoot) * fExtentsRecip; return 2; // two intersections. } }
void CSquirrel::Update( FLOAT32 fTimeDelta ) { if ( 0 == m_uNumLives ) { return; } const static CFVec2 s_vMoveOffsets[] = { SFVec2( -1.0f, 0.0f ), //"Left", SFVec2( 1.0f, 0.0f ), //"Right", SFVec2( 0.0f, -1.0f ), //"Up", SFVec2( 0.0f, 1.0f ), //"Down" }; _COMPILE_ASSERT( _ARRAY_SIZE( s_vMoveOffsets ) == EMove_COUNT ); CFVec2 vMove = SFVec2( 0.0f, 0.0f ); for ( UINT32 i=0; i< EMove_COUNT; i++ ) { if ( m_Movements[i].m_bValue ) { vMove += s_vMoveOffsets[i]; } } // great, now we have the movement direction. if ( vMove.SquareMagnitude() != 0.0f ) { m_fDistTimeMoving += fTimeDelta; vMove.Normalise(); FLOAT32 fStep = 1.0f; CFVec2 vTestedMove = vMove * fTimeDelta * m_fSpeed * fStep; // now check the target position - is it embedded in any walls? CCircle TargetBounds; while ( fStep > 0.0f ) { TargetBounds.Initialise( m_vPosition + vTestedMove, m_fRadius ); if ( false == CMMMContext::GetInstance().CircleIntersectsGeometry( TargetBounds ) ) { break; // found a valid, allowable movement. } fStep -= 0.2f; vTestedMove = vMove * fTimeDelta * m_fSpeed * fStep; } // now update to the new position m_vPosition += vTestedMove; // finally what happens at the new position // is an acorn collected? if ( m_uNumAcorns != m_uMaxAcorns ) { CAcorn* pLevelAcorns; UINT32 uNumLevelAcorns; CMMMContext::GetInstance().GetAcorns( pLevelAcorns, uNumLevelAcorns ); for ( UINT32 i=0; i<uNumLevelAcorns; i++ ) { if ( pLevelAcorns[i].GetState() == CAcorn::ES_Available ) { if ( pLevelAcorns[i].Intersects( TargetBounds ) ) { pLevelAcorns[i].SetState( CAcorn::ES_Carried ); m_ppAcorns[m_uNumAcorns++] = pLevelAcorns+i; if ( m_uNumAcorns == m_uMaxAcorns ) { break; // cannont collect any more! } } } } } // is a tunnel reached? if ( m_uNumAcorns > 0 ) { CTunnel* pTunnels; UINT32 uNumTunnels; CMMMContext::GetInstance().GetTunnels( pTunnels, uNumTunnels ); for ( UINT32 i=0; i<uNumTunnels; i++ ) { if ( pTunnels[i].Intersects( TargetBounds ) ) { // reached the tunnel. for ( UINT32 i=0; i<m_uNumAcorns; i++ ) { m_ppAcorns[i]->SetState( CAcorn::ES_Collected ); m_uScore++; } m_uNumAcorns = 0; } } } } m_fTimeToDisturbance -= fTimeDelta; if ( m_fTimeToDisturbance <= 0.0f ) { // schedule the next disturbance. m_fTimeToDisturbance = FLOAT32(rand())/FLOAT32(RAND_MAX); m_fTimeToDisturbance *= m_fMaxDistSep-m_fMinDistSep; m_fTimeToDisturbance += m_fMinDistSep; // create this disturbance: FLOAT32 fRad = m_fLastDistDelay=0.0f?0.0f:m_fDistTimeMoving/m_fLastDistDelay; // DbgPrint( "Creating Disturbance strength %0.2f, next delay %0.2f\n", fRad,m_fTimeToDisturbance ); fRad *= m_fMaxDistRad-m_fMinDistRad; fRad += m_fMinDistRad; if ( fRad >= 0.0f ) { CCircle Dist; Dist.Initialise( m_vPosition, fRad ); CMMMContext::GetInstance().CreateDisturbance( Dist ); } m_fDistTimeMoving = 0.0f; m_fLastDistDelay = m_fTimeToDisturbance; } }