11 [
Attribute(
"X_QRF", UIWidgets.Auto,
"Name of the QRF layer that contains pool of spawn points")]
12 protected string m_sQRFLayerName;
17 [
Attribute(
"5", UIWidgets.Auto,
"Numeric value which interpretation depends on the m_eThresholdType f.e. if Threshold Type == REMAINING_UNITS then if Threshold >= (number of units alive) then send QRF")]
18 protected int m_iThreshold;
20 [
Attribute(
"5", UIWidgets.Auto,
"How many times this Area can send QRF where -1 == unlimited")]
21 protected int m_iNumberOfAvailableQRFWaves;
23 [
Attribute(
"1", UIWidgets.Auto,
"How much threat level has to be increased each time that threshold is reached")]
24 protected float m_fThreatLevelEscalation;
26 [
Attribute(
"0", UIWidgets.Auto,
"How long (in seconds) game should wait when threshold is reached before spawning QRF where 0 == imminently",
"0 inf 1")]
27 protected float m_fQRFSpawnDelay;
29 [
Attribute(
"300", UIWidgets.Auto,
"How long (in seconds) game should wait until next QRF will be possible to be requested where 0 == no delay",
"0 inf 1")]
30 protected float m_fQRFNextWaveDelay;
33 protected ref array<ref SCR_QRFGroupConfig> m_aGroupList;
35 [
Attribute(
desc:
"List defining maximal distance for QRF unit of given type")]
36 protected ref array<ref SCR_QRFTypeMaxDistance> m_aQRFMaxDistanceConfig;
38 [
Attribute(
desc:
"List of waypoints for QRF that will be applied in order (to aplicable group type)")]
39 protected ref array<ref SCR_QRFWaypointConfig> m_aWPConfig;
41 [
Attribute(
desc:
"Sound event name that will be played on dead soldier entity when QRF is requested")]
42 protected string m_sQRFRequestedSoundEventName;
44 [
Attribute(
desc:
"Sound event name that will be played on dead soldier entity when QRF is spawned")]
45 protected string m_sQRFSentSoundEventName;
48 protected ResourceName m_sSoundProjectFile;
50 [
Attribute(
desc:
"Actions that will be triggered once QRF are dispatched.")]
51 ref array<ref SCR_ScenarioFrameworkActionBase>
m_aActions;
53 protected ref array<SCR_ScenarioFrameworkQRFSlotAI> m_aQRFSpawnPoints = {};
54 protected SCR_ScenarioFrameworkArea m_AreaFramework;
56 protected int m_iNumberOfSoldiersInTheArea;
57 protected int m_iRemovedSoldier;
58 protected float m_fNextWaveDelayClock;
59 protected float m_fThreatLevel = 1;
60 protected int m_iSpawnTickets;
61 protected bool m_bWaitingForDelayedSpawn;
62 protected bool m_bWaitingForNextWave;
63 protected vector m_vTargetPosition;
64 protected ref array<ref SCR_QRFVehicleSpawnConfig> m_aVehicleSpawnQueueConfig = {};
69 protected bool WatchAIGroup(
IEntity entities)
71 return WatchAIGroup({entities});
77 protected bool WatchAIGroup(array<IEntity> entities)
84 foreach (
IEntity entity : entities)
94 if (aiGroup.GetAgentsCount() == 0)
95 m_iNumberOfSoldiersInTheArea++;
97 m_iNumberOfSoldiersInTheArea += aiGroup.GetAgentsCount();
116 AIAgent agent = AIAgent.Cast(entity);
120 if (!SCR_ChimeraCharacter.Cast(entity))
123 AIControlComponent control = AIControlComponent.Cast(entity.
FindComponent(AIControlComponent));
127 agent = control.GetControlAIAgent();
135 protected void OnDelayedGroupMembersSpawned(
SCR_AIGroup group)
137 m_iNumberOfSoldiersInTheArea++;
141 protected void OnGroupCompositionChanged(
SCR_AIGroup group, AIAgent agent)
146 if (!agent.GetControlledEntity())
149 SCR_ChimeraCharacter
char = SCR_ChimeraCharacter.Cast(agent.GetControlledEntity());
153 SCR_CharacterControllerComponent controller = SCR_CharacterControllerComponent.Cast(
char.
GetCharacterController());
157 vector tmpVec = agent.GetControlledEntity().GetOrigin();
158 if (m_vTargetPosition == vector.Zero)
159 m_vTargetPosition = tmpVec;
161 m_vTargetPosition = vector.Lerp(m_vTargetPosition, tmpVec, 0.95);
164 ThresholdValidation(
char)
168 protected void ThresholdValidation(
IEntity killedEntity = null)
170 bool startQRFProcedure;
171 switch (m_eThresholdType)
175 if (m_iRemovedSoldier >= m_iThreshold)
176 startQRFProcedure =
true;
182 if (m_iNumberOfSoldiersInTheArea - m_iRemovedSoldier <= m_iThreshold)
183 startQRFProcedure =
true;
189 if (m_iNumberOfSoldiersInTheArea > 0 && (m_iNumberOfSoldiersInTheArea - m_iRemovedSoldier) * 100 / m_iNumberOfSoldiersInTheArea <= m_iThreshold)
190 startQRFProcedure =
true;
195 if (!startQRFProcedure)
198 if (m_bWaitingForDelayedSpawn)
200 if (!m_bWaitingForNextWave && m_iNumberOfAvailableQRFWaves - 1 > 0 && m_fQRFSpawnDelay > 0)
202 float callTime = m_fNextWaveDelayClock -
GetGame().GetWorld().GetWorldTime();
206 m_bWaitingForNextWave =
true;
207 SCR_ScenarioFrameworkSystem.GetCallQueuePausable().CallLater(StartQRFProcedure, callTime,
param1: killedEntity);
212 if (m_bWaitingForNextWave)
215 StartQRFProcedure(killedEntity);
219 protected void ThresholdHandling()
221 switch (m_eThresholdType)
225 m_iRemovedSoldier = 0;
230 m_iNumberOfSoldiersInTheArea -= m_iRemovedSoldier;
236 protected void StartQRFProcedure(
IEntity killedEntity)
238 if (m_iNumberOfAvailableQRFWaves == 0)
241 float time =
GetGame().GetWorld().GetWorldTime();
242 if (m_fNextWaveDelayClock > time && !m_bWaitingForDelayedSpawn && !m_bWaitingForNextWave)
245 if (m_fQRFSpawnDelay > 0)
247 if (!m_bWaitingForDelayedSpawn)
249 if (m_bWaitingForNextWave)
250 m_bWaitingForNextWave =
false;
253 m_fNextWaveDelayClock = time + m_fQRFNextWaveDelay * 1000;
254 m_bWaitingForDelayedSpawn =
true;
255 SCR_ScenarioFrameworkSystem.GetCallQueuePausable().CallLater(StartQRFProcedure, m_fQRFSpawnDelay * 1000,
param1: killedEntity);
257 DoPlaySoundOnEntityPosition(killedEntity, m_sSoundProjectFile, m_sQRFRequestedSoundEventName);
263 m_bWaitingForDelayedSpawn =
false;
269 m_fNextWaveDelayClock = time + m_fQRFNextWaveDelay * 1000;
273 DoPlaySoundOnEntityPosition(killedEntity, m_sSoundProjectFile, m_sQRFSentSoundEventName);
275 m_iNumberOfSoldiersInTheArea -= m_iRemovedSoldier;
276 m_iSpawnTickets += m_fThreatLevel;
277 m_fThreatLevel += m_fThreatLevelEscalation;
278 if (m_aQRFSpawnPoints.IsEmpty())
281 while (m_iSpawnTickets > 0)
283 SCR_QRFGroupConfig selectedGroup = SelectRandomGroup(m_iSpawnTickets);
290 m_iSpawnTickets -= selectedGroup.GetSpawnCost();
291 SCR_ScenarioFrameworkQRFSlotAI selectedSpawnPoint = SelectRandomSpawnpoint(m_aQRFSpawnPoints, selectedGroup.GetGroupType());
292 if (!selectedSpawnPoint)
298 if (selectedGroup.GetNumberOfAvailableGroups() > 0)
299 selectedGroup.SetNumberOfAvailableGroups(selectedGroup.GetNumberOfAvailableGroups() - 1);
301 selectedSpawnPoint.SetObjectToSpawn(selectedGroup.GetGroupPrefabName());
302 selectedSpawnPoint.SetEnableRepeatedSpawn(
true);
303 selectedSpawnPoint.SetIsTerminated(
false);
304 if (selectedSpawnPoint.GetNumberOfExistingWaypoints())
305 selectedSpawnPoint.ClearWaypoints();
308 IEntity spawnedEntity = selectedSpawnPoint.GetSpawnedEntity();
312 if (WatchAIGroup(aiGroup))
320 if (wp.GetDistanceOffsetToTargetLocation())
321 wpPosition = SCR_Math3D.MoveTowards(m_vTargetPosition, selectedSpawnPoint.GetOwner().GetOrigin(), wp.GetDistanceOffsetToTargetLocation());
323 wpPosition = m_vTargetPosition;
325 if (
float.AlmostEqual(vector.DistanceXZ(wpPosition, selectedSpawnPoint.GetOwner().GetOrigin()), 0, 1))
328 wpPosition[1] =
GetGame().GetWorld().GetSurfaceY(wpPosition[0], wpPosition[2]);
329 AIWaypoint aiWP = selectedSpawnPoint.CreateWaypoint(wpPosition, wp.GetWaypointPrefabName());
331 aiGroup.AddWaypoint(aiWP);
337 if (
Vehicle.Cast(spawnedEntity))
339 SCR_BaseCompartmentManagerComponent compartmentManager = SCR_BaseCompartmentManagerComponent.Cast(spawnedEntity.
FindComponent(SCR_BaseCompartmentManagerComponent));
340 if (compartmentManager)
342 compartmentManager.GetOnDoneSpawningDefaultOccupants().Insert(OnFinishedSpawningVehicleOccupants);
347 m_aVehicleSpawnQueueConfig.Insert(
new SCR_QRFVehicleSpawnConfig(compartmentManager, selectedGroup.GetGroupType(), m_vTargetPosition, selectedSpawnPoint));
348 compartmentManager.SpawnDefaultOccupants(compartmentTypes);
353 if (m_iNumberOfAvailableQRFWaves > 0)
354 m_iNumberOfAvailableQRFWaves--;
356 m_vTargetPosition = vector.Zero;
358 foreach (SCR_ScenarioFrameworkActionBase actions :
m_aActions)
365 protected SCR_QRFGroupConfig SelectRandomGroup(
int maxCost)
368 bool availableGroupAtFullPrice;
369 array<SCR_QRFGroupConfig> verifiedList = {};
370 foreach (SCR_QRFGroupConfig group : m_aGroupList)
375 if (group.GetNumberOfAvailableGroups() == -1 || group.GetNumberOfAvailableGroups() > 0)
377 if (group.GetSpawnCost() == maxCost)
379 if (!availableGroupAtFullPrice)
381 availableGroupAtFullPrice =
true;
382 verifiedList.Clear();
384 verifiedList.Insert(group);
386 else if (group.GetSpawnCost() < maxCost && !availableGroupAtFullPrice)
388 verifiedList.Insert(group);
393 if (availableGroupAtFullPrice)
394 return verifiedList.GetRandomElement();
396 SCR_QRFGroupConfig returnedGroup;
397 if (!verifiedList.IsEmpty())
399 int i, numOfTries, count = verifiedList.Count();
400 while (numOfTries < 10)
402 i = Math.RandomInt(0, count);
403 if (!returnedGroup || returnedGroup && returnedGroup.GetSpawnCost() < verifiedList[i].GetSpawnCost())
404 returnedGroup = verifiedList[i];
410 return returnedGroup;
414 protected SCR_ScenarioFrameworkQRFSlotAI SelectRandomSpawnpoint(array<SCR_ScenarioFrameworkQRFSlotAI> aListOfSpawnPoints,
SCR_EQRFGroupType searchedType)
416 float maxSpawnDistance = GetMaxDistanceForUnitType(searchedType);
417 array<SCR_ScenarioFrameworkQRFSlotAI> verifiedList = {};
418 foreach (SCR_ScenarioFrameworkQRFSlotAI spawn : aListOfSpawnPoints)
420 if (!(spawn.GetGroupType() & searchedType))
423 if (!CheckSpawnPointSafeZones(spawn.GetOwner().GetOrigin(), spawn.GetSpawnSafeZones(), searchedType))
426 if (maxSpawnDistance > -1 && vector.Distance(m_vTargetPosition, spawn.GetOwner().GetOrigin()) > maxSpawnDistance)
429 verifiedList.Insert(spawn);
433 if (verifiedList.Count() > 0)
434 return verifiedList.GetRandomElement();
436 float distanceToTarget, cloasestPosDistance =
float.MAX;
437 foreach (
int i, SCR_ScenarioFrameworkQRFSlotAI spawn : aListOfSpawnPoints)
439 if ( !(spawn.GetGroupType() & searchedType) )
442 if (!CheckSpawnPointSafeZones(spawn.GetOwner().GetOrigin(), spawn.GetSpawnSafeZones(), searchedType))
445 distanceToTarget = vector.Distance(m_vTargetPosition, spawn.GetOwner().GetOrigin());
446 if (distanceToTarget < cloasestPosDistance)
448 cloasestPosDistance = distanceToTarget;
456 return aListOfSpawnPoints[
index];
461 protected bool CheckSpawnPointSafeZones(vector spawnPointPosition, array<ref SCR_QRFSpawnSafeZone> spawnSafeZones,
SCR_EQRFGroupType searchedType)
463 if (spawnSafeZones.IsEmpty())
466 array<vector> aObserversPositions = {};
467 array<int> playerIds = {};
468 PlayerManager playerManager =
GetGame().GetPlayerManager();
470 SCR_DamageManagerComponent damageManager;
471 playerManager.GetPlayers(playerIds);
473 foreach (
int playerId : playerIds)
475 player = playerManager.GetPlayerControlledEntity(playerId);
479 damageManager = SCR_DamageManagerComponent.GetDamageManager(player);
480 if (damageManager && damageManager.GetState() !=
EDamageState.DESTROYED)
481 aObserversPositions.Insert(player.
GetOrigin());
484 foreach (SCR_QRFSpawnSafeZone safeZone : spawnSafeZones)
486 if (safeZone.GetGroupType() != searchedType)
489 foreach (vector observerPos : aObserversPositions)
491 if (vector.Distance(observerPos, spawnPointPosition) < safeZone.GetMinDistanceToClosestObserver())
502 foreach (SCR_QRFTypeMaxDistance
conf : m_aQRFMaxDistanceConfig)
504 if (
conf.GetGroupType() == unitType)
505 return conf.GetMaxSpawnDistance();
512 protected void OnFinishedSpawningVehicleOccupants(SCR_BaseCompartmentManagerComponent compartmentManager, array<IEntity> occupants,
bool wasCanceled)
514 compartmentManager.GetOnDoneSpawningDefaultOccupants().Remove(OnFinishedSpawningVehicleOccupants);
518 if (occupants.IsEmpty())
521 SCR_ChimeraCharacter occupant = SCR_ChimeraCharacter.Cast(occupants[0]);
525 AIControlComponent control = AIControlComponent.Cast(occupant.FindComponent(AIControlComponent));
529 AIAgent agent = control.GetControlAIAgent();
537 if (!WatchAIGroup(aiGroup))
541 for (
int i, count = m_aVehicleSpawnQueueConfig.Count(); i < count; i++)
543 if (m_aVehicleSpawnQueueConfig[i].m_VehicleCompartmentMGR != compartmentManager)
548 if (wp.GetOrderType() !=
SCR_EQRFGroupOrderType.ANY && wp.GetOrderType() != m_aVehicleSpawnQueueConfig[i].m_eGroupType)
551 if (wp.GetDistanceOffsetToTargetLocation())
552 wpPosition = SCR_Math3D.MoveTowards(m_aVehicleSpawnQueueConfig[i].m_vTargetPosition, occupant.GetOrigin(), wp.GetDistanceOffsetToTargetLocation());
554 wpPosition = m_aVehicleSpawnQueueConfig[i].m_vTargetPosition;
556 if (
float.AlmostEqual(vector.DistanceXZ(wpPosition, occupant.GetOrigin()), 0, 1))
559 AIWaypoint aiWP = m_aVehicleSpawnQueueConfig[i].m_Slot.CreateWaypoint(wpPosition, wp.GetWaypointPrefabName());
561 aiGroup.AddWaypoint(aiWP);
563 m_aVehicleSpawnQueueConfig.Remove(i);
570 protected void DoPlaySoundOnEntityPosition(
IEntity entity,
string soundFileName,
string soundEventName)
575 if (SCR_StringHelper.IsEmptyOrWhiteSpace(soundFileName))
578 if (SCR_StringHelper.IsEmptyOrWhiteSpace(soundEventName))
581 SCR_ScenarioFrameworkSystem scenarioFrameworkSystem = SCR_ScenarioFrameworkSystem.GetInstance();
582 if (!scenarioFrameworkSystem)
585 SCR_GadgetManagerComponent gadgetManager = SCR_GadgetManagerComponent.Cast(entity.
FindComponent(SCR_GadgetManagerComponent));
602 m_AreaFramework = thisLayer.GetParentArea();
603 if (!m_AreaFramework)
615 array<SCR_ScenarioFrameworkLayerBase> childLayers = m_AreaFramework.GetChildrenEntities();
616 if (childLayers.IsEmpty())
618 IEntity child = m_AreaFramework.GetOwner().GetChildren();
624 childLayers.Insert(
layer);
628 if (childLayers.IsEmpty())
632 m_iNumberOfSoldiersInTheArea = 0;
638 if (
layer.GetName() == m_sQRFLayerName)
642 SCR_ScenarioFrameworkQRFSlotAI qrfSlot;
645 qrfSlot = SCR_ScenarioFrameworkQRFSlotAI.Cast(child.
FindComponent(SCR_ScenarioFrameworkQRFSlotAI));
646 if (qrfSlot && !m_aQRFSpawnPoints.Contains(qrfSlot))
647 m_aQRFSpawnPoints.Insert(qrfSlot);
655 if (m_aQRFSpawnPoints && m_aQRFSpawnPoints.IsEmpty())
656 Print(
"SCR_ScenarioFrameworkActionQRFDispacher.OnActivate: Layer " + m_AreaFramework.GetName() +
" doesn't have any spawn points for its QRF dispatcher!",
LogLevel.ERROR);
658 CheckForAIToWatch(m_AreaFramework.GetOwner());
663 protected void CheckForAIToWatch(
IEntity entity)
670 if (SCR_ScenarioFrameworkSlotAI.Cast(
layer) || SCR_ScenarioFrameworkSlotTaskAI.Cast(
layer))
671 WatchAIGroup(
layer.GetSpawnedEntities());
672 else if (
layer &&
layer.GetName() != m_sQRFLayerName)
673 CheckForAIToWatch(child);
680 override array<ref SCR_ScenarioFrameworkActionBase> GetSubActions()
ArmaReforgerScripted GetGame()
void SCR_AIGroup(IEntitySource src, IEntity parent)
enum EAITargetInfoCategory m_Entity
SCR_CharacterControllerComponent GetCharacterController()
ref SCR_SortedArray< SCR_BaseEditorAction > m_aActions
override void OnActivate()
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
override SCR_EditableEntityComponent GetAIGroup()
Configs ServerBrowser KickDialogs conf
void SCR_QRFWaypointConfig(ResourceName wpPrefab, SCR_EQRFGroupOrderType groupType=SCR_EQRFGroupOrderType.ANY, int distanceOffset=0, vector wpPosition=vector.Zero)
SCR_EQRFGroupOrderType
Extension to allow for comparing orders with type of a group to which it should apply with option to ...
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum SCR_EQRFThresholdType BaseContainerProps()
SCR_ScenarioFrameworkEActivationType
void SCR_ScenarioFrameworkLayerBase(IEntityComponentSource src, IEntity ent, IEntity parent)
enum EVehicleType IEntity
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
proto external IEntity GetChildren()
proto external IEntity GetSibling()
ScriptInvoker GetOnAgentRemoved()
ScriptInvokerBase< ScriptInvokerAIGroup > GetOnAllDelayedEntitySpawned()
IEntity GetGadgetByType(EGadgetType type)
void PlaySoundOnEntityPosition(IEntity object, string soundFile, string soundEventName)
enum EPhysicsLayerPresets Vehicle
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute