/** * Check this wall with an infinite ray, given in parametric form and return the collision if applicable. * @param vRayStart Start point for parametric equation of infinite ray. * @param vRayEnd Direction vector (coefficient of t) for paremetric equation of infinitie ray. * @param fT To be filled in with the time (paremetric T) of intersection. * @return True if the intersection occurs within this line segment. */ bool CWall::GetLineIntersectionTime( CFVec2Arg vRayStart, CFVec2Arg vRayOffset, FLOAT32& fT ) const { // http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ const CFVec2& P1 = m_vEnd[0]; const CFVec2& P2 = m_vEnd[1]; const CFVec2& P3 = vRayStart; // no P4 required.. const CFVec2 V1 = P2-P1; const CFVec2& V2 = vRayOffset; const FLOAT32 fDenom = V2.Y()*V1.X() - V2.X()*V1.Y(); if ( fDenom == 0.0f ) { return false; // no intersections, or the lines are the same! } const FLOAT32 fDRecip = 1.0f/fDenom; // and then, finally Q (for x/y1 - x/y3) const CFVec2 Q = P1-P3; const FLOAT32 fTimeThis = (V2.X()*Q.Y() - V2.Y()*Q.X())*fDRecip; if ( fTimeThis < 0.0f || fTimeThis > 1.0f ) { // outside of the wall line segment. return false; } fT = (V1.X()*Q.Y() - V1.Y()*Q.X())*fDRecip; return true; }
bool CBox2D::Intersects( const CCircle& Circle ) const { CFVec2 vOffset = (Circle.Centre() - m_vCentre); vOffset.SetAbs(); vOffset -= m_vExtents; return ( vOffset.X() < Circle.Radius() && vOffset.Y() < Circle.Radius() ); }
CFVec2 CSteeringAgent::GetAdjustedWhisker(const CFVec2& _krvWhisker) const { CFVec2 vAdjustedWhisker = _krvWhisker; vAdjustedWhisker.RotateZ(m_krCat.GetHeadingRadians()); return vAdjustedWhisker; }
void CMole::Update(FLOAT32 _fTimeDelta) { m_pMoleAgent->Update(_fTimeDelta); // Turn. m_fHeading += m_pMoleAgent->GetRotationalSpeed() * _fTimeDelta; // Update position. CFVec2 vHeading; vHeading.SetToRotationZ(m_fHeading); CFVec2 vVelocity = vHeading * m_pMoleAgent->GetSpeed() * _fTimeDelta; m_vPosition += vVelocity; if ((m_vDestination - m_vPosition).Magnitude() < fMOLE_RADIUS) { Messages::send(DESTINATION_REACHED_EVENT, this); } // finally check if that pesky rodent got caught: CCircle Bounds; Bounds.Initialise( m_vPosition, fMOLE_RADIUS ); if ( CMMMContext::GetInstance().GetSquirrel()->Intersects( Bounds ) ) { CMMMContext::GetInstance().GetSquirrel()->LoseLife(); Messages::send(DESTINATION_REACHED_EVENT, this); } }
void CMole::UpdateRotation(float _fTimeDelta, const CFVec2& _kvrForce) { if (_kvrForce.X() != 0.0f || _kvrForce.Y() != 0.0f) { // Create heading vector CFVec2 vHeading(0.0f, -1.0f); vHeading.RotateZ(m_fHeading); // Cross float fCross = 0.0f; fCross = vHeading.X() * _kvrForce.Y(); fCross -= _kvrForce.X() * vHeading.Y(); float fRotationVelocity = m_fRotationVelocity + fDegsToRad(static_cast<float>(DIFFICULTY_ROTATION_VELOCITY_INCR * m_iDifficultyLevel)); if (fCross > 0.0f) { // Turn left m_fHeading += fRotationVelocity * _fTimeDelta; } else { // Turn right m_fHeading += fRotationVelocity * - _fTimeDelta; } } }
bool CBox2D::ContainsPoint( CFVec2Arg vPoint ) const { CFVec2 vOffset = (vPoint - m_vCentre); vOffset.SetAbs(); vOffset -= m_vExtents; return ( vOffset.X() < 0.0f && vOffset.Y() < 0.0f ); }
/** * @param fX Filled with the mouse X coordinate. * @param fY Filled with the mouse Y coordinate. * @param bWasClicked Filled with a bool to say if the mouse was clicked. */ bool CGameBoard::GetMouseInfo( FLOAT32& fX, FLOAT32& fY, bool& bWasClicked ) const { CFVec2 vMousePos; CConnect4Context::GetInstance().GetMouse()->GetPosition( vMousePos ); bWasClicked = CConnect4Context::GetInstance().GetMouse()->WasClicked(); fX = vMousePos.X(); fY = vMousePos.Y(); return true; }
bool CBox2D::Intersects( const CBox2D& Box ) const { CFVec2 vOffset = (Box.m_vCentre - m_vCentre); vOffset.SetAbs(); vOffset -= m_vExtents; vOffset -= Box.m_vExtents; return ( vOffset.X() < 0.0f && vOffset.Y() < 0.0f ); }
CFVec2 CSteeringAgent::GetRepulsionBearingEffect() const { CFVec2 vCumulativeRepulsionEffect = fv2EMPTY; const vector<CCircle>& krRepulsionZones = CBlackboard::GetInstance().GetRepulsionZones(); for (unsigned int uiIndex = 0; uiIndex < krRepulsionZones.size(); uiIndex++) { CFVec2 vRepulsionEffect = m_krCat.GetPosition() - krRepulsionZones.at(uiIndex).Centre(); FLOAT32 fDistanceBetweenCenters = vRepulsionEffect.Magnitude(); FLOAT32 fDistanceBetweenBorders = fDistanceBetweenCenters - m_krCat.GetRadius() - krRepulsionZones.at(uiIndex).Radius(); if (m_krCat.GetHeadingVector().DotProduct(vRepulsionEffect) < 0.0f && fDistanceBetweenBorders < m_fRepulsionBufferStatic) { FLOAT32 fDistanceIntoRepulsionBuffer = (m_fRepulsionBufferStatic - fDistanceBetweenBorders) / m_fRepulsionBufferStatic; vRepulsionEffect.Normalise(); vRepulsionEffect *= fDistanceIntoRepulsionBuffer; vCumulativeRepulsionEffect += vRepulsionEffect; } } const vector<const CCat*>& krCats = CBlackboard::GetInstance().GetCats(); for (unsigned int uiIndex = 0; uiIndex < krCats.size(); uiIndex++) { CFVec2 vRepulsionEffect = m_krCat.GetPosition() - krCats.at(uiIndex)->GetPosition(); FLOAT32 fDistanceBetweenCenters = vRepulsionEffect.Magnitude(); // We don't want the cat to repel itself... if (fDistanceBetweenCenters == 0.0f) { continue; } FLOAT32 fDistanceBetweenBorders = fDistanceBetweenCenters - m_krCat.GetRadius() * 2.0f; if (m_krCat.GetHeadingVector().DotProduct(vRepulsionEffect) < 0.0f && fDistanceBetweenBorders < m_fRepulsionBufferDynamic) { FLOAT32 fDistanceIntoRepulsionBuffer = (m_fRepulsionBufferDynamic - fDistanceBetweenBorders) / m_fRepulsionBufferDynamic; vRepulsionEffect.Normalise(); vRepulsionEffect *= fDistanceIntoRepulsionBuffer; vCumulativeRepulsionEffect += vRepulsionEffect; } } if (vCumulativeRepulsionEffect.Magnitude() > 1.0f) { vCumulativeRepulsionEffect.Normalise(); } return vCumulativeRepulsionEffect; }
void CMole::ComputeSeekForce(CFVec2& _vrSeekForce, int _iTargetNodeId) { const Path::TNode& ktrTargetNode = s_pSharedPath->GetNode(_iTargetNodeId); CFVec2 vSeekForce; vSeekForce = ktrTargetNode.vPosition; vSeekForce -= m_vPosition; vSeekForce.Normalise(); _vrSeekForce = vSeekForce; }
CFVec2 CSteeringAgent::GetAbsoluteWhiskerStart(const CFVec2& _krvRelativeWhisker) const { CFVec2 vAbsoluteWhiskerStart = GetAdjustedWhisker(_krvRelativeWhisker); if (vAbsoluteWhiskerStart.Magnitude() != 0.0f) { vAbsoluteWhiskerStart.Normalise(); vAbsoluteWhiskerStart *= m_krCat.GetRadius(); } vAbsoluteWhiskerStart += m_krCat.GetPosition(); return vAbsoluteWhiskerStart; }
CFVec2 CSteeringAgent::GetWhiskerBearingEffect() const { CBlackboard& rBlackboard = CBlackboard::GetInstance(); bool bRotationApplied = false; FLOAT32 fTotalRotation = 0.0f; FLOAT32 fRotation = Math::PI / 2.0f; // 90 degrees for (unsigned int uiIndex = 0 ; uiIndex < SIDE_WHISKER_COUNT; uiIndex++) { FLOAT32 fWhiskerTime = rBlackboard.GetClosestWallIntersectionTime(GetAbsoluteWhiskerStart(m_vWhiskersLeft[uiIndex]), GetAdjustedWhisker(m_vWhiskersLeft[uiIndex])); if (fWhiskerTime != -1.0f) { fTotalRotation += fRotation; bRotationApplied = true; } fWhiskerTime = rBlackboard.GetClosestWallIntersectionTime(GetAbsoluteWhiskerStart(m_vWhiskersRight[uiIndex]), GetAdjustedWhisker(m_vWhiskersRight[uiIndex])); if (fWhiskerTime != -1.0f) { fTotalRotation -= fRotation; bRotationApplied = true; } fRotation /= 5.0f; // TODO MAGIC } if (bRotationApplied) { // If the rotation was cancelled out (accounting for rounding errors). if (abs(fTotalRotation) < 0.0001f) { // The whiskers are intersecting on both sides so chances are we're heading straight for something and we // need to turn. Clockwise 90 degrees sounds good... fTotalRotation += Math::PI / 2.0f; } CFVec2 vWhiskerBearingEffect = m_krCat.GetHeadingVector(); vWhiskerBearingEffect.Normalise(); vWhiskerBearingEffect.RotateZ(fTotalRotation); return vWhiskerBearingEffect; } return fv2EMPTY; }
void CAddWallTool::NotifyLeftClick( void ) { CFVec2 vPos = CMMMContext::GetInstance().GetMouse()->GetPosition(); if ( m_uNumPoints > 1 && vPos.SquareDistance( m_vSegmentEnds[0] ) < fHANDLE_DIST*fHANDLE_DIST ) { // close the wall segment. m_vSegmentEnds[m_uNumPoints++] = m_vSegmentEnds[0]; CMMMContext::GetInstance().CreateWalls( m_vSegmentEnds, m_uNumPoints/2 ); #pragma warnmsg( "TODO: closed wall segments: fill?" ) m_uNumPoints = 0; return; } // start the next segment: m_vSegmentEnds[m_uNumPoints++] = vPos; if ( m_uNumPoints > 1 ) { // and finish the last one: m_vSegmentEnds[m_uNumPoints++] = vPos; } }
void CAddWallTool::Render( void ) { if ( 0 == m_uNumPoints ) { return; } CFVec2 vPos = CMMMContext::GetInstance().GetMouse()->GetPosition(); m_vSegmentEnds[m_uNumPoints] = vPos; if ( vPos.SquareDistance( m_vSegmentEnds[0] ) < fHANDLE_DIST*fHANDLE_DIST ) { // close the wall segment, so show the snap. m_vSegmentEnds[m_uNumPoints] = m_vSegmentEnds[0]; } for ( UINT32 i=0; i<m_uNumPoints; i+=2 ) { // draw in red CMMMContext::GetInstance().DrawLine( m_vSegmentEnds[i], m_vSegmentEnds[i+1], 0xFFFF0000 ); } }
/** * @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 CPathFindingAgent::AdvanceDestinationOnPath(float _fDistance) { if (m_iPreviousWaypointIndex == -1) { m_iPreviousWaypointIndex = 0; m_iNextWaypointIndex = 1; } CFVec2 vAdvancement = m_path.at(m_iNextWaypointIndex)->GetPosition() - m_path.at(m_iPreviousWaypointIndex)->GetPosition(); vAdvancement.Normalise(); vAdvancement *= _fDistance; CFVec2 vDestinationCopy = m_vDestination; vDestinationCopy += vAdvancement; float fDistancePastNextWaypoint = (vDestinationCopy - m_path.at(m_iPreviousWaypointIndex)->GetPosition()).Magnitude() - (m_path.at(m_iNextWaypointIndex)->GetPosition() - m_path.at(m_iPreviousWaypointIndex)->GetPosition()).Magnitude(); if (fDistancePastNextWaypoint > 0.0f) { m_vDestination = m_path.at(m_iNextWaypointIndex)->GetPosition(); if (m_path.size() > (unsigned) m_iNextWaypointIndex + 1) { m_iPreviousWaypointIndex++; m_iNextWaypointIndex++; AdvanceDestinationOnPath(fDistancePastNextWaypoint); } } else { m_vDestination += vAdvancement; } }
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; } }
void CMole::ComputeWallForce(CFVec2& _vrRepelForce) { for (unsigned int i = 0; i < MAX_WHISKER; ++ i) { CFVec2 vLineRepelForce; CWall* pClosestWall = 0; float fImpactTime = 0; bool bIntersected = FindWiskerClosestWall(static_cast<EWhisker>(i), pClosestWall, fImpactTime); if (bIntersected) { // Time radio float fTimeRatio = 1.0f - (fImpactTime / 1.0f); fTimeRatio *= 1.8f; if (fTimeRatio > 1.0f) { fTimeRatio = 1.0f; } CFVec2 vPointDisplacement(pClosestWall->GetStartPoint()); vPointDisplacement -= pClosestWall->GetEndPoint(); // Line direction CFVec2 vLineDirection(vPointDisplacement); vLineDirection.Normalise(); // Get Normal CFVec2 vLineNormal(pClosestWall->GetNormal()); vLineNormal.Normalise(); CFVec2 vStart = pClosestWall->GetStartPoint(); CFVec2 vEnd = pClosestWall->GetEndPoint(); // Which side is the cat on float fPerpendicularDot = (vEnd.X() - vStart.X()) * (m_vPosition.Y() - vStart.Y()) - (vEnd.Y() - vStart.Y()) * (m_vPosition.X() - vStart.X()); bool bOnNormalSide = (fPerpendicularDot > 0); // Cat going against the normal if (bOnNormalSide) { // Cat has to be on normal side vLineRepelForce = vLineNormal; vLineRepelForce.RotateZ(fDegsToRad(180.0f)); } // Cat going towards normal else { // Cat has to be not on normal side vLineRepelForce = vLineNormal; } // Scale based on collision time //vLineRepelForce *= 1.25f; vLineRepelForce *= fTimeRatio; _vrRepelForce += vLineRepelForce; m_pWhiskers[i].SetColour(0xFFFF0000); } } }