9 class SCR_ScenarioFrameworkActionQRFDispacher : SCR_ScenarioFrameworkActionBase
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 protected ref array<SCR_ScenarioFrameworkQRFSlotAI> m_aQRFSpawnPoints = {};
53 protected int m_iNumberOfSoldiersInTheArea;
54 protected int m_iRemovedSoldier;
55 protected float m_fNextWaveDelayClock;
56 protected float m_fThreatLevel = 1;
57 protected int m_iSpawnTickets;
58 protected bool m_bWaitingForDelayedSpawn;
59 protected bool m_bWaitingForNextWave;
60 protected vector m_vTargetPosition;
61 protected ref array<ref SCR_QRFVehicleSpawnConfig> m_aVehicleSpawnQueueConfig = {};
66 protected bool WatchAIGroup(IEntity entities)
68 return WatchAIGroup({entities});
74 protected bool WatchAIGroup(array<IEntity> entities)
81 foreach (IEntity entity : entities)
87 aiGroup.GetOnAgentRemoved().Remove(OnGroupCompositionChanged);
88 aiGroup.GetOnAgentRemoved().Insert(OnGroupCompositionChanged);
89 aiGroup.GetOnAllDelayedEntitySpawned().Remove(OnDelayedGroupMembersSpawned);
90 aiGroup.GetOnAllDelayedEntitySpawned().Insert(OnDelayedGroupMembersSpawned);
91 if (aiGroup.GetAgentsCount() == 0)
92 m_iNumberOfSoldiersInTheArea++;
94 m_iNumberOfSoldiersInTheArea += aiGroup.GetAgentsCount();
113 AIAgent agent = AIAgent.Cast(entity);
117 if (!SCR_ChimeraCharacter.Cast(entity))
120 AIControlComponent control = AIControlComponent.Cast(entity.FindComponent(AIControlComponent));
124 agent = control.GetControlAIAgent();
132 protected void OnDelayedGroupMembersSpawned(
SCR_AIGroup group)
134 m_iNumberOfSoldiersInTheArea++;
138 protected void OnGroupCompositionChanged(
SCR_AIGroup group, AIAgent agent)
143 if (!agent.GetControlledEntity())
146 SCR_ChimeraCharacter
char = SCR_ChimeraCharacter.Cast(agent.GetControlledEntity());
154 vector tmpVec = agent.GetControlledEntity().GetOrigin();
155 if (m_vTargetPosition == vector.Zero)
156 m_vTargetPosition = tmpVec;
158 m_vTargetPosition = vector.Lerp(m_vTargetPosition, tmpVec, 0.95);
161 ThresholdValidation(
char)
165 protected void ThresholdValidation(IEntity killedEntity =
null)
167 bool startQRFProcedure;
168 switch (m_eThresholdType)
172 if (m_iRemovedSoldier >= m_iThreshold)
173 startQRFProcedure =
true;
179 if (m_iNumberOfSoldiersInTheArea - m_iRemovedSoldier <= m_iThreshold)
180 startQRFProcedure =
true;
186 if (m_iNumberOfSoldiersInTheArea > 0 && (m_iNumberOfSoldiersInTheArea - m_iRemovedSoldier) * 100 / m_iNumberOfSoldiersInTheArea <= m_iThreshold)
187 startQRFProcedure =
true;
192 if (!startQRFProcedure)
195 if (m_bWaitingForDelayedSpawn)
197 if (!m_bWaitingForNextWave && m_iNumberOfAvailableQRFWaves - 1 > 0 && m_fQRFSpawnDelay > 0)
199 float callTime = m_fNextWaveDelayClock -
GetGame().GetWorld().GetWorldTime();
203 m_bWaitingForNextWave =
true;
204 GetGame().GetCallqueue().CallLater(StartQRFProcedure, callTime, param1: killedEntity);
209 if (m_bWaitingForNextWave)
212 StartQRFProcedure(killedEntity);
216 protected void ThresholdHandling()
218 switch (m_eThresholdType)
222 m_iRemovedSoldier = 0;
227 m_iNumberOfSoldiersInTheArea -= m_iRemovedSoldier;
233 protected void StartQRFProcedure(IEntity killedEntity)
235 if (m_iNumberOfAvailableQRFWaves == 0)
238 float time =
GetGame().GetWorld().GetWorldTime();
239 if (m_fNextWaveDelayClock > time && !m_bWaitingForDelayedSpawn && !m_bWaitingForNextWave)
242 if (m_fQRFSpawnDelay > 0)
244 if (!m_bWaitingForDelayedSpawn)
246 if (m_bWaitingForNextWave)
247 m_bWaitingForNextWave =
false;
250 m_fNextWaveDelayClock = time + m_fQRFNextWaveDelay * 1000;
251 m_bWaitingForDelayedSpawn =
true;
252 GetGame().GetCallqueue().CallLater(StartQRFProcedure, m_fQRFSpawnDelay * 1000, param1: killedEntity);
254 DoPlaySoundOnEntityPosition(killedEntity, m_sSoundProjectFile, m_sQRFRequestedSoundEventName);
260 m_bWaitingForDelayedSpawn =
false;
266 m_fNextWaveDelayClock = time + m_fQRFNextWaveDelay * 1000;
270 DoPlaySoundOnEntityPosition(killedEntity, m_sSoundProjectFile, m_sQRFSentSoundEventName);
272 m_iNumberOfSoldiersInTheArea -= m_iRemovedSoldier;
273 m_iSpawnTickets += m_fThreatLevel;
274 m_fThreatLevel += m_fThreatLevelEscalation;
275 if (m_aQRFSpawnPoints.IsEmpty())
278 while (m_iSpawnTickets > 0)
280 SCR_QRFGroupConfig selectedGroup = SelectRandomGroup(m_iSpawnTickets);
287 m_iSpawnTickets -= selectedGroup.GetSpawnCost();
288 SCR_ScenarioFrameworkQRFSlotAI selectedSpawnPoint = SelectRandomSpawnpoint(m_aQRFSpawnPoints, selectedGroup.GetGroupType());
289 if (!selectedSpawnPoint)
295 if (selectedGroup.GetNumberOfAvailableGroups() > 0)
296 selectedGroup.SetNumberOfAvailableGroups(selectedGroup.GetNumberOfAvailableGroups() - 1);
298 selectedSpawnPoint.SetObjectToSpawn(selectedGroup.GetGroupPrefabName());
299 selectedSpawnPoint.SetEnableRepeatedSpawn(
true);
300 selectedSpawnPoint.SetIsTerminated(
false);
301 if (selectedSpawnPoint.GetNumberOfExistingWaypoints())
302 selectedSpawnPoint.ClearWaypoints();
304 selectedSpawnPoint.Init(m_AreaFramework, SCR_ScenarioFrameworkEActivationType.SAME_AS_PARENT);
305 IEntity spawnedEntity = selectedSpawnPoint.GetSpawnedEntity();
309 if (WatchAIGroup(aiGroup))
317 if (wp.GetDistanceOffsetToTargetLocation())
318 wpPosition =
SCR_Math3D.MoveTowards(m_vTargetPosition, selectedSpawnPoint.GetOwner().GetOrigin(), wp.GetDistanceOffsetToTargetLocation());
320 wpPosition = m_vTargetPosition;
322 if (
float.AlmostEqual(vector.DistanceXZ(wpPosition, selectedSpawnPoint.GetOwner().GetOrigin()), 0, 1))
325 wpPosition[1] =
GetGame().GetWorld().GetSurfaceY(wpPosition[0], wpPosition[2]);
326 AIWaypoint aiWP = selectedSpawnPoint.CreateWaypoint(wpPosition, wp.GetWaypointPrefabName());
328 aiGroup.AddWaypoint(aiWP);
334 if (Vehicle.Cast(spawnedEntity))
336 SCR_BaseCompartmentManagerComponent compartmentManager = SCR_BaseCompartmentManagerComponent.Cast(spawnedEntity.FindComponent(SCR_BaseCompartmentManagerComponent));
337 if (compartmentManager)
339 compartmentManager.GetOnDoneSpawningDefaultOccupants().Insert(OnFinishedSpawningVehicleOccupants);
344 m_aVehicleSpawnQueueConfig.Insert(
new SCR_QRFVehicleSpawnConfig(compartmentManager, selectedGroup.GetGroupType(), m_vTargetPosition, selectedSpawnPoint));
345 compartmentManager.SpawnDefaultOccupants(compartmentTypes);
350 if (m_iNumberOfAvailableQRFWaves > 0)
351 m_iNumberOfAvailableQRFWaves--;
353 m_vTargetPosition = vector.Zero;
357 protected SCR_QRFGroupConfig SelectRandomGroup(
int maxCost)
360 bool availableGroupAtFullPrice;
361 array<SCR_QRFGroupConfig> verifiedList = {};
362 foreach (SCR_QRFGroupConfig group : m_aGroupList)
367 if (group.GetNumberOfAvailableGroups() == -1 || group.GetNumberOfAvailableGroups() > 0)
369 if (group.GetSpawnCost() == maxCost)
371 if (!availableGroupAtFullPrice)
373 availableGroupAtFullPrice =
true;
374 verifiedList.Clear();
376 verifiedList.Insert(group);
378 else if (group.GetSpawnCost() < maxCost && !availableGroupAtFullPrice)
380 verifiedList.Insert(group);
385 if (availableGroupAtFullPrice)
386 return verifiedList.GetRandomElement();
388 SCR_QRFGroupConfig returnedGroup;
389 if (!verifiedList.IsEmpty())
391 int i, numOfTries, count = verifiedList.Count();
392 while (numOfTries < 10)
394 i = Math.RandomInt(0, count);
395 if (!returnedGroup || returnedGroup && returnedGroup.GetSpawnCost() < verifiedList[i].GetSpawnCost())
396 returnedGroup = verifiedList[i];
402 return returnedGroup;
406 protected SCR_ScenarioFrameworkQRFSlotAI SelectRandomSpawnpoint(array<SCR_ScenarioFrameworkQRFSlotAI> aListOfSpawnPoints,
SCR_EQRFGroupType searchedType)
408 float maxSpawnDistance = GetMaxDistanceForUnitType(searchedType);
409 array<SCR_ScenarioFrameworkQRFSlotAI> verifiedList = {};
410 foreach (SCR_ScenarioFrameworkQRFSlotAI spawn : aListOfSpawnPoints)
412 if (spawn.GetGroupType() ~& searchedType)
415 if (!CheckSpawnPointSafeZones(spawn.GetOwner().GetOrigin(), spawn.GetSpawnSafeZones(), searchedType))
418 if (maxSpawnDistance > -1 && vector.Distance(m_vTargetPosition, spawn.GetOwner().GetOrigin()) > maxSpawnDistance)
421 verifiedList.Insert(spawn);
425 if (verifiedList.Count() > 0)
426 return verifiedList.GetRandomElement();
428 float distanceToTarget, cloasestPosDistance =
float.MAX;
429 foreach (
int i, SCR_ScenarioFrameworkQRFSlotAI spawn : aListOfSpawnPoints)
431 if (spawn.GetGroupType() ~& searchedType)
434 if (!CheckSpawnPointSafeZones(spawn.GetOwner().GetOrigin(), spawn.GetSpawnSafeZones(), searchedType))
437 distanceToTarget = vector.Distance(m_vTargetPosition, spawn.GetOwner().GetOrigin());
438 if (distanceToTarget < cloasestPosDistance)
440 cloasestPosDistance = distanceToTarget;
448 return aListOfSpawnPoints[
index];
453 protected bool CheckSpawnPointSafeZones(vector spawnPointPosition, array<ref SCR_QRFSpawnSafeZone> spawnSafeZones,
SCR_EQRFGroupType searchedType)
455 if (spawnSafeZones.IsEmpty())
458 array<vector> aObserversPositions = {};
459 array<int> playerIds = {};
462 SCR_DamageManagerComponent damageManager;
463 playerManager.GetPlayers(playerIds);
465 foreach (
int playerId : playerIds)
467 player = playerManager.GetPlayerControlledEntity(playerId);
471 damageManager = SCR_DamageManagerComponent.GetDamageManager(player);
472 if (damageManager && damageManager.GetState() !=
EDamageState.DESTROYED)
473 aObserversPositions.Insert(player.GetOrigin());
478 if (safeZone.GetGroupType() != searchedType)
481 foreach (vector observerPos : aObserversPositions)
483 if (vector.Distance(observerPos, spawnPointPosition) < safeZone.GetMinDistanceToClosestObserver())
496 if (
conf.GetGroupType() == unitType)
497 return conf.GetMaxSpawnDistance();
504 protected void OnFinishedSpawningVehicleOccupants(SCR_BaseCompartmentManagerComponent compartmentManager, array<IEntity> occupants,
bool wasCanceled)
506 compartmentManager.GetOnDoneSpawningDefaultOccupants().Remove(OnFinishedSpawningVehicleOccupants);
510 if (occupants.IsEmpty())
513 SCR_ChimeraCharacter occupant = SCR_ChimeraCharacter.Cast(occupants[0]);
517 AIControlComponent control = AIControlComponent.Cast(occupant.FindComponent(AIControlComponent));
521 AIAgent agent = control.GetControlAIAgent();
529 if (!WatchAIGroup(aiGroup))
533 for (
int i, count = m_aVehicleSpawnQueueConfig.Count(); i < count; i++)
535 if (m_aVehicleSpawnQueueConfig[i].m_VehicleCompartmentMGR != compartmentManager)
540 if (wp.GetOrderType() !=
SCR_EQRFGroupOrderType.ANY && wp.GetOrderType() != m_aVehicleSpawnQueueConfig[i].m_eGroupType)
543 if (wp.GetDistanceOffsetToTargetLocation())
544 wpPosition =
SCR_Math3D.MoveTowards(m_aVehicleSpawnQueueConfig[i].m_vTargetPosition, occupant.GetOrigin(), wp.GetDistanceOffsetToTargetLocation());
546 wpPosition = m_aVehicleSpawnQueueConfig[i].m_vTargetPosition;
548 if (
float.AlmostEqual(vector.DistanceXZ(wpPosition, occupant.GetOrigin()), 0, 1))
551 AIWaypoint aiWP = m_aVehicleSpawnQueueConfig[i].m_Slot.CreateWaypoint(wpPosition, wp.GetWaypointPrefabName());
553 aiGroup.AddWaypoint(aiWP);
555 m_aVehicleSpawnQueueConfig.Remove(i);
562 protected void DoPlaySoundOnEntityPosition(IEntity entity,
string soundFileName,
string soundEventName)
573 SCR_GameModeSFManager sfManager = SCR_GameModeSFManager.Cast(
GetGame().
GetGameMode().FindComponent(SCR_GameModeSFManager));
581 if (!gadgetManager.GetGadgetByType(EGadgetType.RADIO))
584 sfManager.PlaySoundOnEntityPosition(entity, soundFileName, soundEventName);
588 override void Init(IEntity entity)
594 m_AreaFramework = thisLayer.GetParentArea();
595 if (!m_AreaFramework)
607 array<SCR_ScenarioFrameworkLayerBase> childLayers = m_AreaFramework.GetChildrenEntities();
608 if (childLayers.IsEmpty())
610 IEntity child = m_AreaFramework.GetOwner().GetChildren();
616 childLayers.Insert(layer);
618 child = child.GetSibling();
620 if (childLayers.IsEmpty())
624 m_iNumberOfSoldiersInTheArea = 0;
630 if (layer.GetName() == m_sQRFLayerName)
633 IEntity child = layer.GetOwner().GetChildren();
634 SCR_ScenarioFrameworkQRFSlotAI qrfSlot;
637 qrfSlot = SCR_ScenarioFrameworkQRFSlotAI.Cast(child.FindComponent(SCR_ScenarioFrameworkQRFSlotAI));
638 if (qrfSlot && !m_aQRFSpawnPoints.Contains(qrfSlot))
639 m_aQRFSpawnPoints.Insert(qrfSlot);
641 child = child.GetSibling();
647 CheckForAIToWatch(m_AreaFramework.GetOwner());
652 protected void CheckForAIToWatch(IEntity entity)
654 IEntity child = entity.GetChildren();
660 WatchAIGroup(layer.GetSpawnedEntities());
661 else if (layer && layer.GetName() != m_sQRFLayerName)
662 CheckForAIToWatch(child);
664 child = child.GetSibling();