//----------------------------------------------------------------------------- // Purpose: Checks if a local route (not using nodes) between vStart // and vEnd exists using the moveType // Input : // Output : Returns a route if sucessful or NULL if no local route was possible //----------------------------------------------------------------------------- bool CAI_Pathfinder::CheckStaleRoute(const Vector &vStart, const Vector &vEnd, int moveTypes) { // ------------------------------------------------------------------- // First try to go there directly // ------------------------------------------------------------------- if (moveTypes & bits_CAP_MOVE_GROUND) { if (CheckStaleNavTypeRoute( NAV_GROUND, vStart, vEnd )) return true; } // ------------------------------------------------------------------- // First try to go there directly // ------------------------------------------------------------------- if (moveTypes & bits_CAP_MOVE_FLY) { if (CheckStaleNavTypeRoute( NAV_FLY, vStart, vEnd )) return true; } // -------------------------------------------------------------- // Try to jump if we can jump to a node // -------------------------------------------------------------- if (moveTypes & bits_CAP_MOVE_JUMP) { AIMoveTrace_t moveTrace; GetOuter()->GetMoveProbe()->MoveLimit( NAV_JUMP, vStart, vEnd, MASK_NPCSOLID, NULL, &moveTrace); if (!IsMoveBlocked(moveTrace)) { return true; } else { // Can't tell jump up from jump down at this point GetOuter()->GetMoveProbe()->MoveLimit( NAV_JUMP, vEnd, vStart, MASK_NPCSOLID, NULL, &moveTrace); if (!IsMoveBlocked(moveTrace)) return true; } } // -------------------------------------------------------------- // Try to climb if we can climb to a node // -------------------------------------------------------------- if (moveTypes & bits_CAP_MOVE_CLIMB) { AIMoveTrace_t moveTrace; GetOuter()->GetMoveProbe()->MoveLimit( NAV_CLIMB, vStart, vEnd, MASK_NPCSOLID, NULL, &moveTrace); if (!IsMoveBlocked(moveTrace)) { return true; } } // Man do we suck! Couldn't get there by any route return false; }
void CNPC_Stalker::AddZigZagToPath(void) { // If already on a detour don't add a zigzag if (GetNavigator()->GetCurWaypointFlags() & bits_WP_TO_DETOUR) { return; } // If enemy isn't facing me or occluded, don't add a zigzag if (HasCondition(COND_ENEMY_OCCLUDED) || !HasCondition ( COND_ENEMY_FACING_ME )) { return; } Vector waypointPos = GetNavigator()->GetCurWaypointPos(); Vector waypointDir = (waypointPos - GetAbsOrigin()); // If the distance to the next node is greater than ZIG_ZAG_SIZE // then add a random zig/zag to the path if (waypointDir.LengthSqr() > ZIG_ZAG_SIZE) { // Pick a random distance for the zigzag (less that sqrt(ZIG_ZAG_SIZE) float distance = random->RandomFloat( 30, 60 ); // Get me a vector orthogonal to the direction of motion VectorNormalize( waypointDir ); Vector vDirUp(0,0,1); Vector vDir; CrossProduct( waypointDir, vDirUp, vDir); // Pick a random direction (left/right) for the zigzag if (random->RandomInt(0,1)) { vDir = -1 * vDir; } // Get zigzag position in direction of target waypoint Vector zigZagPos = GetAbsOrigin() + waypointDir * 60; // Now offset zigZagPos = zigZagPos + (vDir * distance); // Now make sure that we can still get to the zigzag position and the waypoint AIMoveTrace_t moveTrace1, moveTrace2; GetMoveProbe()->MoveLimit( NAV_GROUND, GetAbsOrigin(), zigZagPos, GetAITraceMask(), NULL, &moveTrace1); GetMoveProbe()->MoveLimit( NAV_GROUND, zigZagPos, waypointPos, GetAITraceMask(), NULL, &moveTrace2); if ( !IsMoveBlocked( moveTrace1 ) && !IsMoveBlocked( moveTrace2 ) ) { GetNavigator()->PrependWaypoint( zigZagPos, NAV_GROUND, bits_WP_TO_DETOUR ); } } }
//----------------------------------------------------------------------------- // Step iteratively toward a destination position //----------------------------------------------------------------------------- AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, AIMoveTrace_t *pTraceResult ) { // By definition, this will produce different results than GroundMoveLimit() // because there's no guarantee that it will step exactly one step // See how far toward the new position we can step... // But don't actually test for ground geometric validity; // if it isn't valid, there's not much we can do about it AIMoveTrace_t moveTrace; GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, MASK_NPCSOLID, AITGM_IGNORE_FLOOR, &moveTrace ); if ( pTraceResult ) { *pTraceResult = moveTrace; } bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction )); // Move forward either if there was no obstruction or if we're told to // move as far as we can, regardless bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus); if ( !bIsBlocked || bAsFarAsCan || bHitTarget ) { // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); // skip tiny steps, but notify the shadow object of any large steps if ( moveTrace.flStepUpDistance > 0.1f ) { float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() ); IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); if ( pPhysicsObject ) { IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController(); if ( pShadow ) { pShadow->StepUp( height ); } } } if ( yaw != -1 ) { QAngle angles = GetLocalAngles(); angles.y = yaw; SetLocalAngles( angles ); } if ( bHitTarget ) return AIM_PARTIAL_HIT_TARGET; if ( !bIsBlocked ) return AIM_SUCCESS; if ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) return AIM_PARTIAL_HIT_NPC; return AIM_PARTIAL_HIT_WORLD; } return AIM_FAILED; }
//----------------------------------------------------------------------------- // Purpose: Handles movement towards the last move target. // Input : flInterval - //----------------------------------------------------------------------------- bool CNPC_Controller::OverridePathMove( float flInterval ) { CBaseEntity *pMoveTarget = (GetTarget()) ? GetTarget() : GetEnemy(); Vector waypointDir = GetNavigator()->GetCurWaypointPos() - GetLocalOrigin(); float flWaypointDist = waypointDir.Length2D(); VectorNormalize(waypointDir); // cut corner? if (flWaypointDist < 128) { if (m_flGroundSpeed > 100) m_flGroundSpeed -= 40; } else { if (m_flGroundSpeed < 400) m_flGroundSpeed += 10; } m_velocity = m_velocity * 0.8 + m_flGroundSpeed * waypointDir * 0.5; SetAbsVelocity( m_velocity ); // ----------------------------------------------------------------- // Check route is blocked // ------------------------------------------------------------------ Vector checkPos = GetLocalOrigin() + (waypointDir * (m_flGroundSpeed * flInterval)); AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_FLY, GetLocalOrigin(), checkPos, MASK_NPCSOLID|CONTENTS_WATER, pMoveTarget, &moveTrace); if (IsMoveBlocked( moveTrace )) { TaskFail(FAIL_NO_ROUTE); GetNavigator()->ClearGoal(); return true; } // ---------------------------------------------- Vector lastPatrolDir = GetNavigator()->GetCurWaypointPos() - GetLocalOrigin(); if ( ProgressFlyPath( flInterval, pMoveTarget, MASK_NPCSOLID, false, 64 ) == AINPP_COMPLETE ) { { m_vLastPatrolDir = lastPatrolDir; VectorNormalize(m_vLastPatrolDir); } return true; } return false; }
// make this alien jump off the head of the ent he's standing on bool CASW_Alien_Jumper::DoJumpOffHead() { //Too soon to try to jump if ( m_flJumpTime > gpGlobals->curtime ) return false; if (!(GetGroundEntity()) || GetNavType() == NAV_JUMP ) return false; // force this drone to have jumping capabilities m_bDisableJump = false; CapabilitiesAdd( bits_CAP_MOVE_JUMP ); //Vector vecDest = RandomVector(-1, 1); //vecDest.z = 0; //vecDest *= random->RandomFloat(30, 100); Vector vecDest; AngleVectors(GetAbsAngles(), &vecDest); vecDest *= 200.0f; vecDest += GetAbsOrigin(); // Try the jump AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), vecDest, MASK_NPCSOLID, NULL, &moveTrace ); //See if it succeeded if ( IsMoveBlocked( moveTrace.fStatus ) ) { if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( vecDest, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), vecDest, 255, 0, 0, 0, 5 ); } m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); return false; } if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( vecDest, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), vecDest, 0, 255, 0, 0, 5 ); } //Save this jump in case the next time fails m_vecSavedJump = moveTrace.vJumpVelocity; m_vecLastJumpAttempt = vecDest; SetSchedule(SCHED_ASW_ALIEN_JUMP); m_bForcedStuckJump = true; return true; }
AIMotorMoveResult_t CAI_Motor::MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult ) { // turn in the direction of movement MoveFacing( move ); // calc accel/decel rates float flNewSpeed = GetIdealSpeed(); SetMoveVel( move.dir * flNewSpeed ); float flTotal = 0.5 * (GetCurSpeed() + flNewSpeed) * GetMoveInterval(); float distance = move.maxDist; // can I move farther in this interval than I'm supposed to? if (flTotal > distance) { // only use a portion of the time interval SetMoveInterval( GetMoveInterval() * (1 - distance / flTotal) ); flTotal = distance; } else { // use all the time SetMoveInterval( 0 ); } Vector vecStart, vecEnd; vecStart = GetLocalOrigin(); VectorMA( vecStart, flTotal, move.dir, vecEnd ); AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_FLY, vecStart, vecEnd, MASK_NPCSOLID, NULL, &moveTrace ); if ( pTraceResult ) *pTraceResult = moveTrace; // Check for total blockage if (fabs(moveTrace.flDistObstructed - flTotal) <= 1e-1) { // But if we bumped into our target, then we succeeded! if ( move.pMoveTarget && (moveTrace.pObstruction == move.pMoveTarget) ) return AIM_PARTIAL_HIT_TARGET; return AIM_FAILED; } // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); return (IsMoveBlocked(moveTrace.fStatus)) ? AIM_PARTIAL_HIT_WORLD : AIM_SUCCESS; }
bool CZombie::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult ) { if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) ) { if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin ) { m_hBlockingDoor = pDoor; m_flDoorBashYaw = UTIL_VecToYaw( pMoveGoal->directTrace.vHitNormal * -1 ); } return true; } return false; }
//----------------------------------------------------------------------------- // Checks a stale navtype route //----------------------------------------------------------------------------- bool CAI_Pathfinder::CheckStaleNavTypeRoute( Navigation_t navType, const Vector &vStart, const Vector &vEnd ) { AIMoveTrace_t moveTrace; GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, MASK_NPCSOLID, NULL, 100, AIMLF_IGNORE_TRANSIENTS, &moveTrace); // Is the direct route clear? if (!IsMoveBlocked(moveTrace)) { return true; } // Next try to triangulate // FIXME: Since blocked dist is an unreliable number, this computation is bogus Vector vecDelta; VectorSubtract( vEnd, vStart, vecDelta ); float flTotalDist = vecDelta.Length(); Vector vApex; if (Triangulate( navType, vStart, vEnd, flTotalDist - moveTrace.flDistObstructed, NULL, &vApex )) { return true; } CEntity *cent = CEntity::Instance(moveTrace.pObstruction); // Try a giveway request, if I can get there ignoring NPCs if ( cent && cent->MyNPCPointer() ) { GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, MASK_NPCSOLID_BRUSHONLY, NULL, &moveTrace); if (!IsMoveBlocked(moveTrace)) { return true; } } return false; }
bool CASW_Alien_Jumper::DoJumpTo(Vector &vecDest) { //Too soon to try to jump if ( m_flJumpTime > gpGlobals->curtime ) return false; // only jump if you're on the ground if (!(GetFlags() & FL_ONGROUND) || GetNavType() == NAV_JUMP ) return false; // Don't jump if I'm not allowed if ( ( CapabilitiesGet() & bits_CAP_MOVE_JUMP ) == false ) return false; // Try the jump AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), vecDest, MASK_NPCSOLID, NULL, &moveTrace ); //See if it succeeded if ( IsMoveBlocked( moveTrace.fStatus ) ) { if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( vecDest, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), vecDest, 255, 0, 0, 0, 5 ); } m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); return false; } if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( vecDest, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), vecDest, 0, 255, 0, 0, 5 ); } //Save this jump in case the next time fails m_vecSavedJump = moveTrace.vJumpVelocity; m_vecLastJumpAttempt = vecDest; SetSchedule(SCHED_ASW_ALIEN_JUMP); m_bForcedStuckJump = true; //Msg("Drone saving jump vec %f %f %f\n", m_vecSavedJump.x, m_vecSavedJump.y, m_vecSavedJump.z); return true; }
bool CAI_PlaneSolver::Solve( const AILocalMoveGoal_t &goal, float distClear, Vector *pSolution ) { bool solved = false; //--------------------------------- if ( goal.speed == 0 ) return false; if ( DetectUnsolvable( goal ) ) return false; //--------------------------------- bool fVeryClose = ( distClear < 1.0 ); float degreesPositiveArc = ( !fVeryClose ) ? DEGREES_POSITIVE_ARC : DEGREES_POSITIVE_ARC_CLOSE_OBSTRUCTION; float probeDist = CalcProbeDist( goal.speed ); if ( goal.flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) { probeDist = min( goal.maxDist, probeDist ); } if ( GenerateObstacleSuggestions( goal, goal.directTrace, distClear, probeDist, degreesPositiveArc, NUM_PROBES ) != SR_FAIL ) { if ( RunMoveSolver( goal, goal.directTrace, degreesPositiveArc, !fVeryClose, pSolution ) ) { // Visualize desired + actual directions VisualizeSolution( goal.dir, *pSolution ); AIMoveTrace_t moveTrace; float requiredMovement = goal.speed * GetMotor()->GetMoveInterval(); MoveLimit( goal.navType, GetLocalOrigin() + *pSolution * requiredMovement, false, true, &moveTrace ); if ( !IsMoveBlocked( moveTrace ) ) solved = true; else solved = false; } } m_fSolvedPrev = ( solved && goal.speed != 0 ); // a solution found when speed is zero is not meaningful m_PrevTarget = goal.target; return solved; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHL1NPCTalker::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult ) { // If we can't get through the door, try and open it if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) ) { if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin ) { // Can't do anything if the door's locked if ( !pDoor->m_bLocked && !pDoor->HasSpawnFlags(SF_DOOR_NONPCS) ) { // Tell the door to open variant_t emptyVariant; pDoor->AcceptInput( "Open", this, this, emptyVariant, USE_TOGGLE ); *pResult = AIMR_OK; } } return true; } return false; }
AIMoveResult_t CAI_LocalNavigator::MoveCalcRaw( AILocalMoveGoal_t *pMoveGoal, bool bOnlyCurThink ) { AI_PROFILE_SCOPE(CAI_Motor_MoveCalc); AIMoveResult_t result = AIMR_OK; // Assume success AIMoveTrace_t directTrace; float distClear; // -------------------------------------------------- bool bDirectClear = MoveCalcDirect( pMoveGoal, bOnlyCurThink, &distClear, &result); if ( OnCalcBaseMove( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } bool bShouldSteer = ( !(pMoveGoal->flags & AILMG_NO_STEER) && ( !bDirectClear || HaveObstacles() ) ); if ( bDirectClear && !bShouldSteer ) { SetSolveCookie(); return DbgResult( result ); } // -------------------------------------------------- if ( bShouldSteer ) { if ( !bDirectClear ) { if ( OnObstructionPreSteer( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } } if ( MoveCalcSteer( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } } if ( OnFailedSteer( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } // -------------------------------------------------- if ( OnFailedLocalNavigation( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } if ( distClear < GetOuter()->GetMotor()->MinStoppingDist() ) { if ( OnInsufficientStopDist( pMoveGoal, distClear, &result ) ) { SetSolveCookie(); return DbgResult( result ); } if ( MoveCalcStop( pMoveGoal, distClear, &result) ) { SetSolveCookie(); return DbgResult( result ); } } // A hopeful result... may get in trouble at next waypoint and obstruction is still there if ( distClear > pMoveGoal->curExpectedDist ) { SetSolveCookie(); return DbgResult( AIMR_OK ); } // -------------------------------------------------- DebugNoteMovementFailure(); SetSolveCookie(); return DbgResult( IsMoveBlocked( pMoveGoal->directTrace.fStatus ) ? pMoveGoal->directTrace.fStatus : AIMR_ILLEGAL ); }
//----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CASW_Alien_Jumper::ShouldJump( void ) { if ( GetEnemy() == NULL ) return false; // don't jump if we're stunned if (m_bElectroStunned) return false; //Too soon to try to jump if ( m_flJumpTime > gpGlobals->curtime ) return false; // only jump if you're on the ground if (!(GetFlags() & FL_ONGROUND) || GetNavType() == NAV_JUMP ) return false; // Don't jump if I'm not allowed if ( ( CapabilitiesGet() & bits_CAP_MOVE_JUMP ) == false ) return false; Vector vEnemyForward, vForward; GetEnemy()->GetVectors( &vEnemyForward, NULL, NULL ); GetVectors( &vForward, NULL, NULL ); float flDot = DotProduct( vForward, vEnemyForward ); if ( flDot < 0.5f ) flDot = 0.5f; Vector vecPredictedPos; //Get our likely position in two seconds UTIL_PredictedPosition( GetEnemy(), flDot * 2.5f, &vecPredictedPos ); // Don't jump if we're already near the target if ( ( GetAbsOrigin() - vecPredictedPos ).LengthSqr() < (512*512) ) return false; //Don't retest if the target hasn't moved enough //FIXME: Check your own distance from last attempt as well if ( ( ( m_vecLastJumpAttempt - vecPredictedPos ).LengthSqr() ) < (128*128) ) { m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); return false; } Vector targetDir = ( vecPredictedPos - GetAbsOrigin() ); float flDist = VectorNormalize( targetDir ); // don't jump at target it it's very close if (flDist < ANTLION_JUMP_MIN) return false; Vector targetPos = vecPredictedPos + ( targetDir * (GetHullWidth()*4.0f) ); // Try the jump AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), targetPos, MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace ); //See if it succeeded if ( IsMoveBlocked( moveTrace.fStatus ) ) { if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), targetPos, 255, 0, 0, 0, 5 ); } m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); return false; } if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 ); NDebugOverlay::Line( GetAbsOrigin(), targetPos, 0, 255, 0, 0, 5 ); } //Save this jump in case the next time fails m_vecSavedJump = moveTrace.vJumpVelocity; m_vecLastJumpAttempt = targetPos; return true; }
//----------------------------------------------------------------------------- // Step iteratively toward a destination position //----------------------------------------------------------------------------- AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, bool bTestZ, AIMoveTrace_t *pTraceResult ) { // By definition, this will produce different results than GroundMoveLimit() // because there's no guarantee that it will step exactly one step // See how far toward the new position we can step... // But don't actually test for ground geometric validity; // if it isn't valid, there's not much we can do about it AIMoveTrace_t moveTrace; unsigned testFlags = AITGM_IGNORE_FLOOR; char *pchHackBoolToInt = (char*)(&bTestZ); if ( *pchHackBoolToInt == 2 ) { testFlags |= AITGM_CRAWL_LARGE_STEPS; } else { if ( !bTestZ ) testFlags |= AITGM_2D; } #ifdef DEBUG if ( ai_draw_motor_movement.GetBool() ) testFlags |= AITGM_DRAW_RESULTS; #endif GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, GetOuter()->GetAITraceMask(), testFlags, &moveTrace ); if ( pTraceResult ) { *pTraceResult = moveTrace; } bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction )); // Move forward either if there was no obstruction or if we're told to // move as far as we can, regardless bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus); if ( !bIsBlocked || bAsFarAsCan || bHitTarget ) { #ifdef DEBUG if ( GetMoveProbe()->CheckStandPosition( GetLocalOrigin(), GetOuter()->GetAITraceMask() ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, GetOuter()->GetAITraceMask() ) ) { DevMsg( 2, "Warning: AI motor probably given invalid instructions\n" ); } #endif // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); // check to see if our ground entity has changed // NOTE: This is to detect changes in ground entity as the movement code has optimized out // ground checks. So now we have to do a simple recheck to make sure we detect when we've // stepped onto a new entity. if ( GetOuter()->GetFlags() & FL_ONGROUND ) { GetOuter()->PhysicsStepRecheckGround(); } // skip tiny steps, but notify the shadow object of any large steps if ( moveTrace.flStepUpDistance > 0.1f ) { float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() ); IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); if ( pPhysicsObject ) { IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController(); if ( pShadow ) { pShadow->StepUp( height ); } } } if ( yaw != -1 ) { QAngle angles = GetLocalAngles(); angles.y = yaw; SetLocalAngles( angles ); } if ( bHitTarget ) return AIM_PARTIAL_HIT_TARGET; if ( !bIsBlocked ) return AIM_SUCCESS; if ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) return AIM_PARTIAL_HIT_NPC; return AIM_PARTIAL_HIT_WORLD; } return AIM_FAILED; }
bool CAI_Pathfinder::Triangulate( Navigation_t navType, const Vector &vecStart, const Vector &vecEndIn, float flDistToBlocker, const CEntity *pTargetEnt, Vector *pApex ) { if ( GetOuter()->IsFlaggedEfficient() ) return false; Assert( pApex ); Vector vecForward, vecUp, vecPerpendicular; VectorSubtract( vecEndIn, vecStart, vecForward ); float flTotalDist = VectorNormalize( vecForward ); Vector vecEnd; // If we're walking, then don't try to triangulate over large distances if ( navType != NAV_FLY && flTotalDist > MAX_TRIAGULATION_DIST) { vecEnd = vecForward * MAX_TRIAGULATION_DIST; flTotalDist = MAX_TRIAGULATION_DIST; if ( !GetOuter()->GetMoveProbe()->MoveLimit(navType, vecEnd, vecEndIn, MASK_NPCSOLID, pTargetEnt) ) { return false; } } else vecEnd = vecEndIn; // Compute a direction vector perpendicular to the desired motion direction if ( 1.0f - fabs(vecForward.z) > 1e-3 ) { vecUp.Init( 0, 0, 1 ); CrossProduct( vecForward, vecUp, vecPerpendicular ); // Orthogonal to facing } else { vecUp.Init( 0, 1, 0 ); vecPerpendicular.Init( 1, 0, 0 ); } // Grab the size of the navigation bounding box float sizeX = 0.5f * NAI_Hull::Length(GetHullType()); float sizeZ = 0.5f * NAI_Hull::Height(GetHullType()); // start checking right about where the object is, picking two equidistant // starting points, one on the left, one on the right. As we progress // through the loop, we'll push these away from the obstacle, hoping to // find a way around on either side. m_vecSize.x is added to the ApexDist // in order to help select an apex point that insures that the NPC is // sufficiently past the obstacle before trying to turn back onto its original course. float flApexDist = flDistToBlocker + sizeX; if (flApexDist > flTotalDist) { flApexDist = flTotalDist; } // Compute triangulation apex points (NAV_FLY attempts vertical triangulation too) Vector vecDelta[2]; Vector vecApex[4]; float pApexDist[4]; Vector vecCenter; int nNumToTest = 2; VectorMultiply( vecPerpendicular, sizeX, vecDelta[0] ); VectorMA( vecStart, flApexDist, vecForward, vecCenter ); VectorSubtract( vecCenter, vecDelta[0], vecApex[0] ); VectorAdd( vecCenter, vecDelta[0], vecApex[1] ); vecDelta[0] *= 2.0f; pApexDist[0] = pApexDist[1] = flApexDist; if (navType == NAV_FLY) { VectorMultiply( vecUp, 3.0f * sizeZ, vecDelta[1] ); VectorSubtract( vecCenter, vecDelta[1], vecApex[2] ); VectorAdd( vecCenter, vecDelta[1], vecApex[3] ); pApexDist[2] = pApexDist[3] = flApexDist; nNumToTest = 4; } AIMoveTrace_t moveTrace; for (int i = 0; i < 2; ++i ) { // NOTE: Do reverse order so fliers try to move over the top first for (int j = nNumToTest; --j >= 0; ) { if (TestTriangulationRoute(navType, vecStart, vecApex[j], vecEnd, pTargetEnt, &moveTrace)) { *pApex = vecApex[j]; return true; } // Here, the starting half of the triangle was blocked. Lets // pull back the apex toward the start... if (IsMoveBlocked(moveTrace)) { Vector vecStartToObstruction; VectorSubtract( moveTrace.vEndPosition, vecStart, vecStartToObstruction ); float flDistToObstruction = DotProduct( vecStartToObstruction, vecForward ); float flNewApexDist = pApexDist[j]; if (pApexDist[j] > flDistToObstruction) flNewApexDist = flDistToObstruction; VectorMA( vecApex[j], flNewApexDist - pApexDist[j], vecForward, vecApex[j] ); pApexDist[j] = flNewApexDist; } // NOTE: This has to occur *after* the code above because // the above code uses vecApex for some distance computations if (j & 0x1) vecApex[j] += vecDelta[j >> 1]; else vecApex[j] -= vecDelta[j >> 1]; } }
AIMotorMoveResult_t CAI_BlendedMotor::MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult ) { if ( move.curExpectedDist < 0.001 ) return BaseClass::MoveFlyExecute( move, pTraceResult ); BuildMoveScript( move, pTraceResult ); float flNewSpeed = GetCurSpeed(); float flTotalDist = GetMoveScriptDist( flNewSpeed ); Assert( move.maxDist < 0.01 || flTotalDist > 0.0 ); // -------------------------------------------- // turn in the direction of movement // -------------------------------------------- float flNewYaw = GetMoveScriptYaw( ); // get facing based on movement yaw AILocalMoveGoal_t move2 = move; move2.facing = UTIL_YawToVector( flNewYaw ); // turn in the direction needed MoveFacing( move2 ); GetOuter()->m_flGroundSpeed = GetSequenceGroundSpeed( GetSequence()); SetMoveScriptAnim( flNewSpeed ); // DevMsg( "%6.2f : Speed %.1f : %.1f\n", gpGlobals->curtime, flNewSpeed, GetIdealSpeed() ); // reset actual "sequence" ground speed based current movement sequence, orientation // FIXME: the above is redundant with MoveGroundExecute, and the below is a mix of MoveGroundExecuteWalk and MoveFlyExecute bool bReachingLocalGoal = ( flTotalDist > move.maxDist ); // can I move farther in this interval than I'm supposed to? if ( bReachingLocalGoal ) { if ( !(move.flags & AILMG_CONSUME_INTERVAL) ) { // only use a portion of the time interval SetMoveInterval( GetMoveInterval() * (1 - move.maxDist / flTotalDist) ); } else SetMoveInterval( 0 ); flTotalDist = move.maxDist; } else { // use all the time SetMoveInterval( 0 ); } SetMoveVel( move.dir * flNewSpeed ); // orig Vector vecStart, vecEnd; vecStart = GetLocalOrigin(); VectorMA( vecStart, flTotalDist, move.dir, vecEnd ); AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_FLY, vecStart, vecEnd, MASK_NPCSOLID, NULL, &moveTrace ); if ( pTraceResult ) *pTraceResult = moveTrace; // Check for total blockage if (fabs(moveTrace.flDistObstructed - flTotalDist) <= 1e-1) { // But if we bumped into our target, then we succeeded! if ( move.pMoveTarget && (moveTrace.pObstruction == move.pMoveTarget) ) return AIM_PARTIAL_HIT_TARGET; return AIM_FAILED; } // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); return (IsMoveBlocked(moveTrace.fStatus)) ? AIM_PARTIAL_HIT_WORLD : AIM_SUCCESS; }
//----------------------------------------------------------------------------- // Purpose: Handles all flight movement. // Input : flInterval - Seconds to simulate. //----------------------------------------------------------------------------- void CNPC_Crow::MoveCrowFly( float flInterval ) { // // Bound interval so we don't get ludicrous motion when debugging // or when framerate drops catastrophically. // if (flInterval > 1.0) { flInterval = 1.0; } m_flDangerSoundTime = gpGlobals->curtime + 5.0f; // // Determine the goal of our movement. // Vector vecMoveGoal = GetAbsOrigin(); if ( GetNavigator()->IsGoalActive() ) { vecMoveGoal = GetNavigator()->GetCurWaypointPos(); if ( GetNavigator()->CurWaypointIsGoal() == false ) { AI_ProgressFlyPathParams_t params( MASK_NPCSOLID ); params.bTrySimplify = false; GetNavigator()->ProgressFlyPath( params ); // ignore result, crow handles completion directly // Fly towards the hint. if ( GetNavigator()->GetPath()->GetCurWaypoint() ) { vecMoveGoal = GetNavigator()->GetCurWaypointPos(); } } } else { // No movement goal. vecMoveGoal = GetAbsOrigin(); SetAbsVelocity( vec3_origin ); return; } Vector vecMoveDir = ( vecMoveGoal - GetAbsOrigin() ); Vector vForward; AngleVectors( GetAbsAngles(), &vForward ); // // Fly towards the movement goal. // float flDistance = ( vecMoveGoal - GetAbsOrigin() ).Length(); if ( vecMoveGoal != m_vDesiredTarget ) { m_vDesiredTarget = vecMoveGoal; } else { m_vCurrentTarget = ( m_vDesiredTarget - GetAbsOrigin() ); VectorNormalize( m_vCurrentTarget ); } float flLerpMod = 0.25f; if ( flDistance <= 256.0f ) { flLerpMod = 1.0f - ( flDistance / 256.0f ); } VectorLerp( vForward, m_vCurrentTarget, flLerpMod, vForward ); if ( flDistance < CROW_AIRSPEED * flInterval ) { if ( GetNavigator()->IsGoalActive() ) { if ( GetNavigator()->CurWaypointIsGoal() ) { m_bReachedMoveGoal = true; } else { GetNavigator()->AdvancePath(); } } else m_bReachedMoveGoal = true; } if ( GetHintNode() ) { AIMoveTrace_t moveTrace; GetMoveProbe()->MoveLimit( NAV_FLY, GetAbsOrigin(), GetNavigator()->GetCurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace ); //See if it succeeded if ( IsMoveBlocked( moveTrace.fStatus ) ) { Vector vNodePos = vecMoveGoal; GetHintNode()->GetPosition(this, &vNodePos); GetNavigator()->SetGoal( vNodePos ); } } // // Look to see if we are going to hit anything. // VectorNormalize( vForward ); Vector vecDeflect; if ( Probe( vForward, CROW_AIRSPEED * flInterval, vecDeflect ) ) { vForward = vecDeflect; VectorNormalize( vForward ); } SetAbsVelocity( vForward * CROW_AIRSPEED ); if ( GetAbsVelocity().Length() > 0 && GetNavigator()->CurWaypointIsGoal() && flDistance < CROW_AIRSPEED ) { SetIdealActivity( (Activity)ACT_CROW_LAND ); } //Bank and set angles. Vector vRight; QAngle vRollAngle; VectorAngles( vForward, vRollAngle ); vRollAngle.z = 0; AngleVectors( vRollAngle, NULL, &vRight, NULL ); float flRoll = DotProduct( vRight, vecMoveDir ) * 45; flRoll = clamp( flRoll, -45, 45 ); vRollAngle[ROLL] = flRoll; SetAbsAngles( vRollAngle ); }