void UEnvironmentQueryGraph::UpdateAsset(int32 UpdateFlags) { if (IsLocked()) { return; } // let's find root node UEnvironmentQueryGraphNode_Root* RootNode = NULL; for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) { RootNode = Cast<UEnvironmentQueryGraphNode_Root>(Nodes[Idx]); if (RootNode != NULL) { break; } } UEnvQuery* Query = Cast<UEnvQuery>(GetOuter()); Query->GetOptionsMutable().Reset(); if (RootNode && RootNode->Pins.Num() > 0 && RootNode->Pins[0]->LinkedTo.Num() > 0) { UEdGraphPin* MyPin = RootNode->Pins[0]; // sort connections so that they're organized the same as user can see in the editor MyPin->LinkedTo.Sort(FCompareNodeXLocation()); for (int32 Idx = 0; Idx < MyPin->LinkedTo.Num(); Idx++) { UEnvironmentQueryGraphNode_Option* OptionNode = Cast<UEnvironmentQueryGraphNode_Option>(MyPin->LinkedTo[Idx]->GetOwningNode()); if (OptionNode) { OptionNode->UpdateNodeData(); UEnvQueryOption* OptionInstance = Cast<UEnvQueryOption>(OptionNode->NodeInstance); if (OptionInstance && OptionInstance->Generator) { OptionInstance->Tests.Reset(); for (int32 TestIdx = 0; TestIdx < OptionNode->SubNodes.Num(); TestIdx++) { UAIGraphNode* SubNode = OptionNode->SubNodes[TestIdx]; if (SubNode == nullptr) { continue; } SubNode->ParentNode = OptionNode; UEnvironmentQueryGraphNode_Test* TestNode = Cast<UEnvironmentQueryGraphNode_Test>(SubNode); if (TestNode && TestNode->bTestEnabled) { UEnvQueryTest* TestInstance = Cast<UEnvQueryTest>(TestNode->NodeInstance); if (TestInstance) { OptionInstance->Tests.Add(TestInstance); } } } Query->GetOptionsMutable().Add(OptionInstance); } // FORT-16508 tracking BEGIN: log invalid option if (OptionInstance && OptionInstance->Generator == nullptr) { FString DebugMessage = FString::Printf(TEXT("[%s] UpdateAsset found option instance [pin:%d] without a generator! tests:%d"), FPlatformTime::StrTimestamp(), Idx, OptionNode->SubNodes.Num()); RootNode->LogDebugMessage(DebugMessage); } else if (OptionInstance == nullptr) { FString DebugMessage = FString::Printf(TEXT("[%s] UpdateAsset found option node [pin:%d] without an instance! tests:%d"), FPlatformTime::StrTimestamp(), Idx, OptionNode->SubNodes.Num()); RootNode->LogDebugMessage(DebugMessage); } // FORT-16508 tracking END } } } RemoveOrphanedNodes(); // FORT-16508 tracking BEGIN: find corrupted options if (RootNode) { for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) { UEnvironmentQueryGraphNode_Option* OptionNode = Cast<UEnvironmentQueryGraphNode_Option>(Nodes[Idx]); if (OptionNode) { UEnvQueryOption* OptionInstance = Cast<UEnvQueryOption>(OptionNode->NodeInstance); if (OptionNode->NodeInstance == nullptr || OptionInstance == nullptr || OptionInstance->HasAnyFlags(RF_Transient)) { FString DebugMessage = FString::Printf(TEXT("[%s] found corrupted node after RemoveOrphanedNodes! type:instance option:%s instance:%d transient:%d tests:%d"), FPlatformTime::StrTimestamp(), *GetNameSafe(OptionNode), OptionNode->NodeInstance ? (OptionInstance ? 1 : -1) : 0, OptionNode->NodeInstance ? (OptionNode->HasAnyFlags(RF_Transient) ? 1 : 0) : -1, OptionNode->SubNodes.Num()); RootNode->LogDebugError(DebugMessage); } if (OptionInstance && (OptionInstance->Generator == nullptr || OptionInstance->Generator->HasAnyFlags(RF_Transient))) { FString DebugMessage = FString::Printf(TEXT("[%s] found corrupted node after RemoveOrphanedNodes! type:generator option:%s instance:%d transient:%d tests:%d"), FPlatformTime::StrTimestamp(), *GetNameSafe(OptionNode), OptionNode->NodeInstance ? 1 : 0, OptionNode->NodeInstance ? (OptionNode->HasAnyFlags(RF_Transient) ? 1 : 0) : -1, OptionNode->SubNodes.Num()); RootNode->LogDebugError(DebugMessage); } } } } // FORT-16508 tracking END #if USE_EQS_DEBUGGER UEnvQueryManager::NotifyAssetUpdate(Query); #endif }
void UK2Node_Composite::PostPasteNode() { Super::PostPasteNode(); //@TODO: Should verify that each node in the composite can be pasted into this new graph successfully (CanPasteHere) if (BoundGraph != NULL) { UEdGraph* ParentGraph = CastChecked<UEdGraph>(GetOuter()); ensure(BoundGraph != ParentGraph); // Update the InputSinkNode / OutputSourceNode pointers to point to the new graph TSet<UEdGraphNode*> BoundaryNodes; for (int32 NodeIndex = 0; NodeIndex < BoundGraph->Nodes.Num(); ++NodeIndex) { UEdGraphNode* Node = BoundGraph->Nodes[NodeIndex]; //Remove this node if it should not exist more then one in blueprint if(UK2Node_Event* Event = Cast<UK2Node_Event>(Node)) { UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraphChecked(BoundGraph); if(FBlueprintEditorUtils::FindOverrideForFunction(BP, Event->EventReference.GetMemberParentClass(Event), Event->EventReference.GetMemberName())) { FBlueprintEditorUtils::RemoveNode(BP, Node, true); NodeIndex--; continue; } } BoundaryNodes.Add(Node); if (Node->GetClass() == UK2Node_Tunnel::StaticClass()) { // Exactly a tunnel node, should be the entrance or exit node UK2Node_Tunnel* Tunnel = CastChecked<UK2Node_Tunnel>(Node); if (Tunnel->bCanHaveInputs && !Tunnel->bCanHaveOutputs) { OutputSourceNode = Tunnel; Tunnel->InputSinkNode = this; } else if (Tunnel->bCanHaveOutputs && !Tunnel->bCanHaveInputs) { InputSinkNode = Tunnel; Tunnel->OutputSourceNode = this; } else { ensureMsgf(false, *LOCTEXT("UnexpectedTunnelNode", "Unexpected tunnel node '%s' in cloned graph '%s' (both I/O or neither)").ToString(), *Tunnel->GetName(), *GetName()); } } } RenameBoundGraphCloseToName(BoundGraph->GetName()); ensure(BoundGraph->SubGraphs.Find(ParentGraph) == INDEX_NONE); //Nested composites will already be in the SubGraph array if(ParentGraph->SubGraphs.Find(BoundGraph) == INDEX_NONE) { ParentGraph->SubGraphs.Add(BoundGraph); } FEdGraphUtilities::PostProcessPastedNodes(BoundaryNodes); } }
bool CAI_BlendedMotor::AddTurnGesture( float flYD ) { // some funky bug with human turn gestures, disable for now return false; // try using a turn gesture Activity activity = ACT_INVALID; float weight = 1.0; float turnCompletion = 1.0; if (m_flNextTurnGesture > gpGlobals->curtime) { /* if ( GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) { Msg( "%.1f : [ %.2f ]\n", flYD, m_flNextTurnAct - gpGlobals->curtime ); } */ return false; } if ( GetOuter()->IsMoving() || GetOuter()->IsCrouching() ) { return false; } if (fabs( flYD ) < 15) { return false; } else if (flYD < -45) { activity = ACT_GESTURE_TURN_RIGHT90; weight = flYD / -90; turnCompletion = 0.36; } else if (flYD < 0) { activity = ACT_GESTURE_TURN_RIGHT45; weight = flYD / -45; turnCompletion = 0.4; } else if (flYD <= 45) { activity = ACT_GESTURE_TURN_LEFT45; weight = flYD / 45; turnCompletion = 0.4; } else { activity = ACT_GESTURE_TURN_LEFT90; weight = flYD / 90; turnCompletion = 0.36; } int seq = SelectWeightedSequence( activity ); if (scene_flatturn->GetBool() && GetOuter()->IsCurSchedule( SCHED_SCENE_GENERIC )) { Activity flatactivity = activity; if (activity == ACT_GESTURE_TURN_RIGHT90) { flatactivity = ACT_GESTURE_TURN_RIGHT90_FLAT; } else if (activity == ACT_GESTURE_TURN_RIGHT45) { flatactivity = ACT_GESTURE_TURN_RIGHT45_FLAT; } else if (activity == ACT_GESTURE_TURN_LEFT90) { flatactivity = ACT_GESTURE_TURN_LEFT90_FLAT; } else if (activity == ACT_GESTURE_TURN_LEFT45) { flatactivity = ACT_GESTURE_TURN_LEFT45_FLAT; } if (flatactivity != activity) { int newseq = SelectWeightedSequence( flatactivity ); if (newseq != ACTIVITY_NOT_AVAILABLE) { seq = newseq; } } } if (seq != ACTIVITY_NOT_AVAILABLE) { int iLayer = GetOuter()->AddGestureSequence( seq ); if (iLayer != -1) { GetOuter()->SetLayerPriority( iLayer, 100 ); // vary the playback a bit SetLayerPlaybackRate( iLayer, 1.0 ); float actualDuration = GetOuter()->GetLayerDuration( iLayer ); float rate = enginerandom->RandomFloat( 0.5, 1.1 ); float diff = fabs( flYD ); float speed = (diff / (turnCompletion * actualDuration / rate)) * 0.1; speed = clamp( speed, 15, 35 ); speed = min( speed, diff ); actualDuration = (diff / (turnCompletion * speed)) * 0.1 ; GetOuter()->SetLayerDuration( iLayer, actualDuration ); SetLayerWeight( iLayer, weight ); SetYawSpeed( speed ); Remember( bits_MEMORY_TURNING ); // don't overlap the turn portion of the gestures, and don't play them too often m_flNextTurnGesture = gpGlobals->curtime + max( turnCompletion * actualDuration, 0.3 ); /* if ( GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) { Msg( "%.1f : %.2f %.2f : %.2f (%.2f)\n", flYD, weight, speed, actualDuration, turnCompletion * actualDuration ); } */ return true; } else { return false; } } return false; }
//----------------------------------------------------------------------------- // Purpose: Suppress melee attacks against enemies for the given duration // Input : flDuration - Amount of time to suppress the attacks //----------------------------------------------------------------------------- void CAI_PassengerBehaviorZombie::SuppressAttack( float flDuration ) { GetOuter()->SetNextAttack( gpGlobals->curtime + flDuration ); }
bool UMovieSceneAudioTrack::IsAMasterTrack() const { return Cast<UMovieScene>(GetOuter())->IsAMasterTrack(this); }
void CAI_LeadBehavior::RunTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_LEAD_SUCCEED: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_DONE ); } break; } case TASK_LEAD_ARRIVE: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_ARRIVAL_DONE ); } break; } case TASK_LEAD_MOVE_TO_RANGE: { // If we haven't spoken our start speech, move closer if ( !m_hasspokenstart) { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 ); } else { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance ); if ( !TaskIsComplete() ) { // Transition to a walk when we get near the player // Check Z first, and only check 2d if we're within that Vector vecGoalPos = GetNavigator()->GetGoalPos(); float distance = fabs(vecGoalPos.z - GetLocalOrigin().z); bool bWithinZ = false; if ( distance < m_retrievedistance ) { distance = ( vecGoalPos - GetLocalOrigin() ).Length2D(); bWithinZ = true; } if ( distance > m_retrievedistance ) { Activity followActivity = ACT_WALK; if ( GetOuter()->GetState() == NPC_STATE_COMBAT || (!bWithinZ || distance < (m_retrievedistance*4)) && GetOuter()->GetState() != NPC_STATE_COMBAT ) { followActivity = ACT_RUN; } // Don't confuse move and shoot by resetting the activity every think Activity curActivity = GetNavigator()->GetMovementActivity(); switch( curActivity ) { case ACT_WALK_AIM: curActivity = ACT_WALK; break; case ACT_RUN_AIM: curActivity = ACT_RUN; break; } if ( curActivity != followActivity ) { GetNavigator()->SetMovementActivity(followActivity); } GetNavigator()->SetArrivalDirection( GetOuter()->GetTarget() ); } } } break; } case TASK_LEAD_RETRIEVE_WAIT: { ChainRunTask( TASK_WAIT_INDEFINITE ); break; } case TASK_LEAD_WALK_PATH: { // If we're leading, and we're supposed to run, run instead of walking if ( m_run && ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) ) { ChainRunTask( TASK_RUN_PATH ); } else { ChainRunTask( TASK_WALK_PATH ); } // While we're walking if ( TaskIsRunning() && IsCurSchedule( SCHED_LEAD_PLAYER, false ) ) { // If we're not speaking, and we haven't tried for a while, try to speak lead idle if ( m_flNextLeadIdle < gpGlobals->curtime && !IsSpeaking() ) { m_flNextLeadIdle = gpGlobals->curtime + RandomFloat( 10,15 ); if ( !m_args.iRetrievePlayer && HasCondition( COND_LEAD_FOLLOWER_LOST ) && HasCondition(COND_SEE_PLAYER) ) { Speak( TLK_LEAD_COMINGBACK ); } else { Speak( TLK_LEAD_IDLE ); } } } break; } default: BaseClass::RunTask( pTask); } }
void CAI_MoveAndShootOverlay::RunShootWhileMove() { if ( m_bNoShootWhileMove ) return; if ( gpGlobals->curtime < m_flSuspendUntilTime ) return; m_flSuspendUntilTime = MOVESHOOT_DO_NOT_SUSPEND; CAI_BaseNPC *pOuter = GetOuter(); // keep enemy if dead but try to look for a new one if (!pOuter->GetEnemy() || !pOuter->GetEnemy()->IsAlive()) { CBaseEntity *pNewEnemy = pOuter->BestEnemy(); if( pNewEnemy != NULL ) { //New enemy! Clear the timers and set conditions. pOuter->SetEnemy( pNewEnemy ); pOuter->SetState( NPC_STATE_COMBAT ); } else { pOuter->ClearAttackConditions(); } // SetEnemy( NULL ); } /*if( !pOuter->GetNavigator()->IsGoalActive() ) return;*/ if ( GetEnemy() == NULL ) { if ( pOuter->GetAlternateMoveShootTarget() ) { // Aim at this other thing if I can't aim at my enemy. pOuter->AddFacingTarget( pOuter->GetAlternateMoveShootTarget(), pOuter->GetAlternateMoveShootTarget()->GetAbsOrigin(), 1.0, 0.2 ); } return; } bool bMoveAimAtEnemy = CanAimAtEnemy(); UpdateMoveShootActivity( bMoveAimAtEnemy ); if ( !bMoveAimAtEnemy ) { EndShootWhileMove(); return; } Assert( HasAvailableRangeAttack() ); // This should have been caught at task start Activity activity; bool bIsReloading = false; if ( ( activity = pOuter->TranslateActivity( ACT_GESTURE_RELOAD ) ) != ACT_INVALID ) { bIsReloading = pOuter->IsPlayingGesture( activity ); } if ( !bIsReloading && HasAvailableRangeAttack() ) { // time to fire? if ( pOuter->HasCondition( COND_CAN_RANGE_ATTACK1, false ) ) { if ( pOuter->GetShotRegulator()->IsInRestInterval() ) { EndShootWhileMove(); } else if ( pOuter->GetShotRegulator()->ShouldShoot() ) { if ( m_bMovingAndShooting || pOuter->OnBeginMoveAndShoot() ) { m_bMovingAndShooting = true; pOuter->OnRangeAttack1(); activity = pOuter->TranslateActivity( ACT_GESTURE_RANGE_ATTACK1 ); Assert( activity != ACT_INVALID ); pOuter->RestartGesture( activity ); // FIXME: this seems a bit wacked pOuter->Weapon_SetActivity( pOuter->Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 ); } } } } // try to keep facing towards the last known position of the enemy Vector vecEnemyLKP = pOuter->GetEnemyLKP(); pOuter->AddFacingTarget( pOuter->GetEnemy(), vecEnemyLKP, 1.0, 0.8 ); }
bool UObjectBaseUtility::IsDefaultSubobject() const { const bool bIsInstanced = GetOuter() && (GetOuter()->HasAnyFlags(RF_ClassDefaultObject) || ((UObject*)this)->GetArchetype() != GetClass()->GetDefaultObject()); return bIsInstanced; }
/** * Walks up the chain of packages until it reaches the top level, which it ignores. * * @param bStartWithOuter whether to include this object's name in the returned string * @return string containing the path name for this object, minus the outermost-package's name */ FString UObjectBaseUtility::GetFullGroupName( bool bStartWithOuter ) const { const UObjectBaseUtility* Obj = bStartWithOuter ? GetOuter() : this; return Obj ? Obj->GetPathName(GetOutermost()) : TEXT(""); }
bool CAI_TacticalServices::FindLateralLos( const Vector &vecThreat, Vector *pResult ) { AI_PROFILE_SCOPE( CAI_TacticalServices_FindLateralLos ); if( !m_bAllowFindLateralLos ) { return false; } MARK_TASK_EXPENSIVE(); Vector vecLeftTest; Vector vecRightTest; Vector vecStepRight; Vector vecCheckStart; bool bLookingForEnemy = GetEnemy() && VectorsAreEqual(vecThreat, GetEnemy()->EyePosition(), 0.1f); int i; if( !bLookingForEnemy || GetOuter()->HasCondition(COND_SEE_ENEMY) || GetOuter()->HasCondition(COND_HAVE_ENEMY_LOS) || GetOuter()->GetTimeScheduleStarted() == gpGlobals->curtime ) // Conditions get nuked before tasks run, assume should try { // My current position might already be valid. if ( TestLateralLos(vecThreat, GetLocalOrigin()) ) { *pResult = GetLocalOrigin(); return true; } } if( !ai_find_lateral_los.GetBool() ) { // Allows us to turn off lateral LOS at the console. Allow the above code to run // just in case the NPC has line of sight to begin with. return false; } int iChecks = COVER_CHECKS; int iDelta = COVER_DELTA; // If we're limited in how far we're allowed to move laterally, don't bother checking past it int iMaxLateralDelta = (int)GetOuter()->GetMaxTacticalLateralMovement(); if ( iMaxLateralDelta != MAXTACLAT_IGNORE && iMaxLateralDelta < iDelta ) { iChecks = 1; iDelta = iMaxLateralDelta; } Vector right; AngleVectors( GetLocalAngles(), NULL, &right, NULL ); vecStepRight = right * iDelta; vecStepRight.z = 0; vecLeftTest = vecRightTest = GetLocalOrigin(); vecCheckStart = vecThreat; for ( i = 0 ; i < iChecks; i++ ) { vecLeftTest = vecLeftTest - vecStepRight; vecRightTest = vecRightTest + vecStepRight; if (TestLateralLos( vecCheckStart, vecLeftTest )) { *pResult = vecLeftTest; return true; } if (TestLateralLos( vecCheckStart, vecRightTest )) { *pResult = vecRightTest; return true; } } return false; }
bool UActorComponent::NeedsLoadForServer() const { check(GetOuter()); return (GetOuter()->NeedsLoadForServer() && Super::NeedsLoadForServer()); }
int CAI_TacticalServices::FindLosNode(const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinThreatDist, float flMaxThreatDist, float flBlockTime, FlankType_t eFlankType, const Vector &vecFlankRefPos, float flFlankParam ) { if ( !CAI_NetworkManager::NetworksLoaded() ) return NO_NODE; AI_PROFILE_SCOPE( CAI_TacticalServices_FindLosNode ); MARK_TASK_EXPENSIVE(); int iMyNode = GetPathfinder()->NearestNodeToNPC(); if ( iMyNode == NO_NODE ) { Vector pos = GetOuter()->GetAbsOrigin(); DevWarning( 2, "FindCover() - %s has no nearest node! (Check near %f %f %f)\n", GetEntClassname(), pos.x, pos.y, pos.z); return NO_NODE; } // ------------------------------------------------------------------------------------ // We're going to search for a shoot node by expanding to our current node's neighbors // and then their neighbors, until a shooting position is found, or all nodes are beyond MaxDist // ------------------------------------------------------------------------------------ AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * GetNetwork()->NumNodes() ); CNodeList list( pBuffer, GetNetwork()->NumNodes() ); CVarBitVec wasVisited(GetNetwork()->NumNodes()); // Nodes visited // mark start as visited wasVisited.Set( iMyNode ); list.Insert( AI_NearNode_t(iMyNode, 0) ); static int nSearchRandomizer = 0; // tries to ensure the links are searched in a different order each time; while ( list.Count() ) { int nodeIndex = list.ElementAtHead().nodeIndex; // remove this item from the list list.RemoveAtHead(); const Vector &nodeOrigin = GetNetwork()->GetNode(nodeIndex)->GetPosition(GetHullType()); // HACKHACK: Can't we rework this loop and get rid of this? // skip the starting node, or we probably wouldn't have called this function. if ( nodeIndex != iMyNode ) { bool skip = false; // See if the node satisfies the flanking criteria. switch ( eFlankType ) { case FLANKTYPE_NONE: break; case FLANKTYPE_RADIUS: { Vector vecDist = nodeOrigin - vecFlankRefPos; if ( vecDist.Length() < flFlankParam ) { skip = true; } break; } case FLANKTYPE_ARC: { Vector vecEnemyToRef = vecFlankRefPos - vThreatPos; VectorNormalize( vecEnemyToRef ); Vector vecEnemyToNode = nodeOrigin - vThreatPos; VectorNormalize( vecEnemyToNode ); float flDot = DotProduct( vecEnemyToRef, vecEnemyToNode ); if ( RAD2DEG( acos( flDot ) ) < flFlankParam ) { skip = true; } break; } } // Don't accept climb nodes, and assume my nearest node isn't valid because // we decided to make this check in the first place. Keep moving if ( !skip && !GetNetwork()->GetNode(nodeIndex)->IsLocked() && GetNetwork()->GetNode(nodeIndex)->GetType() != NODE_CLIMB ) { // Now check its distance and only accept if in range float flThreatDist = ( nodeOrigin - vThreatPos ).Length(); if ( flThreatDist < flMaxThreatDist && flThreatDist > flMinThreatDist ) { CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex); if ( GetOuter()->IsValidShootPosition( nodeOrigin, pNode, pNode->GetHint() ) ) { if (GetOuter()->TestShootPosition(nodeOrigin,vThreatEyePos)) { // Note when this node was used, so we don't try // to use it again right away. GetNetwork()->GetNode(nodeIndex)->Lock( flBlockTime ); #if 0 if ( GetOuter()->GetHintNode() ) { GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType())); GetOuter()->SetHintNode( NULL ); } // This used to not be set, why? (kenb) // @Note (toml 05-19-04): I think because stomping the hint can lead to // unintended side effects. The hint node is primarily a high level // tool, and certain NPCs break if it gets slammed here. If we need // this, we should propagate it out and let the schedule selector // or task decide to set the hint node GetOuter()->SetHintNode( GetNetwork()->GetNode(nodeIndex)->GetHint() ); #endif if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:los!", nodeIndex), false, 1 ); } // The next NPC who searches should use a slight different pattern nSearchRandomizer = nodeIndex; return nodeIndex; } else { if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!shoot", nodeIndex), false, 1 ); } } } else { if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!valid", nodeIndex), false, 1 ); } } } else { if ( ShouldDebugLos( nodeIndex ) ) { CFmtStr msg( "%d:%s", nodeIndex, ( flThreatDist < flMaxThreatDist ) ? "too close" : "too far" ); NDebugOverlay::Text( nodeOrigin, msg, false, 1 ); } } } } // Go through each link and add connected nodes to the list for (int link=0; link < GetNetwork()->GetNode(nodeIndex)->NumLinks();link++) { int index = (link + nSearchRandomizer) % GetNetwork()->GetNode(nodeIndex)->NumLinks(); CAI_Link *nodeLink = GetNetwork()->GetNode(nodeIndex)->GetLinkByIndex(index); if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) ) continue; int newID = nodeLink->DestNodeID(nodeIndex); // If not already visited, add to the list if (!wasVisited.IsBitSet(newID)) { float dist = (GetLocalOrigin() - GetNetwork()->GetNode(newID)->GetPosition(GetHullType())).LengthSqr(); list.Insert( AI_NearNode_t(newID, dist) ); wasVisited.Set( newID ); } } } // We failed. No range attack node node was found return NO_NODE; }
int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinDist, float flMaxDist ) { if ( !CAI_NetworkManager::NetworksLoaded() ) return NO_NODE; AI_PROFILE_SCOPE( CAI_TacticalServices_FindCoverNode ); MARK_TASK_EXPENSIVE(); DebugFindCover( g_AIDebugFindCoverNode, GetOuter()->EyePosition(), vThreatEyePos, 0, 255, 255 ); int iMyNode = GetPathfinder()->NearestNodeToPoint( vNearPos ); if ( iMyNode == NO_NODE ) { Vector pos = GetOuter()->GetAbsOrigin(); DevWarning( 2, "FindCover() - %s has no nearest node! (Check near %f %f %f)\n", GetEntClassname(), pos.x, pos.y, pos.z); return NO_NODE; } if ( !flMaxDist ) { // user didn't supply a MaxDist, so work up a crazy one. flMaxDist = 784; } if ( flMinDist > 0.5 * flMaxDist) { flMinDist = 0.5 * flMaxDist; } // ------------------------------------------------------------------------------------ // We're going to search for a cover node by expanding to our current node's neighbors // and then their neighbors, until cover is found, or all nodes are beyond MaxDist // ------------------------------------------------------------------------------------ AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * GetNetwork()->NumNodes() ); CNodeList list( pBuffer, GetNetwork()->NumNodes() ); CVarBitVec wasVisited(GetNetwork()->NumNodes()); // Nodes visited // mark start as visited list.Insert( AI_NearNode_t(iMyNode, 0) ); wasVisited.Set( iMyNode ); float flMinDistSqr = flMinDist*flMinDist; float flMaxDistSqr = flMaxDist*flMaxDist; static int nSearchRandomizer = 0; // tries to ensure the links are searched in a different order each time; // Search until the list is empty while( list.Count() ) { // Get the node that is closest in the number of steps and remove from the list int nodeIndex = list.ElementAtHead().nodeIndex; list.RemoveAtHead(); CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex); Vector nodeOrigin = pNode->GetPosition(GetHullType()); float dist = (vNearPos - nodeOrigin).LengthSqr(); if (dist >= flMinDistSqr && dist < flMaxDistSqr) { Activity nCoverActivity = GetOuter()->GetCoverActivity( pNode->GetHint() ); Vector vEyePos = nodeOrigin + GetOuter()->EyeOffset(nCoverActivity); if ( GetOuter()->IsValidCover( nodeOrigin, pNode->GetHint() ) ) { // Check if this location will block the threat's line of sight to me if (GetOuter()->IsCoverPosition(vThreatEyePos, vEyePos)) { // -------------------------------------------------------- // Don't let anyone else use this node for a while // -------------------------------------------------------- pNode->Lock( 1.0 ); if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) ) { if ( GetOuter()->GetHintNode() ) { GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType())); GetOuter()->SetHintNode( NULL ); } GetOuter()->SetHintNode( pNode->GetHint() ); } // The next NPC who searches should use a slight different pattern nSearchRandomizer = nodeIndex; DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 255, 0 ); return nodeIndex; } else { DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 255, 0, 0 ); } } else { DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 0, 255 ); } } // Add its children to the search list // Go through each link // UNDONE: Pass in a cost function to measure each link? for ( int link = 0; link < GetNetwork()->GetNode(nodeIndex)->NumLinks(); link++ ) { int index = (link + nSearchRandomizer) % GetNetwork()->GetNode(nodeIndex)->NumLinks(); CAI_Link *nodeLink = GetNetwork()->GetNode(nodeIndex)->GetLinkByIndex(index); if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) ) continue; int newID = nodeLink->DestNodeID(nodeIndex); // If not already on the closed list, add to it and set its distance if (!wasVisited.IsBitSet(newID)) { // Don't accept climb nodes or nodes that aren't ready to use yet if ( GetNetwork()->GetNode(newID)->GetType() != NODE_CLIMB && !GetNetwork()->GetNode(newID)->IsLocked() ) { // UNDONE: Shouldn't we really accumulate the distance by path rather than // absolute distance. After all, we are performing essentially an A* here. nodeOrigin = GetNetwork()->GetNode(newID)->GetPosition(GetHullType()); dist = (vNearPos - nodeOrigin).LengthSqr(); // use distance to threat as a heuristic to keep AIs from running toward // the threat in order to take cover from it. float threatDist = (vThreatPos - nodeOrigin).LengthSqr(); // Now check this node is not too close towards the threat if ( dist < threatDist * 1.5 ) { list.Insert( AI_NearNode_t(newID, dist) ); } } // mark visited wasVisited.Set(newID); } } } // We failed. Not cover node was found // Clear hint node used to set ducking GetOuter()->ClearHintNode(); return NO_NODE; }
void UEnvironmentQueryGraph::SpawnMissingNodes() { UEnvQuery* QueryOwner = Cast<UEnvQuery>(GetOuter()); if (QueryOwner == nullptr) { return; } TSet<UEnvQueryTest*> ExistingTests; TSet<UEnvQueryOption*> ExistingNodes; TArray<UEnvQueryOption*> OptionsCopy = QueryOwner->GetOptions(); UAIGraphNode* MyRootNode = nullptr; for (int32 Idx = 0; Idx < Nodes.Num(); Idx++) { UEnvironmentQueryGraphNode* MyNode = Cast<UEnvironmentQueryGraphNode>(Nodes[Idx]); UEnvQueryOption* OptionInstance = MyNode ? Cast<UEnvQueryOption>(MyNode->NodeInstance) : nullptr; if (OptionInstance && OptionInstance->Generator) { ExistingNodes.Add(OptionInstance); ExistingTests.Empty(ExistingTests.Num()); for (int32 SubIdx = 0; SubIdx < MyNode->SubNodes.Num(); SubIdx++) { UEnvironmentQueryGraphNode* MySubNode = Cast<UEnvironmentQueryGraphNode>(MyNode->SubNodes[SubIdx]); UEnvQueryTest* TestInstance = MySubNode ? Cast<UEnvQueryTest>(MySubNode->NodeInstance) : nullptr; if (TestInstance) { ExistingTests.Add(TestInstance); } else { MyNode->RemoveSubNode(MySubNode); SubIdx--; } } SpawnMissingSubNodes(OptionInstance, ExistingTests, MyNode); } UEnvironmentQueryGraphNode_Root* RootNode = Cast<UEnvironmentQueryGraphNode_Root>(Nodes[Idx]); if (RootNode) { MyRootNode = RootNode; } } UEdGraphPin* RootOutPin = MyRootNode ? FindGraphNodePin(MyRootNode, EGPD_Output) : nullptr; ExistingTests.Empty(0); for (int32 Idx = 0; Idx < OptionsCopy.Num(); Idx++) { UEnvQueryOption* OptionInstance = OptionsCopy[Idx]; if (ExistingNodes.Contains(OptionInstance) || OptionInstance == nullptr || OptionInstance->Generator == nullptr) { continue; } FGraphNodeCreator<UEnvironmentQueryGraphNode_Option> NodeBuilder(*this); UEnvironmentQueryGraphNode_Option* MyNode = NodeBuilder.CreateNode(); UAIGraphNode::UpdateNodeClassDataFrom(OptionInstance->Generator->GetClass(), MyNode->ClassData); MyNode->ErrorMessage = MyNode->ClassData.GetDeprecatedMessage(); NodeBuilder.Finalize(); if (MyRootNode) { MyNode->NodePosX = MyRootNode->NodePosX + (Idx * 300); MyNode->NodePosY = MyRootNode->NodePosY + 100; } MyNode->NodeInstance = OptionInstance; SpawnMissingSubNodes(OptionInstance, ExistingTests, MyNode); UEdGraphPin* SpawnedInPin = FindGraphNodePin(MyNode, EGPD_Input); if (RootOutPin && SpawnedInPin) { RootOutPin->MakeLinkTo(SpawnedInPin); } } }
int CAI_LeadBehavior::SelectSchedule() { if ( HasGoal() ) { if( HasCondition(COND_LEAD_SUCCESS) ) { return SCHED_LEAD_SUCCEED; } // Player's here, but does he have the weapon we want him to have? if ( m_weaponname != NULL_STRING ) { CBasePlayer *pFollower = AI_GetSinglePlayer(); if ( pFollower && !pFollower->Weapon_OwnsThisType( STRING(m_weaponname) ) ) { // If the safety timeout has run out, just give the player the weapon if ( !m_flWeaponSafetyTimeOut || (m_flWeaponSafetyTimeOut > gpGlobals->curtime) ) return SCHED_LEAD_PLAYERNEEDSWEAPON; string_t iszItem = AllocPooledString( "weapon_bugbait" ); pFollower->GiveNamedItem( STRING(iszItem) ); } } // If we have a waitpoint, we want to wait at it for the player. if( HasWaitPoint() && !PlayerIsAheadOfMe( true ) ) { bool bKeepWaiting = true; // If we have no wait distance, trigger as soon as the player comes in view if ( !m_waitdistance ) { if ( HasCondition( COND_SEE_PLAYER ) ) { // We've spotted the player, so stop waiting bKeepWaiting = false; } } else { // We have to collect data about the person we're leading around. CBaseEntity *pFollower = AI_GetSinglePlayer(); if( pFollower ) { float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length(); if ( flFollowerDist < m_waitdistance ) { bKeepWaiting = false; } } } // Player still not here? if ( bKeepWaiting ) return SCHED_LEAD_WAITFORPLAYER; // We're finished waiting m_waitpoint = vec3_origin; Speak( TLK_LEAD_WAITOVER ); // Don't speak the start line, because we've said m_hasspokenstart = true; return SCHED_WAIT_FOR_SPEAK_FINISH; } // If we haven't spoken our start speech, do that first if ( !m_hasspokenstart ) { if ( HasCondition(COND_LEAD_HAVE_FOLLOWER_LOS) && HasCondition(COND_LEAD_FOLLOWER_VERY_CLOSE) ) return SCHED_LEAD_SPEAK_START; // We haven't spoken to him, and we still need to. Go get him. return SCHED_LEAD_RETRIEVE; } if( HasCondition( COND_LEAD_FOLLOWER_LOST ) ) { if( m_args.iRetrievePlayer ) { // If not, we want to go get the player. DevMsg( GetOuter(), "Follower lost. Spoke COMING_BACK.\n"); Speak( TLK_LEAD_COMINGBACK ); m_MoveMonitor.ClearMark(); // If we spoke something, wait for it to finish if ( m_args.iComingBackWaitForSpeak && IsSpeaking() ) return SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER; return SCHED_LEAD_RETRIEVE; } else { // Just stay right here and wait. return SCHED_LEAD_WAITFORPLAYERIDLE; } } if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) ) { DevMsg( GetOuter(), "Follower lagging. Spoke CATCHUP.\n"); Speak( TLK_LEAD_CATCHUP ); return SCHED_LEAD_PAUSE; } else { // If we're at the goal, wait for the player to get here if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() < (64*64) ) return SCHED_LEAD_AWAIT_SUCCESS; // If we were retrieving the player, speak the resume if ( IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) || IsCurSchedule( SCHED_LEAD_WAITFORPLAYERIDLE, false ) ) { Speak( TLK_LEAD_RETRIEVE ); // If we spoke something, wait for it to finish, if the mapmakers wants us to if ( m_args.iRetrieveWaitForSpeak && IsSpeaking() ) return SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER; } DevMsg( GetOuter(), "Leading Follower.\n"); return SCHED_LEAD_PLAYER; } } return BaseClass::SelectSchedule(); }
UWALandscapeGraph* UWALandscapeNode::GetGraph() { return Cast<UWALandscapeGraph>(GetOuter()); }
void CAI_LeadBehavior::StartTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_LEAD_FACE_GOAL: { if ( m_goalyaw != -1 ) { GetMotor()->SetIdealYaw( m_goalyaw ); } TaskComplete(); break; } case TASK_LEAD_SUCCEED: { Speak( TLK_LEAD_SUCCESS ); NotifyEvent( LBE_SUCCESS ); break; } case TASK_LEAD_ARRIVE: { // Only speak the first time we arrive if ( !m_hasspokenarrival ) { Speak( TLK_LEAD_ARRIVAL ); NotifyEvent( LBE_ARRIVAL ); m_hasspokenarrival = true; } else { TaskComplete(); } break; } case TASK_STOP_LEADING: { ClearGoal(); TaskComplete(); break; } case TASK_GET_PATH_TO_LEAD_GOAL: { if ( GetNavigator()->SetGoal( m_goal ) ) { TaskComplete(); } else { TaskFail("NO PATH"); } break; } case TASK_LEAD_GET_PATH_TO_WAITPOINT: { if ( GetNavigator()->SetGoal( m_waitpoint ) ) { TaskComplete(); } else { TaskFail("NO PATH"); } break; } case TASK_LEAD_WALK_PATH: { // If we're leading, and we're supposed to run, run instead of walking if ( m_run && ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) ) { ChainStartTask( TASK_RUN_PATH ); } else { ChainStartTask( TASK_WALK_PATH ); } break; } case TASK_LEAD_WAVE_TO_PLAYER: { // Wave to the player if we can see him. Otherwise, just idle. if ( HasCondition( COND_SEE_PLAYER ) ) { Speak( TLK_LEAD_ATTRACTPLAYER ); if ( HaveSequenceForActivity(ACT_SIGNAL1) ) { SetActivity(ACT_SIGNAL1); } } else { SetActivity(ACT_IDLE); } TaskComplete(); break; } case TASK_LEAD_PLAYER_NEEDS_WEAPON: { float flAvailableTime = GetOuter()->GetExpresser()->GetSemaphoreAvailableTime( GetOuter() ); // if someone else is talking, don't speak if ( flAvailableTime <= gpGlobals->curtime ) { Speak( TLK_LEAD_MISSINGWEAPON ); } SetActivity(ACT_IDLE); TaskComplete(); break; } case TASK_LEAD_SPEAK_START: { m_hasspokenstart = true; Speak( TLK_LEAD_START ); SetActivity(ACT_IDLE); TaskComplete(); break; } case TASK_LEAD_MOVE_TO_RANGE: { // If we haven't spoken our start speech, move closer if ( !m_hasspokenstart) { ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 ); } else { ChainStartTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance ); } break; } case TASK_LEAD_RETRIEVE_WAIT: { m_MoveMonitor.SetMark( AI_GetSinglePlayer(), 24 ); ChainStartTask( TASK_WAIT_INDEFINITE ); break; } case TASK_STOP_MOVING: { BaseClass::StartTask( pTask); if ( IsCurSchedule( SCHED_LEAD_PAUSE, false ) && pTask->flTaskData == 1 ) { GetNavigator()->SetArrivalDirection( GetTarget() ); } break; } case TASK_WAIT_FOR_SPEAK_FINISH: { BaseClass::StartTask( pTask); if( GetOuter()->GetState() == NPC_STATE_COMBAT ) { // Don't stand around jabbering in combat. TaskComplete(); } // If we're not supposed to wait for the player, don't wait for speech to finish. // Instead, just wait a wee tad, and then start moving. NPC will speak on the go. if ( TaskIsRunning() && !m_args.iRetrievePlayer ) { if ( gpGlobals->curtime - GetOuter()->GetTimeTaskStarted() > 0.3 ) { TaskComplete(); } } break; } default: BaseClass::StartTask( pTask); } }
UAbilitySystemComponent* UAttributeSet::GetOwningAbilitySystemComponent() const { return UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(CastChecked<AActor>(GetOuter())); }
void AActor::SetActorLabelInternal( const FString& NewActorLabelDirty, bool bMakeGloballyUniqueFName, bool bMarkDirty ) { // Clean up the incoming string a bit FString NewActorLabel = NewActorLabelDirty; NewActorLabel.Trim(); NewActorLabel.TrimTrailing(); // First, update the actor label { // Has anything changed? if( FCString::Strcmp( *NewActorLabel, *GetActorLabel() ) != 0 ) { // Store new label Modify( bMarkDirty ); ActorLabel = NewActorLabel; } } // Next, update the actor's name { // Generate an object name for the actor's label const FName OldActorName = GetFName(); FName NewActorName = MakeObjectNameFromActorLabel( GetActorLabel(), OldActorName ); // Has anything changed? if( OldActorName != NewActorName ) { // Try to rename the object UObject* NewOuter = NULL; // Outer won't be changing ERenameFlags RenFlags = bMakeGloballyUniqueFName ? (REN_DontCreateRedirectors | REN_ForceGlobalUnique) : REN_DontCreateRedirectors; bool bCanRename = Rename( *NewActorName.ToString(), NewOuter, REN_Test | REN_DoNotDirty | REN_NonTransactional | RenFlags ); if( bCanRename ) { // NOTE: Will assert internally if rename fails const bool bWasRenamed = Rename( *NewActorName.ToString(), NewOuter, RenFlags ); } else { // Unable to rename the object. Use a unique object name variant. NewActorName = MakeUniqueObjectName( bMakeGloballyUniqueFName ? ANY_PACKAGE : GetOuter(), GetClass(), NewActorName ); bCanRename = Rename( *NewActorName.ToString(), NewOuter, REN_Test | REN_DoNotDirty | REN_NonTransactional | RenFlags ); if( bCanRename ) { // NOTE: Will assert internally if rename fails const bool bWasRenamed = Rename( *NewActorName.ToString(), NewOuter, RenFlags ); } else { // Unable to rename the object. Oh well, not a big deal. } } } } FPropertyChangedEvent PropertyEvent( FindField<UProperty>( AActor::StaticClass(), "ActorLabel" ) ); PostEditChangeProperty(PropertyEvent); FCoreDelegates::OnActorLabelChanged.Broadcast(this); }
//----------------------------------------------------------------------------- // Purpose: Searches for a possible response // Input : concept - // NULL - // Output : AI_Response //----------------------------------------------------------------------------- AI_Response *CAI_Expresser::SpeakFindResponse( AIConcept_t concept, const char *modifiers /*= NULL*/ ) { IResponseSystem *rs = GetOuter()->GetResponseSystem(); if ( !rs ) { Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" ); return NULL; } AI_CriteriaSet set; // Always include the concept name set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT ); // Always include any optional modifiers if ( modifiers != NULL ) { char copy_modifiers[ 255 ]; const char *pCopy; char key[ 128 ] = { 0 }; char value[ 128 ] = { 0 }; Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) ); pCopy = copy_modifiers; while( pCopy ) { pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL ); if( *key && *value ) { set.AppendCriteria( key, value, CONCEPT_WEIGHT ); } } } // Let our outer fill in most match criteria GetOuter()->ModifyOrAppendCriteria( set ); // Append local player criteria to set, but not if this is a player doing the talking if ( !GetOuter()->IsPlayer() ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); if( pPlayer ) pPlayer->ModifyOrAppendPlayerCriteria( set ); } // Now that we have a criteria set, ask for a suitable response AI_Response *result = new AI_Response; Assert( result && "new AI_Response: Returned a NULL AI_Response!" ); bool found = rs->FindBestResponse( set, *result, this ); if ( rr_debugresponses.GetInt() == 3 ) { if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() ) { const char *pszName; if ( GetOuter()->IsPlayer() ) { pszName = ((CBasePlayer*)GetOuter())->GetPlayerName(); } else { pszName = GetOuter()->GetDebugName(); } if ( found ) { char response[ 256 ]; result->GetResponse( response, sizeof( response ) ); Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, concept, response ); } else { Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, concept ); } } } if ( !found ) { //Assert( !"rs->FindBestResponse: Returned a NULL AI_Response!" ); delete result; return NULL; } char response[ 256 ]; result->GetResponse( response, sizeof( response ) ); if ( !response[0] ) { delete result; return NULL; } if ( result->GetOdds() < 100 && random->RandomInt( 1, 100 ) <= result->GetOdds() ) { delete result; return NULL; } return result; }
bool CAI_MoveAndShootOverlay::HasAvailableRangeAttack() { return ( ( GetOuter()->GetActiveWeapon() != NULL ) || ( GetOuter()->CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1 ) || ( GetOuter()->CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK2 ) ); }
//----------------------------------------------------------------------------- // Purpose: Dispatches the result // Input : *response - //----------------------------------------------------------------------------- bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *result, IRecipientFilter *filter /* = NULL */ ) { char response[ 256 ]; result->GetResponse( response, sizeof( response ) ); float delay = result->GetDelay(); bool spoke = false; soundlevel_t soundlevel = result->GetSoundLevel(); if ( IsSpeaking() && concept[0] != 0 ) { DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) already speaking, forcing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); // Tracker 15911: Can break the game if we stop an imported map placed lcs here, so only // cancel actor out of instanced scripted scenes. ywb RemoveActorFromScriptedScenes( GetOuter(), true /*instanced scenes only*/ ); GetOuter()->SentenceStop(); if ( IsRunningScriptedScene( GetOuter() ) ) { DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) refusing to speak due to scene entity, tossing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept ); delete result; return false; } } switch ( result->GetType() ) { default: case RESPONSE_NONE: break; case RESPONSE_SPEAK: { if ( !result->ShouldntUseScene() ) { // This generates a fake CChoreoScene wrapping the sound.txt name spoke = SpeakAutoGeneratedScene( response, delay ); } else { float speakTime = GetResponseDuration( result ); GetOuter()->EmitSound( response ); DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) playing sound '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), response ); NoteSpeaking( speakTime, delay ); spoke = true; } } break; case RESPONSE_SENTENCE: { spoke = ( -1 != SpeakRawSentence( response, delay, VOL_NORM, soundlevel ) ) ? true : false; } break; case RESPONSE_SCENE: { spoke = SpeakRawScene( response, delay, result, filter ); } break; case RESPONSE_RESPONSE: { // This should have been recursively resolved already Assert( 0 ); } break; case RESPONSE_PRINT: { if ( g_pDeveloper->GetInt() > 0 ) { Vector vPrintPos; GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos ); NDebugOverlay::Text( vPrintPos, response, true, 1.5 ); spoke = true; } } break; } if ( spoke ) { m_flLastTimeAcceptedSpeak = gpGlobals->curtime; if ( DebuggingSpeech() && g_pDeveloper->GetInt() > 0 && response && result->GetType() != RESPONSE_PRINT ) { Vector vPrintPos; GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos ); NDebugOverlay::Text( vPrintPos, CFmtStr( "%s: %s", concept, response ), true, 1.5 ); } if ( result->IsApplyContextToWorld() ) { CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) ); if ( pEntity ) { pEntity->AddContext( result->GetContext() ); } } else { GetOuter()->AddContext( result->GetContext() ); } SetSpokeConcept( concept, result ); } else { delete result; } return spoke; }
//----------------------------------------------------------------------------- // Computes the surrounding collision bounds based on whatever algorithm we want... //----------------------------------------------------------------------------- void CCollisionProperty::ComputeSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) { if (( GetSolid() == SOLID_CUSTOM ) && (m_nSurroundType != USE_GAME_CODE )) { // NOTE: This can only happen in transition periods, say during network // reception on the client. We expect USE_GAME_CODE to be used with SOLID_CUSTOM *pVecWorldMins = GetCollisionOrigin(); *pVecWorldMaxs = *pVecWorldMins; return; } switch( m_nSurroundType ) { case USE_OBB_COLLISION_BOUNDS: { Assert( GetSolid() != SOLID_CUSTOM ); bool bUseVPhysics = false; if ( ( GetSolid() == SOLID_VPHYSICS ) && ( GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS ) ) { // UNDONE: This may not be necessary any more. IPhysicsObject *pPhysics = GetOuter()->VPhysicsGetObject(); bUseVPhysics = pPhysics && pPhysics->IsAsleep(); } ComputeCollisionSurroundingBox( bUseVPhysics, pVecWorldMins, pVecWorldMaxs ); } break; case USE_BEST_COLLISION_BOUNDS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), pVecWorldMins, pVecWorldMaxs ); break; case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( false, pVecWorldMins, pVecWorldMaxs ); break; case USE_HITBOXES: ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); break; case USE_ROTATION_EXPANDED_BOUNDS: ComputeRotationExpandedBounds( pVecWorldMins, pVecWorldMaxs ); break; case USE_SPECIFIED_BOUNDS: VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMins, *pVecWorldMins ); VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMaxs, *pVecWorldMaxs ); break; case USE_GAME_CODE: GetOuter()->ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs ); Assert( pVecWorldMins->x <= pVecWorldMaxs->x ); Assert( pVecWorldMins->y <= pVecWorldMaxs->y ); Assert( pVecWorldMins->z <= pVecWorldMaxs->z ); return; } #ifdef DEBUG /* // For debugging purposes, make sure the bounds actually does surround the thing. // Otherwise the optimization we were using isn't really all that great, is it? Vector vecTestMins, vecTestMaxs; ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), &vecTestMins, &vecTestMaxs ); // Now that we have the basics, let's expand for hitboxes if appropriate Vector vecWorldHitboxMins, vecWorldHitboxMaxs; if ( ComputeHitboxSurroundingBox( &vecWorldHitboxMins, &vecWorldHitboxMaxs ) ) { VectorMin( vecWorldHitboxMaxs, vecTestMins, vecTestMins ); VectorMax( vecWorldHitboxMaxs, vecTestMaxs, vecTestMaxs ); } Assert( vecTestMins.x >= pVecWorldMins->x && vecTestMins.y >= pVecWorldMins->y && vecTestMins.z >= pVecWorldMins->z ); Assert( vecTestMaxs.x <= pVecWorldMaxs->x && vecTestMaxs.y <= pVecWorldMaxs->y && vecTestMaxs.z <= pVecWorldMaxs->z ); */ #endif }
void CAI_Expresser::BlockSpeechUntil( float time ) { SpeechMsg( GetOuter(), "BlockSpeechUntil(%f) %f\n", time, time - gpGlobals->curtime ); m_flBlockedTalkTime = time; }
UEngine* UGameInstance::GetEngine() const { return CastChecked<UEngine>(GetOuter()); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CAI_LeadBehavior::GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint ) { AI_Waypoint_t *waypoint = GetOuter()->GetNavigator()->GetPath()->GetCurWaypoint(); AI_Waypoint_t *builtwaypoints = NULL; if ( !waypoint ) { // We arrive here twice when lead behaviour starts: // - When the lead behaviour is first enabled. We have no schedule. We want to know if the player is ahead of us. // - A frame later when we've chosen to lead the player, but we still haven't built our route. We know that the // the player isn't lagging, so it's safe to go ahead and simply say he's ahead of us. This avoids building // the temp route twice. if ( IsCurSchedule( SCHED_LEAD_PLAYER, false ) ) return true; // Build a temp route to the gold and use that builtwaypoints = GetOuter()->GetPathfinder()->BuildRoute( GetOuter()->GetAbsOrigin(), m_goal, NULL, GetOuter()->GetDefaultNavGoalTolerance(), GetOuter()->GetNavType(), bits_BUILD_GET_CLOSE ); if ( !builtwaypoints ) return false; GetOuter()->GetPathfinder()->UnlockRouteNodes( builtwaypoints ); waypoint = builtwaypoints; } // Find the nearest node to the target (going forward) float flNearestDist2D = 999999999; float flNearestDist = 999999999; float flPathDist, flPathDist2D; Vector vecNearestPoint; Vector vecPrevPos = GetOuter()->GetAbsOrigin(); for ( ; (waypoint != NULL) ; waypoint = waypoint->GetNext() ) { // Find the closest point on the line segment on the path Vector vecClosest; CalcClosestPointOnLineSegment( targetPos, vecPrevPos, waypoint->GetPos(), vecClosest ); /* if ( builtwaypoints ) { NDebugOverlay::Line( vecPrevPos, waypoint->GetPos(), 0,0,255,true, 10.0 ); } */ vecPrevPos = waypoint->GetPos(); // Find the distance between this test point and our goal point flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() ); if ( flPathDist2D > flNearestDist2D ) continue; flPathDist = vecClosest.z - targetPos.z; flPathDist *= flPathDist; flPathDist += flPathDist2D; if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist )) continue; flNearestDist2D = flPathDist2D; flNearestDist = flPathDist; vecNearestPoint = vecClosest; } if ( builtwaypoints ) { //NDebugOverlay::Line( vecNearestPoint, targetPos, 0,255,0,true, 10.0 ); DeleteAll( builtwaypoints ); } *pVecClosestPoint = vecNearestPoint; return true; }
void CAI_BlendedMotor::BuildVelocityScript( const AILocalMoveGoal_t &move ) { int i; float a; float idealVelocity = GetIdealSpeed(); if (idealVelocity == 0) { idealVelocity = 50; } float idealAccel = GetIdealAccel(); if (idealAccel == 0) { idealAccel = 100; } AI_Movementscript_t script; // set current location as start of script script.vecLocation = GetAbsOrigin(); script.flMaxVelocity = GetCurSpeed(); m_scriptMove.AddToTail( script ); //------------------------- extern ConVar *npc_height_adjust; if (npc_height_adjust->GetBool() && move.bHasTraced && move.directTrace.flTotalDist != move.thinkTrace.flTotalDist) { float flDist = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D(); float flHeight = move.directTrace.vEndPosition.z - m_scriptMove[0].vecLocation.z; float flDelta; if (flDist > 0) { flDelta = flHeight / flDist; } else { flDelta = 0; } m_flPredictiveSpeedAdjust = 1.1 - fabs( flDelta ); m_flPredictiveSpeedAdjust = clamp( m_flPredictiveSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 ); /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { Msg("m_flPredictiveSpeedAdjust %.3f %.1f %.1f\n", m_flPredictiveSpeedAdjust, flHeight, flDist ); NDebugOverlay::Box( move.directTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0,255,255, 0, 0.12 ); } */ } if (npc_height_adjust->GetBool()) { float flDist = (move.thinkTrace.vEndPosition - m_vecPrevOrigin2).Length2D(); float flHeight = move.thinkTrace.vEndPosition.z - m_vecPrevOrigin2.z; float flDelta; if (flDist > 0) { flDelta = flHeight / flDist; } else { flDelta = 0; } float newSpeedAdjust = 1.1 - fabs( flDelta ); newSpeedAdjust = clamp( newSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 ); // debounce speed adjust if (newSpeedAdjust < m_flReactiveSpeedAdjust) { m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.2 + newSpeedAdjust * 0.8; } else { m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.5 + newSpeedAdjust * 0.5; } // filter through origins m_vecPrevOrigin2 = m_vecPrevOrigin1; m_vecPrevOrigin1 = GetAbsOrigin(); /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { NDebugOverlay::Box( m_vecPrevOrigin2, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 ); NDebugOverlay::Box( move.thinkTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 ); Msg("m_flReactiveSpeedAdjust %.3f %.1f %.1f\n", m_flReactiveSpeedAdjust, flHeight, flDist ); } */ } idealVelocity = idealVelocity * min( m_flReactiveSpeedAdjust, m_flPredictiveSpeedAdjust ); //------------------------- bool bAddedExpected = false; // add all waypoint locations and velocities AI_Waypoint_t *pCurWaypoint = GetNavigator()->GetPath()->GetCurWaypoint(); // there has to be at least one waypoint Assert( pCurWaypoint ); while (pCurWaypoint && (pCurWaypoint->NavType() == NAV_GROUND || pCurWaypoint->NavType() == NAV_FLY) /*&& flTotalDist / idealVelocity < 3.0*/) // limit lookahead to 3 seconds { script.Init(); AI_Waypoint_t *pNext = pCurWaypoint->GetNext(); if (ai_path_adjust_speed_on_immediate_turns->GetBool() && !bAddedExpected) { // hack in next expected immediate location for move script.vecLocation = GetAbsOrigin() + move.dir * move.curExpectedDist; bAddedExpected = true; pNext = pCurWaypoint; } else { script.vecLocation = pCurWaypoint->vecLocation; script.pWaypoint = pCurWaypoint; } //DevMsg("waypoint %.1f %.1f %.1f\n", script.vecLocation.x, script.vecLocation.y, script.vecLocation.z ); if (pNext) { switch( pNext->NavType()) { case NAV_GROUND: case NAV_FLY: { Vector d1 = pNext->vecLocation - script.vecLocation; Vector d2 = script.vecLocation - m_scriptMove[m_scriptMove.Count()-1].vecLocation; // remove very short, non terminal ground links // FIXME: is this safe? Maybe just check for co-located ground points? if (d1.Length2D() < 1.0) { /* if (m_scriptMove.Count() > 1) { int i = m_scriptMove.Count() - 1; m_scriptMove[i].vecLocation = pCurWaypoint->vecLocation; m_scriptMove[i].pWaypoint = pCurWaypoint; } */ pCurWaypoint = pNext; continue; } d1.z = 0; VectorNormalize( d1 ); d2.z = 0; VectorNormalize( d2 ); // figure velocity float dot = (DotProduct( d1, d2 ) + 0.2); if (dot > 0) { dot = clamp( dot, 0.0f, 1.0f ); script.flMaxVelocity = idealVelocity * dot; } else { script.flMaxVelocity = 0; } } break; case NAV_JUMP: // FIXME: information about what the jump should look like isn't stored in the waypoints // this'll need to call // GetMoveProbe()->MoveLimit( NAV_JUMP, GetLocalOrigin(), GetPath()->CurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace ); // to get how far/fast the jump will be, but this is also stateless, so it'd call it per frame. // So far it's not clear that the moveprobe doesn't also call this..... { float minJumpHeight = 0; float maxHorzVel = max( GetCurSpeed(), 100 ); float gravity = sv_gravity->GetFloat() * GetOuter()->GetGravity(); Vector vecApex; Vector rawJumpVel = GetMoveProbe()->CalcJumpLaunchVelocity(script.vecLocation, pNext->vecLocation, gravity, &minJumpHeight, maxHorzVel, &vecApex ); script.flMaxVelocity = rawJumpVel.Length2D(); // Msg("%.1f\n", script.flMaxVelocity ); } break; case NAV_CLIMB: { /* CAI_Node *pClimbNode = GetNavigator()->GetNetwork()->GetNode(pNext->iNodeID); check: pClimbNode->m_eNodeInfo bits_NODE_CLIMB_BOTTOM, bits_NODE_CLIMB_ON, bits_NODE_CLIMB_OFF_FORWARD, bits_NODE_CLIMB_OFF_LEFT, bits_NODE_CLIMB_OFF_RIGHT */ script.flMaxVelocity = 0; } break; /* case NAV_FLY: // FIXME: can there be a NAV_GROUND -> NAV_FLY transition? script.flMaxVelocity = 0; break; */ default: break; } } else { script.flMaxVelocity = GetNavigator()->GetArrivalSpeed(); // Assert( script.flMaxVelocity == 0 ); } m_scriptMove.AddToTail( script ); pCurWaypoint = pNext; } //------------------------- // update distances float flTotalDist = 0; for (i = 0; i < m_scriptMove.Count() - 1; i++ ) { flTotalDist += m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); } //------------------------- if ( !m_bDeceleratingToGoal && m_scriptMove.Count() && flTotalDist > 0 ) { float flNeededAccel = DeltaV( m_scriptMove[0].flMaxVelocity, m_scriptMove[m_scriptMove.Count() - 1].flMaxVelocity, flTotalDist ); m_bDeceleratingToGoal = (flNeededAccel < -idealAccel); //Assert( flNeededAccel != idealAccel); } //------------------------- // insert slowdown points due to blocking if (ai_path_insert_pause_at_obstruction->GetBool() && move.directTrace.pObstruction) { float distToObstruction = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D(); // HACK move obstruction out "stepsize" to account for it being based on stand position and not a trace distToObstruction = distToObstruction + 16; InsertSlowdown( distToObstruction, idealAccel, false ); } if (ai_path_insert_pause_at_est_end->GetBool() && GetNavigator()->GetArrivalDistance() > 0.0) { InsertSlowdown( flTotalDist - GetNavigator()->GetArrivalDistance(), idealAccel, true ); } // calc initial velocity based on immediate direction changes if ( ai_path_adjust_speed_on_immediate_turns->GetBool() && m_scriptMove.Count() > 1) { /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { Vector tmp = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation; VectorNormalize( tmp ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 255,255,255, true, 0.1 ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[1].vecLocation + Vector( 0, 0, 10 ), 255,0,0, true, 0.1 ); tmp = GetCurVel(); VectorNormalize( tmp ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 0,0,255, true, 0.1 ); } */ Vector d1 = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation; d1.z = 0; VectorNormalize( d1 ); Vector d2 = GetCurVel(); d2.z = 0; VectorNormalize( d2 ); float dot = (DotProduct( d1, d2 ) + MIN_STEER_DOT); dot = clamp( dot, 0.0f, 1.0f ); m_scriptMove[0].flMaxVelocity = m_scriptMove[0].flMaxVelocity * dot; } // clamp forward velocities for (i = 0; i < m_scriptMove.Count() - 1; i++ ) { // find needed acceleration float dv = m_scriptMove[i+1].flMaxVelocity - m_scriptMove[i].flMaxVelocity; if (dv > 0.0) { // find time, distance to accel to next max vel float t1 = dv / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // is there enough distance if (d1 > m_scriptMove[i].flDist) { float r1, r2; // clamp the next velocity to the possible accel in the given distance if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i].flDist, r1, r2 )) { m_scriptMove[i+1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1; } } } } // clamp decel velocities for (i = m_scriptMove.Count() - 1; i > 0; i-- ) { // find needed deceleration float dv = m_scriptMove[i].flMaxVelocity - m_scriptMove[i-1].flMaxVelocity; if (dv < 0.0) { // find time, distance to decal to next max vel float t1 = -dv / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // is there enough distance if (d1 > m_scriptMove[i-1].flDist) { float r1, r2; // clamp the next velocity to the possible decal in the given distance if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i-1].flDist, r1, r2 )) { m_scriptMove[i-1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1; } } } } /* for (i = 0; i < m_scriptMove.Count(); i++) { NDebugOverlay::Text( m_scriptMove[i].vecLocation, (const char *)CFmtStr( "%.2f ", m_scriptMove[i].flMaxVelocity ), false, 0.1 ); // DevMsg("%.2f ", m_scriptMove[i].flMaxVelocity ); } // DevMsg("\n"); */ // insert intermediate ideal velocities for (i = 0; i < m_scriptMove.Count() - 1;) { // accel to ideal float t1 = (idealVelocity - m_scriptMove[i].flMaxVelocity) / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // decel from ideal float t2 = (idealVelocity - m_scriptMove[i+1].flMaxVelocity) / idealAccel; float d2 = m_scriptMove[i+1].flMaxVelocity * t2 + 0.5 * (idealAccel) * t2 * t2; m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); // is it possible to accel and decal to idealVelocity between next two nodes if (d1 + d2 < m_scriptMove[i].flDist) { Vector start = m_scriptMove[i].vecLocation; Vector end = m_scriptMove[i+1].vecLocation; float dist = m_scriptMove[i].flDist; // insert the two points needed to end accel and start decel if (d1 > 1.0 && t1 > 0.1) { a = d1 / dist; script.Init(); script.vecLocation = end * a + start * (1 - a); script.flMaxVelocity = idealVelocity; m_scriptMove.InsertAfter( i, script ); i++; } if (dist - d2 > 1.0 && t2 > 0.1) { // DevMsg("%.2f : ", a ); a = (dist - d2) / dist; script.Init(); script.vecLocation = end * a + start * (1 - a); script.flMaxVelocity = idealVelocity; m_scriptMove.InsertAfter( i, script ); i++; } i++; } else { // check to see if the amount of change needed to reach target is less than the ideal acceleration float flNeededAccel = fabs( DeltaV( m_scriptMove[i].flMaxVelocity, m_scriptMove[i+1].flMaxVelocity, m_scriptMove[i].flDist ) ); if (flNeededAccel < idealAccel) { // if so, they it's possible to get a bit towards the ideal velocity float v1 = m_scriptMove[i].flMaxVelocity; float v2 = m_scriptMove[i+1].flMaxVelocity; float dist = m_scriptMove[i].flDist; // based on solving: // v1+A*t1-v2-A*t2=0 // v1*t1+0.5*A*t1*t1+v2*t2+0.5*A*t2*t2-D=0 float tmp = idealAccel*dist+0.5*v1*v1+0.5*v2*v2; Assert( tmp >= 0 ); t1 = (-v1+sqrt( tmp )) / idealAccel; t2 = (v1+idealAccel*t1-v2)/idealAccel; // if this assert hits, write down the v1, v2, dist, and idealAccel numbers and send them to me (Ken). // go ahead the comment it out, it's safe, but I'd like to know a test case where it's happening //Assert( t1 > 0 && t2 > 0 ); // check to make sure it's really worth it if (t1 > 0.0 && t2 > 0.0) { d1 = v1 * t1 + 0.5 * idealAccel * t1 * t1; /* d2 = v2 * t2 + 0.5 * idealAccel * t2 * t2; Assert( fabs( d1 + d2 - dist ) < 0.001 ); */ float a = d1 / m_scriptMove[i].flDist; script.Init(); script.vecLocation = m_scriptMove[i+1].vecLocation * a + m_scriptMove[i].vecLocation * (1 - a); script.flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * t1; if (script.flMaxVelocity < idealVelocity) { // DevMsg("insert %.2f %.2f %.2f\n", m_scriptMove[i].flMaxVelocity, script.flMaxVelocity, m_scriptMove[i+1].flMaxVelocity ); m_scriptMove.InsertAfter( i, script ); i += 1; } } } i += 1; } } // clamp min velocities for (i = 0; i < m_scriptMove.Count(); i++) { m_scriptMove[i].flMaxVelocity = max( m_scriptMove[i].flMaxVelocity, MIN_VELOCITY ); } // rebuild fields m_scriptMove[0].flElapsedTime = 0; for (i = 0; i < m_scriptMove.Count() - 1; ) { m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); if (m_scriptMove[i].flMaxVelocity == 0 && m_scriptMove[i+1].flMaxVelocity == 0) { // force a minimum velocity //CE_assert //Assert( 0 ); m_scriptMove[i+1].flMaxVelocity = 1.0; } float t = m_scriptMove[i].flDist / (0.5 * (m_scriptMove[i].flMaxVelocity + m_scriptMove[i+1].flMaxVelocity)); m_scriptMove[i].flTime = t; /* if (m_scriptMove[i].flDist < 0.01) { // Assert( m_scriptMove[i+1].pWaypoint == NULL ); m_scriptMove.Remove( i + 1 ); continue; } */ m_scriptMove[i+1].flElapsedTime = m_scriptMove[i].flElapsedTime + m_scriptMove[i].flTime; i++; } /* for (i = 0; i < m_scriptMove.Count(); i++) { DevMsg("(%.2f : %.2f : %.2f)", m_scriptMove[i].flMaxVelocity, m_scriptMove[i].flDist, m_scriptMove[i].flTime ); // DevMsg("(%.2f:%.2f)", m_scriptMove[i].flTime, m_scriptMove[i].flElapsedTime ); } DevMsg("\n"); */ }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_LeadBehavior::GatherConditions( void ) { BaseClass::GatherConditions(); if ( HasGoal() ) { // Fix for bad transition case (to investigate) if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() > (64*64) && IsCurSchedule( SCHED_LEAD_AWAIT_SUCCESS, false) ) { GetOuter()->ClearSchedule( "Lead behavior - bad transition?" ); } // We have to collect data about the person we're leading around. CBaseEntity *pFollower = AI_GetSinglePlayer(); if( pFollower ) { ClearCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); ClearCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); // Check distance to the follower float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length(); bool bLagging = flFollowerDist > (m_leaddistance*4); if ( bLagging ) { if ( PlayerIsAheadOfMe() ) { bLagging = false; } } // Player heading towards me? // Only factor this in if you're not too far from them if ( flFollowerDist < (m_leaddistance*4) ) { Vector vecVelocity = pFollower->GetSmoothedVelocity(); if ( VectorNormalize(vecVelocity) > 50 ) { Vector vecToPlayer = (GetAbsOrigin() - pFollower->GetAbsOrigin()); VectorNormalize( vecToPlayer ); if ( DotProduct( vecVelocity, vecToPlayer ) > 0.5 ) { SetCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); bLagging = false; } } } // If he's outside our lag range, consider him lagging if ( bLagging ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } else { ClearCondition( COND_LEAD_FOLLOWER_LAGGING ); SetCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); // If he's really close, note that if ( flFollowerDist < m_leaddistance ) { SetCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); } } // To be considered not lagging, the follower must be visible, and within the lead distance if ( GetOuter()->FVisible( pFollower ) && GetOuter()->GetSenses()->ShouldSeeEntity( pFollower ) ) { SetCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); m_LostLOSTimer.Stop(); } else { ClearCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); // We don't have a LOS. But if we did have LOS, don't clear it until the timer is up. if ( m_LostLOSTimer.IsRunning() ) { if ( m_LostLOSTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } } else { m_LostLOSTimer.Start(); } } // Now we want to see if the follower is lost. Being lost means being (far away || out of LOS ) // && some time has passed. Also, lagging players are considered lost if the NPC's never delivered // the start speech, because it means the NPC should run to the player to start the lead. if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) ) { if ( !m_hasspokenstart ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } else { if ( m_args.bStopScenesWhenPlayerLost ) { // Try and stop me speaking my monolog, if I am if ( !m_hasPausedScenes && IsRunningScriptedScene( GetOuter() ) ) { //Msg("Stopping scenes.\n"); PauseActorsScriptedScenes( GetOuter(), false ); m_hasPausedScenes = true; } } if( m_LostTimer.IsRunning() ) { if( m_LostTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } } else { m_LostTimer.Start(); } } } else { // If I was speaking a monolog, resume it if ( m_args.bStopScenesWhenPlayerLost && m_hasPausedScenes ) { if ( IsRunningScriptedScene( GetOuter() ) ) { //Msg("Resuming scenes.\n"); ResumeActorsScriptedScenes( GetOuter(), false ); } m_hasPausedScenes = false; } m_LostTimer.Stop(); ClearCondition( COND_LEAD_FOLLOWER_LOST ); } // Evaluate for success // Success right now means being stationary, close to the goal, and having the player close by if ( !( m_args.flags & AILF_NO_DEF_SUCCESS ) ) { ClearCondition( COND_LEAD_SUCCESS ); // Check Z first, and only check 2d if we're within that bool bWithinZ = fabs(GetLocalOrigin().z - m_goal.z) < 64; if ( bWithinZ && (GetLocalOrigin() - m_goal).Length2D() <= 64 ) { if ( HasCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ) ) { SetCondition( COND_LEAD_SUCCESS ); } else if ( m_successdistance ) { float flDistSqr = (pFollower->GetAbsOrigin() - GetLocalOrigin()).Length2DSqr(); if ( flDistSqr < (m_successdistance*m_successdistance) ) { SetCondition( COND_LEAD_SUCCESS ); } } } } if ( m_MoveMonitor.IsMarkSet() && m_MoveMonitor.TargetMoved( pFollower ) ) SetCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); else ClearCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); } } if( m_args.bLeadDuringCombat ) { ClearCondition( COND_LIGHT_DAMAGE ); ClearCondition( COND_HEAVY_DAMAGE ); } }
void CAI_BlendedMotor::MaintainTurnActivity( void ) { if (m_flNextTurnGesture > gpGlobals->curtime || m_flNextTurnAct > gpGlobals->curtime || GetOuter()->IsMoving() ) { // clear out turn detection if currently turing or moving m_doTurn = m_doRight = m_doLeft = 0; if ( GetOuter()->IsMoving()) { m_flNextTurnAct = gpGlobals->curtime + 0.3; } } else { // detect undirected turns if (m_prevYaw != GetAbsAngles().y) { float diff = UTIL_AngleDiff( m_prevYaw, GetAbsAngles().y ); if (diff < 0.0) { m_doLeft += -diff; } else { m_doRight += diff; } m_prevYaw = GetAbsAngles().y; } // accumulate turn angle, delay response for short turns m_doTurn += m_doRight + m_doLeft; // accumulate random foot stick clearing m_doTurn += enginerandom->RandomFloat( 0.4, 0.6 ); } if (m_doTurn > 15.0f) { // mostly a foot stick clear int iSeq = ACT_INVALID; if (m_doLeft > m_doRight) { iSeq = SelectWeightedSequence( ACT_GESTURE_TURN_LEFT ); } else { iSeq = SelectWeightedSequence( ACT_GESTURE_TURN_RIGHT ); } m_doLeft = 0; m_doRight = 0; if (iSeq != ACT_INVALID) { int iLayer = GetOuter()->AddGestureSequence( iSeq ); if (iLayer != -1) { GetOuter()->SetLayerPriority( iLayer, 100 ); // increase speed if we're getting behind or they're turning quickly float rate = enginerandom->RandomFloat( 0.8, 1.2 ); if (m_doTurn > 90.0) { rate *= 1.5; } GetOuter()->SetLayerPlaybackRate( iLayer, rate ); // disable turing for the duration of the gesture m_flNextTurnAct = gpGlobals->curtime + GetOuter()->GetLayerDuration( iLayer ); } else { // too many active gestures, try again in half a second m_flNextTurnAct = gpGlobals->curtime + 0.3; } } m_doTurn = m_doRight = m_doLeft = 0; } }
USoundCue* USoundCueGraph::GetSoundCue() const { return CastChecked<USoundCue>(GetOuter()); }