bool AINodeValidatorPathBlocked::Evaluate( uint32 dwFilteredStatusFlags, const LTVector& vNodePos, const LTVector& vAIPos, const LTVector& vThreatPos ) const { // Threat blocking path. if( dwFilteredStatusFlags & kNodeStatus_ThreatBlockingPath ) { // Check if the threat is too close to the AI, // and is blocking the path to the node. if ( vAIPos.DistSqr(vThreatPos) < g_pAIDB->GetAIConstantsRecord()->fThreatTooCloseDistanceSqr ) { LTVector vToThreat = vThreatPos - vAIPos; LTVector vToNode = vNodePos - vAIPos; if( vToThreat.Dot( vToNode ) > c_fFOV60 ) { return false; } } } return true; }
LTBOOL FindGrenadeDangerPosition(const LTVector& vPos, LTFLOAT fDangerRadiusSqr, LTVector* pvDangerPos, CGrenade** ppGrenadeDanger) { _ASSERT(pvDangerPos); CGrenade** ppGrenade = g_lstGrenades.GetItem(TLIT_FIRST); while ( ppGrenade && *ppGrenade ) { CGrenade* pGrenade = *ppGrenade; LTVector vGrenadePosition; g_pLTServer->GetObjectPos(pGrenade->m_hObject, &vGrenadePosition); if ( vPos.DistSqr(vGrenadePosition) < fDangerRadiusSqr ) { *ppGrenadeDanger = pGrenade; *pvDangerPos = vGrenadePosition; return LTTRUE; } ppGrenade = g_lstGrenades.GetItem(TLIT_NEXT); } return LTFALSE; }
bool AINodeValidatorPlayerOnNode::Evaluate( uint32 dwFilteredStatusFlags, CAI* pAI, const LTVector& vNodePos ) const { // Node is always valid if there is no AI to consider. if( !pAI ) { return true; } // Player is standing on the node. if( dwFilteredStatusFlags & kNodeStatus_PlayerOnNode ) { float fMinDist; uint32 iPlayer = 0; LTVector vPlayerDims; LTVector vPlayerPos; CPlayerObj* pPlayer; while( true ) { // Check the next Player, if others exist. pPlayer = g_pCharacterMgr->FindPlayer( iPlayer ); if( !pPlayer ) { return true; } ++iPlayer; // Bail if we don't like Players. if( g_pCharacterDB->GetStance( pAI->GetAlignment(), pPlayer->GetAlignment() ) != kCharStance_Like ) { return true; } // Node is above or below the Player's cylinder. g_pLTServer->GetObjectPos( pPlayer->m_hObject, &vPlayerPos ); g_pPhysicsLT->GetObjectDims( pPlayer->m_hObject, &vPlayerDims ); if( ( vPlayerPos.y - vPlayerDims.y > vNodePos.y ) || ( vPlayerPos.y + vPlayerDims.y < vNodePos.y ) ) { continue; } // Pad the Player's radius by 100%. fMinDist = pPlayer->GetRadius(); fMinDist *= 2.f; // Player is too close to the Node. vPlayerPos.y = vNodePos.y; if( vPlayerPos.DistSqr( vNodePos ) < fMinDist * fMinDist ) { return false; } } } return true; }
// Tries everything it can think of to reject this object intersection. // If it does intersect and is closer than the current best world intersection // then it replaces the current one. inline bool i_HandlePossibleIntersection(const LTVector& Point1, const LTVector& Point2, LTObject *pServerObj) { // Quick sphere test. if (i_QuickSphereTest(pServerObj)) { // Ok, filter if necessary. if (g_pCurQuery->m_FilterFn && !g_pCurQuery->m_FilterFn((HOBJECT)pServerObj, g_pCurQuery->m_pUserData)) { // They said to ignore it.. } else { // If it's a WorldModel, add it to the list to be tested later. // You can't treat it like a solid box here because a ray could go // right through all its geometry. // [kls 5/16/00 Always do full intersection tests on world models // so we get the correct hpoly information // if (HasWorldModel(pServerObj) && !(pServerObj->m_Flags & FLAG_BOXPHYSICS)) if (HasWorldModel(pServerObj)) { return i_TestWorldModel(pServerObj->ToWorldModel()); } else { // Bounding box test... LTVector testPt; LTPlane testPlane; if (i_BoundingBoxTest(Point1, Point2, pServerObj, &testPt, &testPlane)) { // Is this intersection closer than the current best? float distToIntersectionSqr = testPt.DistSqr(g_pCurQuery->m_From); if (g_pIntersection) { if (distToIntersectionSqr < g_IntersectionBestDistSqr) { // Do we care about model OBBs? if(g_bProcessModelObbs) { // Is this object a model? if(IsModel(pServerObj)) { ModelInstance *pModel = pServerObj->ToModel(); // Does this object want to expose it's OBBs? if(pServerObj->m_Flags2 & FLAG2_USEMODELOBBS) { // Does this model have OBBS? if(pModel->IsCollisionObjectsEnabled()) { // Then test the OBBs return i_TestModelOBBS(pModel); }else { // If this object has specified OBBs enabled but is // not equipt for model obbs, then return no collision return false; } } } } //else return UseThisObject(pServerObj, distToIntersectionSqr, testPlane, testPt, INVALID_HPOLY, INVALID_MODEL_NODE); } } else { //USE_THIS_OBJECT(pServerObj, distToIntersectionSqr, testPlane, testPt, INVALID_HPOLY); //return true; return UseThisObject(pServerObj, distToIntersectionSqr, testPlane, testPt, INVALID_HPOLY, INVALID_MODEL_NODE); } } } } } return false; }
bool CAIActionAttackLungeUncloaked::ValidateContextPreconditions( CAI* pAI, CAIWorldState& wsWorldStateGoal, bool bIsPlanning ) { // AI doesn't have a target. if (!pAI->HasTarget( kTarget_Character )) { return false; } // Target is not visible. if( !pAI->GetAIBlackBoard()->GetBBTargetVisibleFromWeapon() ) { return false; } // AI does not have a weapon of the correct type if (!AIWeaponUtils::HasWeaponType(pAI, GetWeaponType(), bIsPlanning)) { return false; } // AI does not have any ammo for this weapon. if ( !AIWeaponUtils::HasAmmo( pAI, GetWeaponType(), bIsPlanning ) ) { return false; } // Someone else is lunging. Only one AI may lunge at a time. CAIWMFact factQuery; factQuery.SetFactType( kFact_Knowledge ); factQuery.SetKnowledgeType( kKnowledge_Lunging ); CAIWMFact* pFact = g_pAIWorkingMemoryCentral->FindWMFact(factQuery); if( pFact ) { // Clear records of dead AI. if( IsDeadAI( pFact->GetSourceObject() ) ) { g_pAIWorkingMemoryCentral->ClearWMFacts( factQuery ); } else return false; } // Bail if the Action's SmartObject record does not exist. AIDB_SmartObjectRecord* pSmartObjectRecord = g_pAIDB->GetAISmartObjectRecord( m_pActionRecord->eSmartObjectID ); if( !pSmartObjectRecord ) { return false; } // Someone has lunged too recently. if( pSmartObjectRecord->fTimeout > 0.f ) { factQuery.SetFactType( kFact_Knowledge ); factQuery.SetKnowledgeType( kKnowledge_NextLungeTime ); pFact = g_pAIWorkingMemoryCentral->FindWMFact(factQuery); if( pFact && ( pFact->GetTime() > g_pLTServer->GetTime() ) ) { return false; } } // Bail if the AI does not have the desire to lunge. // The desire indicates the max range of the lunge. CAIWMFact factDesireQuery; factDesireQuery.SetFactType( kFact_Desire ); factDesireQuery.SetDesireType( kDesire_Lunge ); CAIWMFact* pDesireFact = pAI->GetAIWorkingMemory()->FindWMFact( factDesireQuery ); if( !pDesireFact ) { return false; } // Target must be in range. LTVector vTarget = pAI->GetAIBlackBoard()->GetBBTargetPosition(); float fDistSqr = vTarget.DistSqr( pAI->GetPosition() ); // Target too close. if( fDistSqr < pSmartObjectRecord->fMinDist * pSmartObjectRecord->fMinDist ) { return false; } // Target too far. float fMaxDist = GetLungeMaxDist( pAI, pDesireFact ); if( fDistSqr > fMaxDist * fMaxDist ) { return false; } // No straight path to the target. LTVector vDir = vTarget - pAI->GetPosition(); vDir.Normalize(); LTVector vDest = pAI->GetPosition() + ( vDir * ( fMaxDist + 50.f ) ); if( !g_pAIPathMgrNavMesh->StraightPathExists( pAI, pAI->GetCharTypeMask(), pAI->GetPosition(), vDest, pAI->GetAIBlackBoard()->GetBBTargetReachableNavMeshPoly(), pAI->GetRadius() ) ) { return false; } // Lunge! return true; }
int operator()(CAIWMFact* pFact) { // Ignore facts that are not node facts. // Ignore node facts with the wrong node type. // Ignore node facts whose object matches the hExclude. if( ( pFact->GetFactType() != kFact_Node ) || ( pFact->GetNodeType() != kNode_Stalk )) { return 0; } AINode* pNode = AINode::HandleToObject( pFact->GetTargetObject() ); if( !pNode ) { return 0; } // Bail if node is locked by someone else, disabled, or timed out. // TODO: Handle the AI locking the node. This can work when code to // handle an invalid node is written. if (pNode->GetHOBJECT() == m_hIgnoreStalkingNode) { return false; } if (pNode->IsNodeLocked()/* && pNode->GetLockingAI() != m_pAI->GetHOBJECT()*/) { return false; } if( pNode->IsNodeDisabled() || pNode->IsNodeTimedOut() ) { return false; } // Require the AI be in the radius or region if (!pNode->IsAIInRadiusOrRegion( m_pAI, m_pAI->GetPosition(), 1.f )) { return 0; } // If AI is aware of a threat, ignore nodes that are invalid. if( m_hThreat && ( !pNode->IsNodeValid( m_pAI, m_pAI->GetPosition(), m_hThreat, kThreatPos_TargetPos, kNodeStatus_All ^ kNodeStatus_ThreatOutsideFOV) ) ) { return 0; } // Failed to find a valid destination position. LTVector vDestination; if (!pNode->GetDestinationPosition(m_pAI, m_pAI->GetAIBlackBoard()->GetBBTargetPosition(), vDestination)) { return 0; } // The stalking position is further from the AIs goal than the AI // currently is (don't run backwards to stalk). float flNodeDestToAIDestDistSqr = vDestination.DistSqr(m_pAI->GetAIBlackBoard()->GetBBTargetReachableNavMeshPosition()); if (flNodeDestToAIDestDistSqr > m_flDistanceToDestinationSqr) { return 0; } // Node is behind the AI. if (0 > m_pAI->GetForwardVector().Dot( vDestination - m_pAI->GetPosition())) { return 0; } // Already found a closer node. float flDistanceFromAISqr = vDestination.DistSqr(m_pAI->GetPosition()); if (flDistanceFromAISqr > m_flBestDistanceFromAISqr) { return 0; } m_vBestPosition = vDestination; m_flBestDistanceFromAISqr = flDistanceFromAISqr; m_pBestFact = pFact; return 0; }
bool AINavMeshLinkPlayer::FindNearestNavMeshPos( CAI* pAI, const LTVector& vPos, LTVector* pvNavMeshPos, ENUM_NMPolyID* peNavMeshPoly ) { // Sanity check. if( !( pAI && pvNavMeshPos && peNavMeshPoly ) ) { return false; } // Bail if Link's poly is invalid. CAINavMeshPoly* pPoly = g_pAINavMesh->GetNMPoly( m_eNMPolyID ); if( !pPoly ) { return false; } // Iterate over Link's edges, searching for edge nearest // to the specified position. ENUM_NMPolyID eNeighborPoly; CAINavMeshEdge* pEdge; CAINavMeshEdge* pEdgeNearest = NULL; float fDistSqr; float fMinDistSqr = FLT_MAX; uint32 cEdges = pPoly->GetNumNMPolyEdges(); for( uint32 iEdge=0; iEdge < cEdges; ++iEdge ) { // Skip edge if doesn't exist. pEdge = pPoly->GetNMPolyEdge( iEdge ); if( !pEdge ) { continue; } // Skip edge if AI cannot pathfind to the neighboring poly. eNeighborPoly = ( pEdge->GetNMPolyIDA() != m_eNMPolyID ) ? pEdge->GetNMPolyIDA() : pEdge->GetNMPolyIDB(); if( ( eNeighborPoly == kNMPoly_Invalid ) || ( !g_pAIPathMgrNavMesh->HasPath( pAI, pAI->GetCharTypeMask(), eNeighborPoly ) ) ) { continue; } // Keep track of the edge nearest to the specified position. fDistSqr = vPos.DistSqr( pEdge->GetNMEdgeMidPt() ); if( fDistSqr < fMinDistSqr ) { fMinDistSqr = fDistSqr; pEdgeNearest = pEdge; } } // Bail if no valid edge was found. if( !pEdgeNearest ) { return false; } // Return success. if( FindNearestPointOnLine( pEdgeNearest->GetNMEdge0(), pEdgeNearest->GetNMEdge1(), vPos, pvNavMeshPos ) ) { // Push the point a small amount out of the link. LTVector vDir = *pvNavMeshPos - vPos; vDir.Normalize(); *pvNavMeshPos += vDir * 1.f; *peNavMeshPoly = ( pEdgeNearest->GetNMPolyIDA() != m_eNMPolyID ) ? pEdgeNearest->GetNMPolyIDA() : pEdgeNearest->GetNMPolyIDB(); return true; } // No intersection found. return false; }
bool CCamWobbleFX::Update(float tmFrameTime) { // Base class update first if (!CBaseFX::Update(tmFrameTime)) return false; // Return out if we have shutdown if (IsShuttingDown()) return true; if (!g_bAppFocus) { return true; } LTVector vCurCamPos; LTVector vObjPos; // Retrieve the current position of the camera and get the distance to it m_pLTClient->GetObjectPos(m_hCamera, &vCurCamPos); if( m_hParent ) { m_pLTClient->GetObjectPos( m_hParent, &vObjPos ); } else { vObjPos = m_vCreatePos; } LTFLOAT fDistSqrd = vObjPos.DistSqr( vCurCamPos ); LTFLOAT fFallOff; // Figure out the fall off of the shaking based on the inner and outer radii... if( fDistSqrd > GetProps()->m_fOuterDistSqrd ) { return LTTRUE; } else if( fDistSqrd <= GetProps()->m_fInnerDistSqrd ) { fFallOff = 1.0f; } else { fFallOff = 1 - ((fDistSqrd - GetProps()->m_fInnerDistSqrd) / (GetProps()->m_fOuterDistSqrd - GetProps()->m_fInnerDistSqrd)); } // Compute the FOV offsets float fLen = GetProps()->m_tmLifespan / GetProps()->m_fPeriod; float fVal = fmodf(m_tmElapsed, fLen); float fRadVal = (MATH_CIRCLE / fLen) * fVal; float xOff = m_xFovAnchor + ((float)sin(fRadVal) * GetProps()->m_xMultiplier * fFallOff); float yOff = m_yFovAnchor + ((float)cos(fRadVal) * GetProps()->m_yMultiplier * fFallOff); m_pLTClient->SetCameraFOV(m_hCamera, xOff, yOff); // Success !! return true; }
void CCharacterHitBox::Update() { AIASSERT( m_pHitBoxUser, m_hObject, "Called with NULL HitBoxUser" ); AIASSERT( m_hModel, m_hObject, "Called with NULL m_hModel" ); AIASSERT( m_hObject, m_hObject, "Called with NULL m_hObject" ); if (!m_hModel || !m_hObject) { return; } // Position to move to... LTVector vPos; bool bUpdateClient = false; // KLS 1/23/04 - Follow the visibility node on our model if specified... if (m_bFollowVisNode) { // Get the model's vis node... HMODELNODE hNode; if ( LT_OK == g_pModelLT->GetPhysicsVisNode(m_hModel, hNode) ) { LTTransform tf; if ( LT_OK == g_pModelLT->GetNodeTransform(m_hModel, hNode, tf, true) ) { vPos = tf.m_vPos; // KLS 5/3/04 - If we're following the model's vis node, we want to move // the model to follow us... LTVector vModelPos; g_pLTServer->GetObjectPos(m_hModel, &vModelPos); if (vPos.DistSqr(vModelPos) > 0.1f) { g_pLTServer->SetObjectPos(m_hModel, vPos); } } } } else // Use the model's position... { // Get current model position... g_pLTServer->GetObjectPos(m_hModel, &vPos); // Make sure the hit box offset is relative to the model... LTRotation rRot; g_pLTServer->GetObjectRotation( m_hModel, &rRot ); vPos += (rRot * m_vOffset); } // Get the hitbox's position... LTVector vMyPos; g_pLTServer->GetObjectPos(m_hObject, &vMyPos); // Only move the hitbox if it isn't close to the target position... if (vPos.DistSqr(vMyPos) > 0.1) { bUpdateClient = true; g_pLTServer->SetObjectPos(m_hObject, vPos); } if( (m_bAnimControlsDims || m_bAnimControlsOffset) && (m_hControllingAnim != INVALID_ANI) ) { HMODELANIM hCurAnim = INVALID_ANI; if( LT_OK == g_pModelLT->GetCurAnim( m_hModel, MAIN_TRACKER, hCurAnim )) { if( hCurAnim != m_hControllingAnim ) { // We changed animations from our controlling anim so default our dims and offset... // Set offset first since SetDimsToModel() will update the client SetOffset( LTVector(0,0,0) ); SetDimsToModel(); // The animation is no longer controlling us... m_bAnimControlsDims = m_bAnimControlsOffset = false; m_hControllingAnim = INVALID_ANI; } } } // Make sure the hit box is at least at the minimum dims... LTVector vDims; g_pPhysicsLT->GetObjectDims( m_hObject, &vDims ); if( vDims.x < HB_DIMS_MIN_XZ || vDims.z < HB_DIMS_MIN_XZ ) { vDims.x = vDims.z = HB_DIMS_MIN_XZ; g_pPhysicsLT->SetObjectDims( m_hObject, &vDims, 0 ); bUpdateClient = true; } if( vDims.y < HB_DIMS_MIN_Y ) { vDims.y = HB_DIMS_MIN_Y; g_pPhysicsLT->SetObjectDims( m_hObject, &vDims, 0 ); bUpdateClient = true; } if (bUpdateClient) { m_pHitBoxUser->UpdateClientHitBox(); } // See if we should show our model node radii... #ifndef _FINAL float fShowNodeRadii = (g_vtShowNodeRadii.GetFloat() ? (IsPlayer(m_hModel) ? g_vtShowPlayerNodeRadii.GetFloat() : 1.0f) : 0.0f); if (fShowNodeRadii) { UpdateNodeRadiusModels(); } else { RemoveNodeRadiusModels(); } #endif }
void CAIGoalCharge::CalculateGoalRelevance() { // AI doesn't have a target. if( !m_pAI->HasTarget( kTarget_Character ) ) { m_fGoalRelevance = 0.f; return; } // No relevance if AI does not have a deisre to lunge. // The desire indicates the max range of the lunge. CAIWMFact factDesireQuery; factDesireQuery.SetFactType( kFact_Desire ); factDesireQuery.SetDesireType( kDesire_Lunge ); CAIWMFact* pDesireFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factDesireQuery ); if( !pDesireFact ) { m_fGoalRelevance = 0.f; return; } // Someone has lunged too recently. CAIWMFact factQuery; factQuery.SetFactType( kFact_Knowledge ); factQuery.SetKnowledgeType( kKnowledge_NextLungeTime ); CAIWMFact* pFact = g_pAIWorkingMemoryCentral->FindWMFact(factQuery); if( pFact && ( pFact->GetTime() > g_pLTServer->GetTime() ) ) { m_fGoalRelevance = 0.f; return; } // Target must be in range. LTVector vTarget = m_pAI->GetAIBlackBoard()->GetBBTargetPosition(); float fDistSqr = vTarget.DistSqr( m_pAI->GetPosition() ); // Target too far. float fMaxDist = pDesireFact->GetRadius(); if( fDistSqr > fMaxDist * fMaxDist ) { m_fGoalRelevance = 0.f; return; } // No straight path to the target. if( !g_pAIPathMgrNavMesh->StraightPathExists( m_pAI, m_pAI->GetCharTypeMask(), m_pAI->GetPosition(), vTarget, m_pAI->GetAIBlackBoard()->GetBBTargetReachableNavMeshPoly(), 0.f ) ) { m_fGoalRelevance = 0.f; return; } // Default handling from KillEnemy. super::CalculateGoalRelevance(); }
void CAIMovement::AvoidDynamicObstacles(LTVector* pvNewPos, EnumAnimMovement eMovementType) { LTFLOAT fRadius = 128.f; LTFLOAT fRadiusSqr = fRadius * fRadius; LTVector vMyPos = m_pAI->GetPosition(); // Calculate the horizontal velocity. LTVector vVel = *pvNewPos - vMyPos; vVel.y = 0.f; // Bail if no velocity. if( ( vVel.x == 0.f ) && ( vVel.z == 0.f ) ) { return; } LTFLOAT fMag = vVel.Mag(); LTVector vTotalForce(0.f, 0.f, 0.f); LTVector vObstaclePos; LTFLOAT fDistSqr; LTFLOAT fForce; LTVector vForce; CTList<CCharacter*>* lstChars = LTNULL; CCharacter** pCur = LTNULL; // Iterate over all characters in the world. int cCharLists = g_pCharacterMgr->GetNumCharacterLists(); for ( int iList = 0 ; iList < cCharLists ; ++iList ) { lstChars = g_pCharacterMgr->GetCharacterList(iList); pCur = lstChars->GetItem(TLIT_FIRST); while( pCur ) { CCharacter* pChar = (CCharacter*)*pCur; pCur = lstChars->GetItem(TLIT_NEXT); // Ignore myself. if( pChar == m_pAI ) { continue; } // Ignore characters that are too close to our dest. // The pathfinding system requires AIs to reach waypoints. g_pLTServer->GetObjectPos( pChar->m_hObject, &vObstaclePos ); if( vObstaclePos.DistSqr( m_vDest ) <= fRadiusSqr ) { continue; } // Only characters within radius have forces that affect me. fDistSqr = vObstaclePos.DistSqr( vMyPos ); if( fDistSqr >= fRadiusSqr ) { continue; } // Calculate the force vector from the obstacle to myself. fForce = fRadius - (LTFLOAT)sqrt( fDistSqr ); fForce /= fRadius; fForce *= fForce; fForce *= ( 2.f * fMag ); vForce = vMyPos - vObstaclePos; vForce.y = 0.f; vForce.Normalize(); vForce *= fForce; // Accumulate the total force from all obstacles. vTotalForce += vForce; } } // Bail if no forces are affecting me. if( ( vTotalForce.x == 0.f ) && ( vTotalForce.z == 0.f ) ) { return; } // Calculate a new velocity vector. LTVector vNewVel = vVel + vTotalForce; // Constrain velocity so that is never deviates more than // 90 degrees in either direction. This prevents AIs from ever // reversing their direction when the forces are stronger than // the initial velocity. if( vNewVel.Dot( vVel ) < 0.f ) { vVel.Normalize(); LTVector vUp( 0.f, 1.f, 0.f ); LTVector vRight = vUp.Cross( vVel ); if( vRight.Dot( vNewVel ) < 0.f ) { vNewVel = -vRight; } else { vNewVel = vRight; } } // Keep magnitude of velocity constant. vNewVel.Normalize(); vNewVel *= fMag; // Calculate new position. // Bail if new position is out of volumes. // Bail if new position is in wrong volume. LTVector vNewPos = vMyPos + vNewVel; if( !m_pDestVolume->Inside2d( vNewPos, m_pAI->GetRadius() ) ) { return; } // Move toward new position. *pvNewPos = vNewPos; if( eMovementType == kAM_Encode_GB ) { m_pAI->FacePosMoving( m_pAI->GetPosition() ); } else { m_pAI->FacePosMoving( vNewPos ); } }
bool CAISensorBerserker::UpdateSensor() { if ( !m_pAI ) { return false; } // Fail if AI was not previously aware of a character target. if( !(m_pAI->GetAIBlackBoard()->GetBBTargetedTypeMask() & kTarget_Character) ) { return false; } // Fail if the AI already has a berserker target if ( m_pAI->HasTarget( kTarget_Berserker ) ) { return false; } float flMin = 0.f; float flMax = 0.f; GetBerserkerSenseRange( &flMin, &flMax ); float flMinSqr = flMin*flMin; float flMaxSqr = flMax*flMax; // // Remove the any current berserker facts if the AI is out of range. // AllBerserkerFacts all; m_pAI->GetAIWorkingMemory()->CollectFact( all ); for ( int iFact = 0; iFact < all.GetCount(); ++iFact ) { CAIWMFact* pFact = all.GetFact( iFact ); if ( pFact ) { LTVector vTargetPos; g_pLTServer->GetObjectPos( pFact->GetTargetObject(), &vTargetPos ); float flDistSqr = vTargetPos.DistSqr( m_pAI->GetPosition()); if ( flDistSqr > flMaxSqr || flDistSqr < flMinSqr) { m_pAI->GetAIWorkingMemory()->ClearWMFact( pFact ); } } } // // Find a valid berserker target. // // Fail if there no target to perform a berserk attack against. BerserkerTargetCollector Berserker( m_pAI, flMinSqr, flMaxSqr ); m_pAI->GetAIWorkingMemory()->CollectFact( Berserker ); if ( !Berserker.m_pBestTargetFact ) { return false; } // Add a desire to perform a berserk attack against if this target if one // doesn't already exist. CAIWMFact factQuery; factQuery.SetFactType( kFact_Desire ); factQuery.SetDesireType( kDesire_Berserker ); factQuery.SetTargetObject( Berserker.m_pBestTargetFact->GetTargetObject() ); CAIWMFact* pBerserkerDesire = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if ( !pBerserkerDesire ) { CAIWMFact* pBerserkerDesire = m_pAI->GetAIWorkingMemory()->CreateWMFact( kFact_Desire ); pBerserkerDesire->SetDesireType( kDesire_Berserker ); pBerserkerDesire->SetTargetObject( Berserker.m_pBestTargetFact->GetTargetObject() ); pBerserkerDesire->SetTime( g_pLTServer->GetTime() ); } // Flag a target re-evaluation, but only if the AI is unarmed if ( !AIWeaponUtils::HasWeaponType( m_pAI, kAIWeaponType_Ranged, AIWEAP_CHECK_HOLSTER ) && !AIWeaponUtils::HasWeaponType( m_pAI, kAIWeaponType_Melee, AIWEAP_CHECK_HOLSTER ) ) { m_pAI->GetAIBlackBoard()->SetBBInvalidateTarget( true ); } return true; }
void CTriggerFX::CalcLocalClientDistance() { m_fDistPercent = -1.0f; // Don't do anything if the trigger is locked or our distances are too small.. if( m_cs.bLocked || (m_cs.fHUDAlwaysOnDist <= 0.0f && m_cs.fHUDLookAtDist <= 0.0f) ) return; // See if the player is within the trigger... LTVector vTrigPos; g_pLTClient->GetObjectPos( m_hServerObject, &vTrigPos ); g_pLTClient->SetObjectPos( m_hDimsObject, vTrigPos ); HLOCALOBJ hPlayerObj = g_pLTClient->GetClientObject(); LTVector vPlayerPos, vPlayerDims; g_pLTClient->GetObjectPos( hPlayerObj, &vPlayerPos ); // Make sure we are within the display radius... float fMaxRadius = LTMAX( m_cs.fHUDAlwaysOnDist, m_cs.fHUDLookAtDist ); float fDistSqr = vTrigPos.DistSqr( vPlayerPos ); bool bWithinLookAtDist = (fDistSqr < m_cs.fHUDLookAtDist * m_cs.fHUDLookAtDist); bool bWithinAlwaysOnDist = (fDistSqr < m_cs.fHUDAlwaysOnDist * m_cs.fHUDAlwaysOnDist); if( !bWithinLookAtDist && !bWithinAlwaysOnDist ) { // We are not close enough... m_bWithinIndicatorRadius = false; return; } m_bWithinIndicatorRadius = true; g_pPhysicsLT->GetObjectDims( hPlayerObj, &vPlayerDims ); LTVector vTrigMin = vTrigPos - m_cs.vDims; LTVector vTrigMax = vTrigPos + m_cs.vDims; LTVector vPlayerMin = vPlayerPos - vPlayerDims; LTVector vPlayerMax = vPlayerPos + vPlayerDims; // Check if we are within the height of the trigger... bool bWithinHeight =false; if( vPlayerMax.y > vTrigMin.y && vPlayerMin.y < vTrigMax.y ) bWithinHeight = true; // See if we are inside the trigger at all... if( bWithinHeight && (BoxesIntersect( vTrigMin, vTrigMax, vPlayerMin, vPlayerMax ) || bWithinAlwaysOnDist)) { m_fDistPercent = 1.0f; } else { // We are within the height of the trigger, show how far from it we are... float fMinDist = (vPlayerDims.x + vPlayerDims.z) * 0.5f; float fMaxDist = 100000.0f; LTVector vDir; if( bWithinAlwaysOnDist ) { vDir = vTrigPos - vPlayerPos; vDir.Normalize(); } else { LTRotation const& rRot = g_pPlayerMgr->GetPlayerCamera()->GetCameraRotation( ); vDir = rRot.Forward(); } IntersectQuery IQuery; IntersectInfo IInfo; IQuery.m_From = vPlayerPos + (vDir * fMinDist); IQuery.m_To = IQuery.m_From + (vDir * fMaxDist); IQuery.m_Flags = INTERSECT_OBJECTS | INTERSECT_HPOLY | IGNORE_NONSOLID; // We need to recieve rayhits for this intersect call... g_pCommonLT->SetObjectFlags( m_hDimsObject, OFT_Flags, FLAG_RAYHIT, FLAG_RAYHIT ); if( g_pLTClient->IntersectSegment( IQuery, &IInfo )) { if( IInfo.m_hObject == m_hDimsObject ) { IInfo.m_Point.y = vPlayerPos.y; float fDist = vPlayerPos.Dist( IInfo.m_Point ); m_fDistPercent = 1.0f - (fDist / fMaxRadius); } } // No more rayhits... g_pCommonLT->SetObjectFlags( m_hDimsObject, OFT_Flags, 0, FLAG_RAYHIT ); } }
bool CAISensorPassTarget::NeedToHoldPosition( bool* pbCalledFindPath ) { // Sanity check. if( !pbCalledFindPath ) { return false; } // No need to hold position if not targeting a character. if( !m_pAI->HasTarget( kTarget_Character ) ) { m_hVerifiedNode = NULL; return false; } // Only check for passing the target if we are within some distance of the target. LTVector vTargetPos = m_pAI->GetAIBlackBoard()->GetBBTargetPosition(); float fDistSqr = vTargetPos.DistSqr( m_pAI->GetPosition() ); if( fDistSqr > g_pAIDB->GetAIConstantsRecord()->fHoldPositionDistanceSqr ) { m_hVerifiedNode = NULL; return false; } // Character is outside of the NavMesh. ENUM_NMPolyID ePolyTarget = GetTargetNavMeshPoly(); if( ePolyTarget == kNMPoly_Invalid ) { m_hVerifiedNode = NULL; return false; } // Bail if AI is not going for cover or ambush. CAIWMFact factQuery; factQuery.SetFactType( kFact_Task ); factQuery.SetTaskType( kTask_Cover ); CAIWMFact* pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); if( !pFact ) { factQuery.SetTaskType( kTask_Ambush ); pFact = m_pAI->GetAIWorkingMemory()->FindWMFact( factQuery ); } if( !( pFact && ( pFact->GetConfidence( CAIWMFact::kFactMask_TaskType ) == 1.f ) ) ) { m_hVerifiedNode = NULL; return false; } // Bail if AI is scripted to go for cover. if( pFact->GetFactFlags() & kFactFlag_Scripted ) { m_hVerifiedNode = NULL; return false; } // We have already determined that we can get to this node without // crossing the target's position. HOBJECT hNode = pFact->GetTargetObject(); if( hNode == m_hVerifiedNode ) { // AI is not going anywhere. CAIPathNavMesh* pNMPath = m_pAI->GetAINavigationMgr()->GetNMPath(); if( ( !pNMPath ) || ( !m_pAI->GetAINavigationMgr()->IsNavSet() ) || ( m_pAI->GetAIBlackBoard()->GetBBDestStatus() != kNav_Set ) ) { return false; } // Existing path is still valid. Path does not cross target. if( !PathIncludesPoly( *pNMPath, ePolyTarget ) ) { return false; } } // No path exists to node. *pbCalledFindPath = true; static CAIPathNavMesh NMPath; if( !FindPathToNode( hNode, &NMPath ) ) { return false; } // Path does cross target's position. if( PathIncludesPoly( NMPath, ePolyTarget ) ) { AvoidNode( hNode ); return true; } // Path does not cross target's position. m_hVerifiedNode = hNode; return false; }