class PoliceAgentRegistry { private var m_game : GameInstance; private var m_vehicleAgents : array< VehicleAgent >; private var m_npcAgents : array< NPCAgent >; private var m_requestTickets : array< TicketData >; public function GetNPCList() : array< NPCAgent > { return m_npcAgents; } public function GetVehicleList() : array< VehicleAgent > { return m_vehicleAgents; } public function GetTotalVehicleCount() : Int32 { return m_vehicleAgents.Size(); } public function GetTotalPendingTicketsCount() : Int32 { return m_requestTickets.Size(); } public function GetTotalNPCCount() : Int32 { var i, count : Int32; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( !( m_npcAgents[ i ].isQuestNPC ) ) { count += 1; } } return count; } public function GetFallbackNPCCount() : Int32 { var i, count : Int32; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedAsFallback ) { count += 1; } } return count; } public function GetPendingFallbackOnFootTicketCount() : Int32 { var i, count : Int32; for( i = 0; i < m_requestTickets.Size(); i += 1 ) { if( m_requestTickets[ i ].isFallback ) { count += 1; } } return count; } public function GetPendingVehicleTicketsCount() : Int32 { var i, count : Int32; for( i = 0; i < m_requestTickets.Size(); i += 1 ) { if( m_requestTickets[ i ].policeStrategy != vehiclePoliceStrategy.None ) { count += 1; } } return count; } public function GetExternalNPCCount() : Int32 { var i : Int32; var count : Int32; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( !( m_npcAgents[ i ].spawnedAsFallback ) && m_npcAgents[ i ].spawnedType == DynamicVehicleType.None ) { count += 1; } } return count; } public function GetMaxTacNPCCount() : Int32 { var i : Int32; var count : Int32; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedType == DynamicVehicleType.AV ) { count += 1; } } return count; } public function GetMaxTacNPCList() : array< NPCAgent > { var i : Int32; var list : array< NPCAgent >; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedType == DynamicVehicleType.AV ) { list.PushBack( m_npcAgents[ i ] ); } } return list; } public function IsPreventionMaxTac( puppet : gamePuppet ) : Bool { var i : Int32; var arr : array< NPCAgent >; arr = GetMaxTacNPCList(); for( i = 0; i < arr.Size(); i += 1 ) { if( arr[ i ].id == puppet.GetEntityID() ) { return true; } } return false; } public function DistanceSquaredToTClosestMaxTacAgent( positionToCheck : Vector4 ) : Float { var i : Int32; var maxTacAgentsList : array< NPCAgent >; var minDistanceSquared : Float; var currentDistanceSquared : Float; maxTacAgentsList = GetMaxTacNPCList(); minDistanceSquared = Vector4.DistanceSquared( maxTacAgentsList[ 0 ].unit.GetWorldPosition(), positionToCheck ); for( i = 1; i < maxTacAgentsList.Size(); i += 1 ) { currentDistanceSquared = Vector4.DistanceSquared( maxTacAgentsList[ i ].unit.GetWorldPosition(), positionToCheck ); minDistanceSquared = ( ( currentDistanceSquared < minDistanceSquared ) ? ( currentDistanceSquared ) : ( minDistanceSquared ) ); } return minDistanceSquared; } public function GetEngagedVehicleList() : array< VehicleAgent > { var i : Int32; var engagedVehicles : array< VehicleAgent >; var vehicle : VehicleAgent; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedType != DynamicVehicleType.Car || !( NPCPuppet.IsInCombat( m_npcAgents[ i ].unit ) ) ) { continue; } if( !( TryGetVehicleAgentByID( m_npcAgents[ i ].TryGetAssignedVehicleId(), vehicle ) ) ) { continue; } if( engagedVehicles.Contains( vehicle ) ) { continue; } engagedVehicles.PushBack( vehicle ); } return engagedVehicles; } public function GetEngagedNotDisengagingVehicleList( out vehicleArray : array< VehicleAgent > ) : Bool { var i : Int32; vehicleArray.Clear(); vehicleArray = GetEngagedVehicleList(); for( i = vehicleArray.Size() - 1; i >= 0; i -= 1 ) { if( vehicleArray[ i ].lifetimeStatus == LifetimeStatus.Disengaging ) { vehicleArray.Erase( i ); } } return vehicleArray.Size() > 0; } public function GetSupportVehicleList() : array< VehicleAgent > { var i : Int32; var supportVehicles : array< VehicleAgent >; var vehicle : VehicleAgent; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedType != DynamicVehicleType.Car || NPCPuppet.IsInCombat( m_npcAgents[ i ].unit ) ) { continue; } if( !( TryGetVehicleAgentByID( m_npcAgents[ i ].TryGetAssignedVehicleId(), vehicle ) ) ) { continue; } if( supportVehicles.Contains( vehicle ) ) { continue; } supportVehicles.PushBack( vehicle ); } return supportVehicles; } public function GetAvCount() : Int32 { var i : Int32; var count : Int32; for( i = 0; i < m_vehicleAgents.Size(); i += 1 ) { if( m_vehicleAgents[ i ].spawnedType == DynamicVehicleType.AV ) { count += 1; } } return count; } public function GetRoadblockCount() : Int32 { var i : Int32; var count : Int32; for( i = 0; i < m_vehicleAgents.Size(); i += 1 ) { if( m_vehicleAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockade || m_vehicleAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockadeWithAV ) { count += 1; } } return count; } public function GetRoadblockNPCList() : array< NPCAgent > { var i : Int32; var list : array< NPCAgent >; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockade || m_npcAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockadeWithAV ) { list.PushBack( m_npcAgents[ i ] ); } } return list; } public function GetRoadblockVehicleList() : array< VehicleAgent > { var i : Int32; var list : array< VehicleAgent >; for( i = 0; i < m_vehicleAgents.Size(); i += 1 ) { if( m_vehicleAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockade || m_vehicleAgents[ i ].spawnedType == DynamicVehicleType.RoadBlockadeWithAV ) { list.PushBack( m_vehicleAgents[ i ] ); } } return list; } public function GetSupportVehiclesWithStrategyCount( strategy : vehiclePoliceStrategy ) : Int32 { var count : Int32; var i : Int32; var list : array< VehicleAgent >; list = GetSupportVehicleList(); for( i = 0; i < list.Size(); i += 1 ) { if( list[ i ].unit.GetPoliceStrategy() == strategy ) { count += 1; } } return count; } public function GetEngagedVehicleCount() : Int32 { var list : array< VehicleAgent >; list = GetEngagedVehicleList(); return list.Size(); } public function GetSupportVehicleCount() : Int32 { var list : array< VehicleAgent >; list = GetSupportVehicleList(); return list.Size(); } public function GetNPCsAssignedToVehicle( vehicleEntityId : EntityID, toFill : ref< array< NPCAgent > > ) { var i : Int32; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { if( m_npcAgents[ i ].TryGetAssignedVehicleId() == vehicleEntityId ) { toFill.PushBack( m_npcAgents[ i ] ); } } } public function GetVehiclesWithoutRegisteredPassengers( toFill : ref< array< VehicleAgent > > ) { var i : Int32; var vehiclesAssignedToNPCs : array< EntityID >; var vehicleID : EntityID; for( i = 0; i < m_npcAgents.Size(); i += 1 ) { vehicleID = m_npcAgents[ i ].TryGetAssignedVehicleId(); if( !( vehiclesAssignedToNPCs.Contains( vehicleID ) ) ) { vehiclesAssignedToNPCs.PushBack( vehicleID ); } } for( i = 0; i < m_vehicleAgents.Size(); i += 1 ) { if( m_vehicleAgents[ i ].spawnedType != DynamicVehicleType.Car ) { continue; } if( !( m_vehicleAgents[ i ].everHadPassengers ) ) { continue; } vehicleID = m_vehicleAgents[ i ].id; if( vehiclesAssignedToNPCs.Contains( vehicleID ) ) { vehiclesAssignedToNPCs.Remove( vehicleID ); continue; } toFill.PushBack( m_vehicleAgents[ i ] ); } } public function IsPoliceInCombatWithPalyer() : Bool { var i : Int32; var player : Entity; player = GetPlayerObject( m_game ); for( i = 0; i <= m_npcAgents.Size(); i += 1 ) { if( NPCPuppet.IsInCombatWithTarget( m_npcAgents[ i ].unit, player ) ) { return true; } } return false; } public function HasPoliceRecentlyDeescalated() : Bool { var threatData : DroppedThreatData; var targetTrackingExtension : TargetTrackingExtension; var i : Int32; for( i = 0; i <= m_npcAgents.Size(); i += 1 ) { targetTrackingExtension = ( ( TargetTrackingExtension )( m_npcAgents[ i ].unit.GetTargetTrackerComponent() ) ); if( targetTrackingExtension ) { threatData = targetTrackingExtension.GetRecentlyDroppedThreat(); if( threatData.threat && ( ( GameObject )( threatData.threat ) ).IsPlayer() ) { return true; } } } return false; } public function HasNPCBeenAttackedByPlayer( id : EntityID ) : Bool { var agent : NPCAgent; if( TryGetNPCAgentByID( id, agent ) ) { if( agent.hasBeenAttackedByPlayer ) { return true; } } return false; } public static function Construct( game : GameInstance ) : PoliceAgentRegistry { var registry : PoliceAgentRegistry; registry = new PoliceAgentRegistry; registry.m_game = game; return registry; } public function Contains( id : EntityID ) : Bool { var contains : Bool; contains = TryGetIndexOf_NPC( id ) || TryGetIndexOf_Vehicle( id ); return contains; } public function TryGetNPCAgentByID( id : EntityID, agent : ref< NPCAgent > ) : Bool { var i : Int32; if( TryGetIndexOf_NPC( id, i ) ) { agent = m_npcAgents[ i ]; return true; } return false; } public function TryGetVehicleAgentByID( id : EntityID, agent : ref< VehicleAgent > ) : Bool { var i : Int32; if( TryGetIndexOf_Vehicle( id, i ) ) { agent = m_vehicleAgents[ i ]; return true; } return false; } public function GetNPCSpawnedType( id : EntityID ) : DynamicVehicleType { var agent : NPCAgent; if( TryGetNPCAgentByID( id, agent ) ) { return agent.spawnedType; } return DynamicVehicleType.None; } public function RegisterAgent( unit : GameObject, type : DynamicVehicleType, optional strategy : vehiclePoliceStrategy, optional isFallback : Bool, overrideExisting : Bool ) : Bool { var npcAgent : NPCAgent; var vehicleAgent : VehicleAgent; var i : Int32; var id : EntityID; id = unit.GetEntityID(); if( unit.IsPuppet() ) { if( TryGetIndexOf_NPC( id, i ) ) { if( overrideExisting ) { npcAgent = m_npcAgents[ i ]; } else { return false; } } else { npcAgent = new NPCAgent; m_npcAgents.PushBack( npcAgent ); } npcAgent.id = id; npcAgent.gameObject = unit; npcAgent.unit = ( ( ScriptedPuppet )( unit ) ); npcAgent.spawnedType = type; npcAgent.spawnedAsFallback = isFallback; npcAgent.isQuestNPC = NPCManager.HasTag( ( ( ScriptedPuppet )( unit ) ).GetRecordID(), 'Scripted_Patrol' ); if( type == DynamicVehicleType.AV ) { StatusEffectHelper.ApplyStatusEffectOnSelf( unit.GetGame(), T"BaseStatusEffect.MaxtacCloaked", unit.GetEntityID() ); } } else if( unit.IsVehicle() ) { if( TryGetIndexOf_Vehicle( id, i ) ) { if( overrideExisting ) { vehicleAgent = m_vehicleAgents[ i ]; } else { return false; } } else { vehicleAgent = new VehicleAgent; m_vehicleAgents.PushBack( vehicleAgent ); } vehicleAgent.id = id; vehicleAgent.gameObject = unit; vehicleAgent.unit = ( ( VehicleObject )( unit ) ); vehicleAgent.spawnedType = type; } return true; } public function UnregisterAgent( id : EntityID ) : UnregisterResult { var i : Int32; var result : UnregisterResult; if( TryGetIndexOf_NPC( id, i ) ) { result.success = true; result.isVehicle = false; result.spawnedType = m_npcAgents[ i ].spawnedType; m_npcAgents.Erase( i ); } else if( TryGetIndexOf_Vehicle( id, i ) ) { result.success = true; result.isVehicle = true; result.spawnedType = m_vehicleAgents[ i ].spawnedType; m_vehicleAgents.Erase( i ); } return result; } public function UnregisterAll() { m_npcAgents.Clear(); m_vehicleAgents.Clear(); } public function UpdateVehiclePassengerCount( vehicleID : EntityID, passengers : Int32 ) { var i : Int32; var vehicleAgent : VehicleAgent; if( TryGetIndexOf_Vehicle( vehicleID, i ) ) { vehicleAgent = m_vehicleAgents[ i ]; vehicleAgent.passangers = passengers; vehicleAgent.slotsAvailable = vehicleAgent.slotsTotal - passengers; if( passengers > 0 ) { vehicleAgent.everHadPassengers = true; } } } private function TryGetIndexOf_NPC( id : EntityID, optional index : ref< Int32 > ) : Bool { var i : Int32; for( i = 0; i <= ( m_npcAgents.Size() - 1 ); i += 1 ) { if( m_npcAgents[ i ].id == id ) { index = i; return true; } } return false; } private function TryGetIndexOf_Vehicle( id : EntityID, optional index : ref< Int32 > ) : Bool { var i : Int32; for( i = 0; i <= ( m_vehicleAgents.Size() - 1 ); i += 1 ) { if( m_vehicleAgents[ i ].id == id ) { index = i; return true; } } return false; } private const function TryGetIndexOf_Ticket( id : Uint32, optional index : ref< Int32 > ) : Bool { var i : Int32; for( i = 0; i <= ( m_requestTickets.Size() - 1 ); i += 1 ) { if( m_requestTickets[ i ].requestID == id ) { index = i; return true; } } return false; } public function CreateTicket( request : Uint32, strategy : vehiclePoliceStrategy, optional isFallback : Bool ) { var ticket : TicketData; ticket.requestID = request; ticket.policeStrategy = strategy; ticket.isFallback = isFallback; m_requestTickets.PushBack( ticket ); } public function PopRequestTicket( request : Uint32, optional ticketData : ref< TicketData > ) : Bool { var i : Int32; if( !( TryGetIndexOf_Ticket( request, i ) ) ) { return false; } ticketData = m_requestTickets[ i ]; m_requestTickets.Erase( i ); return true; } } class AgentBase { var id : EntityID; var gameObject : weak< GameObject >; var spawnedType : DynamicVehicleType; } class NPCAgent extends AgentBase { var unit : weak< ScriptedPuppet >; var hasBeenAttackedByPlayer : Bool; var isQuestNPC : Bool; var spawnedAsFallback : Bool; var markedToBeDespawned : Bool; public function TryGetAssignedVehicleId() : EntityID { var assignedVehicleID : EntityID; if( unit ) { assignedVehicleID = unit.GetAIControllerComponent().GetAssignedVehicleId(); } return assignedVehicleID; } } enum LifetimeStatus { Base = 0, Near = 1, Disengaging = 2, } class VehicleAgent extends AgentBase { var unit : weak< VehicleObject >; var passangers : Int32; var slotsTotal : Int32; var slotsReserved : Int32; var slotsAvailable : Int32; var everHadPassengers : Bool; default everHadPassengers = false; var distanceToPlayerSquared : Float; var lifetimeStatus : LifetimeStatus; var nearTimeStamp : Float; default nearTimeStamp = -1.f; public function UpdateLifetimeStatus( playerPos : Vector4 ) { var now : Float; var unitStrong : VehicleObject; var vehicleStrategyDespawnDistanceSquared : Float; var vehicleStrategyNearDespawnDistanceSquared : Float; var vehicleStrategyEnterNearStateDistanceSquared : Float; unitStrong = unit; vehicleStrategyDespawnDistanceSquared = TweakDBInterface.GetFloat( T"PreventionSystem.setup.vehicleStrategyDespawnDistanceSquared", 490000.0 ); vehicleStrategyNearDespawnDistanceSquared = TweakDBInterface.GetFloat( T"PreventionSystem.setup.vehicleStrategyNearDespawnDistanceSquared", 40000.0 ); vehicleStrategyEnterNearStateDistanceSquared = TweakDBInterface.GetFloat( T"PreventionSystem.setup.vehicleStrategyEnterNearStateDistanceSquared", 40000.0 ); if( !( unitStrong ) ) { return; } if( spawnedType != DynamicVehicleType.Car ) { return; } if( lifetimeStatus != LifetimeStatus.Disengaging ) { if( unitStrong.IsDestroyed() ) { Disengage(); return; } } distanceToPlayerSquared = Vector4.DistanceSquared( playerPos, unitStrong.GetWorldPosition() ); now = EngineTime.ToFloat( GameInstance.GetEngineTime( unitStrong.GetGame() ) ); if( lifetimeStatus == LifetimeStatus.Base ) { if( distanceToPlayerSquared > vehicleStrategyDespawnDistanceSquared ) { Disengage(); } else if( distanceToPlayerSquared < vehicleStrategyEnterNearStateDistanceSquared ) { if( nearTimeStamp < 0.0 ) { nearTimeStamp = now; } else if( ( now - nearTimeStamp ) > 3.0 ) { lifetimeStatus = LifetimeStatus.Near; } } else if( nearTimeStamp > 0.0 ) { nearTimeStamp = -1.0; } } else if( lifetimeStatus == LifetimeStatus.Near && ( distanceToPlayerSquared > vehicleStrategyNearDespawnDistanceSquared ) ) { Disengage(); } } public function Disengage() { var unitStrong : VehicleObject; unitStrong = unit; if( lifetimeStatus == LifetimeStatus.Disengaging ) { return; } if( unitStrong ) { GameInstance.GetPreventionSpawnSystem( unitStrong.GetGame() ).RequestDespawnVehicleAndPassengers( unitStrong ); } lifetimeStatus = LifetimeStatus.Disengaging; } } struct TicketData { var requestID : Uint32; var policeStrategy : vehiclePoliceStrategy; var vehicleType : DynamicVehicleType; var isFallback : Bool; } struct UnregisterResult { var success : Bool; default success = false; var isVehicle : Bool; default isVehicle = false; var spawnedType : DynamicVehicleType; default spawnedType = DynamicVehicleType.None; } class IsNPCMarkedForDespawn extends AIbehaviorconditionScript { protected override function Check( context : ScriptExecutionContext ) : AIbehaviorConditionOutcomes { var preventionSystem : PreventionSystem; var isMarkedForDespawn : Bool; preventionSystem = ( ( PreventionSystem )( GameInstance.GetScriptableSystemsContainer( ScriptExecutionContext.GetOwner( context ).GetGame() ).Get( PreventionSystem.GetSystemName() ) ) ); if( !( preventionSystem ) ) { return false; } if( preventionSystem.TryGetNPCMarkedForDespawnAI( ScriptExecutionContext.GetOwner( context ).GetEntityID(), isMarkedForDespawn ) ) { return isMarkedForDespawn; } return false; } } class MarkNPCAgentForDespawn extends AIbehaviortaskScript { protected override function Activate( context : ScriptExecutionContext ) { var preventionSystem : PreventionSystem; preventionSystem = ( ( PreventionSystem )( GameInstance.GetScriptableSystemsContainer( ScriptExecutionContext.GetOwner( context ).GetGame() ).Get( PreventionSystem.GetSystemName() ) ) ); if( !( preventionSystem ) ) { return; } preventionSystem.TrySetNPCMarkedForDespawnAI( ScriptExecutionContext.GetOwner( context ).GetEntityID(), true ); } }