//----------------------------------------------------------------------------- // 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; }
//----------------------------------------------------------------------------- // 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; }
//----------------------------------------------------------------------------- // Purpose: // Input : *pTask - //----------------------------------------------------------------------------- void CNPC_AntlionGrub::StartTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_ANTLIONGRUB_FIND_RETREAT_GOAL: { if ( GetEnemy() == NULL ) { TaskFail( FAIL_NO_ENEMY ); return; } Vector testPos, testPos2, threatDir; trace_t tr; //Find the direction to our enemy threatDir = ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ); VectorNormalize( threatDir ); //Find a position farther out away from our enemy VectorMA( GetAbsOrigin(), random->RandomInt( 32, 128 ), threatDir, testPos ); testPos[2] += StepHeight()*2.0f; testPos2 = testPos; testPos2[2] -= StepHeight()*2.0f; //Check the position AI_TraceLine( testPos, testPos2, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); //Must be clear if ( ( tr.startsolid ) || ( tr.allsolid ) || ( tr.fraction == 1.0f ) ) { TaskFail( FAIL_NO_ROUTE ); return; } //Save the position and go m_vSavePosition = tr.endpos; TaskComplete(); } break; case TASK_ANTLIONGRUB_MOVE_TO_TARGET: if ( GetEnemy() == NULL) { TaskFail( FAIL_NO_TARGET ); } else if ( ( GetEnemy()->GetLocalOrigin() - GetLocalOrigin()).Length() < pTask->flTaskData ) { TaskComplete(); } break; case TASK_ANTLIONGRUB_GIVE_HEALTH: m_bHealing = true; SetActivity( (Activity) ACT_ANTLIONGRUB_HEAL ); //CSoundEnvelopeController::GetController().SoundChangeVolume( m_pHealSound, 0.5f, 2.0f ); //Must have a target if ( GetEnemy() == NULL ) { TaskFail( FAIL_NO_ENEMY ); return; } //Must be within range if ( (GetEnemy()->GetLocalOrigin() - GetLocalOrigin()).Length() > 92 ) { TaskFail( FAIL_NO_ENEMY ); } break; case TASK_ANTLIONGRUB_SQUIRM: { //Pick a squirm movement to perform Vector vecStart; //Move randomly around, and start a step's height above our current position vecStart.Random( -32.0f, 32.0f ); vecStart[2] = StepHeight(); vecStart += GetLocalOrigin(); //Look straight down for the ground Vector vecEnd = vecStart; vecEnd[2] -= StepHeight()*2.0f; trace_t tr; //Check the position //FIXME: Trace by the entity's hull size? AI_TraceLine( vecStart, vecEnd, MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); //See if we can move there if ( ( tr.fraction == 1.0f ) || ( tr.startsolid ) || ( tr.allsolid ) ) { TaskFail( FAIL_NO_ROUTE ); return; } m_vSavePosition = tr.endpos; TaskComplete(); } break; default: BaseClass::StartTask( pTask ); break; } }