AIMotorMoveResult_t CAI_BlendedMotor::MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult ) { if ( move.curExpectedDist < 0.001 ) { AIMotorMoveResult_t result = BaseClass::MoveGroundExecute( move, pTraceResult ); // Msg(" BaseClass::MoveGroundExecute() - remaining %.2f\n", GetMoveInterval() ); SetMoveScriptAnim( 0.0 ); return result; } 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 ); // reset actual "sequence" ground speed based current movement sequence, orientation // FIXME: this should be based on GetOuter()->m_flGroundSpeed = GetSequenceGroundSpeed( GetSequence()); /* if (1 || flNewSpeed > GetIdealSpeed()) { // DevMsg( "%6.2f : Speed %.1f : %.1f (%.1f) : %d\n", gpGlobals->curtime, flNewSpeed, move.maxDist, move.transitionDist, GetOuter()->m_pHintNode != NULL ); // DevMsg( "%6.2f : Speed %.1f : %.1f\n", gpGlobals->curtime, flNewSpeed, GetIdealSpeed() ); } */ SetMoveScriptAnim( flNewSpeed ); /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { DevMsg( "%6.2f : Speed %.1f : %.1f : %.2f\n", gpGlobals->curtime, flNewSpeed, GetIdealSpeed(), flNewSpeed / GetIdealSpeed() ); } */ AIMotorMoveResult_t result = MoveGroundExecuteWalk( move, flNewSpeed, flTotalDist, pTraceResult ); return result; }
void CAI_StandoffBehavior::UpdateBattleLines() { if ( m_UpdateBattleLinesSemaphore.EnterThink() ) { // @TODO (toml 06-19-03): This is the quick to code thing. Could use some optimization/caching to not recalc everything (up to) each think m_BattleLines.RemoveAll(); bool bHaveGoalPosition = ( m_vecStandoffGoalPosition != GOAL_POSITION_INVALID ); if ( bHaveGoalPosition ) { // If we have a valid standoff goal position, it takes precendence. const float DIST_GOAL_PLANE = 180; BattleLine_t goalLine; if ( GetDirectionOfStandoff( &goalLine.normal ) ) { goalLine.point = GetStandoffGoalPosition() + goalLine.normal * DIST_GOAL_PLANE; m_BattleLines.AddToTail( goalLine ); } } else if ( PlayerIsLeading() && GetEnemy() ) { if ( m_params.fPlayerIsBattleline ) { const float DIST_PLAYER_PLANE = 180; CBaseEntity *pPlayer = UTIL_PlayerByIndex( 1 ); BattleLine_t playerLine; if ( GetDirectionOfStandoff( &playerLine.normal ) ) { playerLine.point = pPlayer->GetAbsOrigin() + playerLine.normal * DIST_PLAYER_PLANE; m_BattleLines.AddToTail( playerLine ); } } } CAI_BattleLine *pBattleLine = NULL; for (;;) { pBattleLine = (CAI_BattleLine *)gEntList.FindEntityByClassname( pBattleLine, "ai_battle_line" ); if ( !pBattleLine ) break; if ( pBattleLine->m_fActive && (!bHaveGoalPosition || !pBattleLine->m_fStrict ) && pBattleLine->Affects( GetOuter() ) ) { BattleLine_t battleLine; battleLine.point = pBattleLine->GetAbsOrigin(); battleLine.normal = UTIL_YawToVector( pBattleLine->GetAbsAngles().y ); m_BattleLines.AddToTail( battleLine ); } } } }
//----------------------------------------------------------------------------- // Visualization //----------------------------------------------------------------------------- void CAI_MoveSolver::VisualizeRegulations( const Vector& origin ) { if ( m_Regulations.Count() ) { CAI_MoveSuggestions regulations; regulations.AddVectorToTail( m_Regulations ); NormalizeSuggestions( ®ulations[0], (®ulations[0]) + regulations.Count() ); Vector side1, mid, side2; for (int i = regulations.Count(); --i >= 0; ) { // Compute the positions of the angles... float flMinAngle = regulations[i].arc.center - regulations[i].arc.span * 0.5f; float flMaxAngle = regulations[i].arc.center + regulations[i].arc.span * 0.5f; side1 = UTIL_YawToVector( flMinAngle ); side2 = UTIL_YawToVector( flMaxAngle ); mid = UTIL_YawToVector( regulations[i].arc.center ); // Stronger weighted ones are bigger if ( regulations[i].weight < 0 ) { float flLength = 10 + 40 * ( regulations[i].weight * -1.0); side1 *= flLength; side2 *= flLength; mid *= flLength; side1 += origin; side2 += origin; mid += origin; NDebugOverlay::Triangle(origin, mid, side1, 255, 0, 0, 48, true, 0.1f ); NDebugOverlay::Triangle(origin, side2, mid, 255, 0, 0, 48, true, 0.1f ); } } } }
// Эффект пыли для шагов крематора (аналогично шагам страйдера или охотника, но в меньших масштабах void CNPC_Cremator::FootstepEffect( const Vector &origin ) { trace_t tr; AI_TraceLine( origin, origin - Vector(0,0,0), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); float yaw = random->RandomInt(0,0); for ( int i = 0; i < 2; i++ ) { if ( UTIL_PointContents( tr.endpos + Vector( 0, 0, 1 ) ) & MASK_WATER ) { float flWaterZ = UTIL_FindWaterSurface( tr.endpos, tr.endpos.z, tr.endpos.z + 100.0f ); CEffectData data; data.m_fFlags = 0; data.m_vOrigin = tr.endpos; data.m_vOrigin.z = flWaterZ; data.m_vNormal = Vector( 0, 0, 1 ); data.m_flScale = random->RandomFloat( 10.0, 14.0 ); // Если крематор идет по неглубокой воде, образуются всплески. DispatchEffect( "watersplash", data ); } else { Vector dir = UTIL_YawToVector( yaw + i*180 ) * 10; VectorNormalize( dir ); dir.z = 0.25; VectorNormalize( dir ); g_pEffects->Dust( tr.endpos, dir, 12, 50 ); /*g_pEffects->FootprintDecal( tr.endpos, dir, 12, 50 ); virtual void FootprintDecal( IRecipientFilter& filer, float delay, const Vector *origin, const Vector* right, int entity, int index, unsigned char materialType ) = 0; virtual void Dust( IRecipientFilter& filer, float delay, const Vector &pos, const Vector &dir, float size, float speed ) = 0;*/ } } }
bool CAI_ScriptConditions::EvalPlayerBlockingActor( const EvalArgs_t &args ) { if ( m_fPlayerBlockingActor == TRS_NONE ) return true; #if 0 CAI_BaseNPC *pNpc = args.pActor->MyNPCPointer(); const float testDist = 30.0; Vector origin = args.pActor->WorldSpaceCenter(); Vector delta = UTIL_YawToVector( args.pActor->GetAngles().y ) * testDist; Vector vecAbsMins, vecAbsMaxs; args.pActor->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); bool intersect = IsBoxIntersectingRay( vecAbsMins, vecAbsMaxs, origin, delta ); #endif if ( m_fPlayerBlockingActor == TRS_FALSE ) return true; return false; // for now, never say player is blocking }
AI_SuggestorResult_t CAI_PlaneSolver::GenerateObstacleSuggestion( const AILocalMoveGoal_t &goal, float yawScanCenter, float probeDist, float spanPerProbe, int probeOffset) { AIMoveTrace_t moveTrace; float yawTest; float arcCenter; CalcYawsFromOffset( yawScanCenter, spanPerProbe, probeOffset, &yawTest, &arcCenter ); Vector probeDir = UTIL_YawToVector( yawTest ); float requiredMovement = goal.speed * GetMotor()->GetMoveInterval(); // Probe immediate move with footing, then look further out ignoring footing bool fTraceClear = true; if ( probeDist > requiredMovement ) { if ( !MoveLimit( goal.navType, GetLocalOrigin() + probeDir * requiredMovement, !ProbeForNpcs(), true, &moveTrace ) ) { fTraceClear = false; moveTrace.flDistObstructed = (probeDist - requiredMovement) + moveTrace.flDistObstructed; } } if ( fTraceClear ) { fTraceClear = MoveLimit( goal.navType, GetLocalOrigin() + probeDir * probeDist, !ProbeForNpcs(), false, &moveTrace ); } if ( !fTraceClear ) { GenerateSuggestionFromTrace( goal, moveTrace, probeDist, arcCenter, spanPerProbe, probeOffset ); return SR_OK; } return SR_NONE; }
AIMoveResult_t CAI_LocalNavigator::MoveCalc( AILocalMoveGoal_t *pMoveGoal, bool bPreviouslyValidated ) { bool bOnlyCurThink = ( bPreviouslyValidated && !HaveObstacles() ); AIMoveResult_t result = MoveCalcRaw( pMoveGoal, bOnlyCurThink ); if ( pMoveGoal->curExpectedDist > pMoveGoal->maxDist ) pMoveGoal->curExpectedDist = pMoveGoal->maxDist; // If success, try to dampen really fast turning movement if ( result == AIMR_OK) { float interval = GetOuter()->GetMotor()->GetMoveInterval(); float currentYaw = UTIL_AngleMod( GetLocalAngles().y ); float goalYaw; float deltaYaw; float speed; float clampedYaw; // Clamp yaw goalYaw = UTIL_VecToYaw( pMoveGoal->facing ); deltaYaw = fabs( UTIL_AngleDiff( goalYaw, currentYaw ) ); if ( deltaYaw > 15 ) { speed = deltaYaw * 4.0; // i.e., any maneuver takes a quarter a second clampedYaw = AI_ClampYaw( speed, currentYaw, goalYaw, interval ); if ( clampedYaw != goalYaw ) { pMoveGoal->facing = UTIL_YawToVector( clampedYaw ); } } } return result; }
CAI_BaseNPC *CASW_Sentry_Top_Cannon::SelectOptimalEnemy() { // prioritize unfrozen aliens who are going to leave the cone soon. // prioritize aliens less the more frozen they get. CUtlVectorFixedGrowable< CAI_BaseNPC *,16 > candidates; CUtlVectorFixedGrowable< float, 16 > candidatescores; // search through all npcs, any that are in LOS and have health CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) { if (ppAIs[i]->GetHealth() > 0 && CanSee(ppAIs[i])) { // don't shoot marines if ( !asw_sentry_friendly_target.GetBool() && ppAIs[i]->Classify() == CLASS_ASW_MARINE ) continue; if ( ppAIs[i]->Classify() == CLASS_SCANNER ) continue; if ( !IsValidEnemy( ppAIs[i] ) ) continue; candidates.AddToTail( ppAIs[i] ); } } // bail out if we don't have anyone if ( candidates.Count() < 1 ) return NULL; else if ( candidates.Count() == 1 ) // just one candidate is an obvious result return candidates[0]; // score each of the candidates candidatescores.EnsureCount( candidates.Count() ); for ( int i = candidates.Count() - 1; i >= 0 ; --i ) { CAI_BaseNPC * RESTRICT pCandidate = candidates[i]; // is the candidate moving into or out of the cone? Vector vCandVel = GetEnemyVelocity(pCandidate); Vector vMeToTarget = pCandidate->GetAbsOrigin() - GetFiringPosition(); Vector vBaseForward = UTIL_YawToVector( m_fDeployYaw ); // crush everything to 2d for simplicity vMeToTarget.z = 0.0f; vCandVel.z = 0.0f; vBaseForward.z = 0.0f; Vector velCross = vBaseForward.Cross(vCandVel); // this encodes also some info on perpendicularity Vector vAimCross = vBaseForward.Cross(vMeToTarget); bool bTargetHeadedOutOfCone = !vCandVel.IsZero() && velCross.z * vAimCross.z >= 0; // true if same sign float flConeLeavingUrgency; if ( bTargetHeadedOutOfCone ) { flConeLeavingUrgency = fabs( velCross.z / vCandVel.Length2D() ); // just the sin; varies 0..1 where 1 means moving perpendicular to my aim } else { flConeLeavingUrgency = 0; // not at threat of leaving just yet } // the angle between my current yaw and what's needed to hit the target float flSwivelNeeded = fabs( UTIL_AngleDiff( // i wish we weren't storing euler angles UTIL_VecToYaw( vMeToTarget ), m_fDeployYaw ) ); flSwivelNeeded /= ASW_SENTRY_ANGLE; // normalize to 0..2 float fBigness = 0.0f; int nClassify = pCandidate->Classify(); switch( nClassify ) { case CLASS_ASW_SHIELDBUG: case CLASS_ASW_MORTAR_BUG: fBigness = 4.0f; break; case CLASS_ASW_HARVESTER: case CLASS_ASW_RANGER: fBigness = 2.0f; break; } candidatescores[i] = Vector( 3.0f, -1.5f, 4.0f ).Dot( Vector( flConeLeavingUrgency, flSwivelNeeded, fBigness ) ); } // find the highest scoring candidate int best = 0; for ( int i = 1 ; i < candidatescores.Count() ; ++i ) { if ( candidatescores[i] > candidatescores[best] ) best = i; } // NDebugOverlay::EntityBounds(candidates[best], 255, 255, 0, 255, 0.2f ); return candidates[best]; }
bool CAI_PlaneSolver::RunMoveSolver( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, float degreesPositiveArc, bool fDeterOscillation, Vector *pResult ) { PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_RunMoveSolver ); AI_MoveSolution_t solution; if ( m_Solver.HaveRegulations() ) { // @TODO (toml 07-19-02): add a movement threshhold here (the target may be the same, // but the ai is nowhere near where the last solution was derived) bool fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target ); // For debugging, visualize our regulations VisualizeRegulations(); AI_MoveSuggestion_t moveSuggestions[2]; int nSuggestions = 1; moveSuggestions[0].Set( AIMST_MOVE, 1, UTIL_VecToYaw( goal.dir ), degreesPositiveArc ); moveSuggestions[0].flags |= ComputeTurnBiasFlags( goal, directTrace ); if ( fDeterOscillation && !fNewTarget ) { #ifndef TESTING_SUGGESTIONS moveSuggestions[nSuggestions++].Set( AIMST_OSCILLATION_DETERRANCE, 1, m_PrevSolution - 180, 180 ); #endif } if ( m_Solver.Solve( moveSuggestions, nSuggestions, &solution ) ) { *pResult = UTIL_YawToVector( solution.dir ); if (goal.navType == NAV_FLY) { // FIXME: Does the z component have to occur during the goal // setting because it's there & only there where MoveLimit // will report contact with the world if we move up? AdjustSolutionForFliers( goal, solution.dir, pResult ); } // A crude attempt at oscillation detection: if we solved last time, and this time, and the same target is // involved, and we resulted in nearly a 180, we are probably oscillating #ifndef TESTING_SUGGESTIONS if ( !fNewTarget ) { float delta = solution.dir - m_PrevSolution; if ( delta < 0 ) delta += 360; if ( delta > 165 && delta < 195 ) return false; } #endif m_PrevSolution = solution.dir; m_PrevSolutionVector = *pResult; Vector curVelocity = m_pNpc->GetSmoothedVelocity(); if ( curVelocity != vec3_origin ) { VectorNormalize( curVelocity ); if ( !fNewTarget ) { *pResult = curVelocity * 0.1 + m_PrevSolutionVector * 0.1 + *pResult * 0.8; } else { *pResult = curVelocity * 0.2 + *pResult * 0.8; } } return true; } } else { if (goal.navType != NAV_FLY) { *pResult = goal.dir; } else { VectorSubtract( goal.target, GetLocalOrigin(), *pResult ); VectorNormalize( *pResult ); } return true; } return false; }
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; }
static int luasrc_UTIL_YawToVector (lua_State *L) { Vector v = UTIL_YawToVector(luaL_checknumber(L, 1)); lua_pushvector(L, v); return 1; }
//------------------------------------------------------------------------------ // Purpose : If connected to a node returns node direction, otherwise // returns local hint direction // // NOTE: Assumes not using multiple AI networks // Input : // Output : //------------------------------------------------------------------------------ Vector CAI_Hint::GetDirection( ) { return UTIL_YawToVector( Yaw() ); }
//----------------------------------------------------------------------------- // Purpose : Draw a horizontal arrow pointing in the specified direction by yaw value //----------------------------------------------------------------------------- void NDebugOverlay::YawArrow( const Vector &startPos, float yaw, float length, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration) { Vector forward = UTIL_YawToVector( yaw ); HorzArrow( startPos, startPos + forward * length, width, r, g, b, a, noDepthTest, flDuration ); }