1 [
EntityEditorProps(
category:
"GameScripted/ScriptWizard", description:
"Component to be used with barrack compositions, handing unit spawning.", color:
"0 0 255 255")]
4 [
Attribute(defvalue:
"{93291E72AC23930F}Prefabs/AI/Waypoints/AIWaypoint_Defend.et", UIWidgets.ResourceNamePicker,
desc:
"Default waypoint prefab",
"et",
category:
"Defender Spawner")]
5 protected ResourceName m_sDefaultWaypointPrefab;
8 protected float m_fDefaultWaypointCompletionRadius;
10 [
Attribute(defvalue :
"Wedge", UIWidgets.Auto,
desc:
"Group formation created on rally point. Available formations are found on SCR_AIWorld entity",
category:
"Defender Spawner")]
11 protected string m_sExitFormation;
15 ResourceName GetDefaultWaypointPrefab()
22 float GetDefaultWaypointCompletionRadius()
29 string GetExitFormation()
43 protected ref array<EEditableEntityLabel> m_aAllowedLabels;
46 protected ref array<EEditableEntityLabel> m_aIgnoredLabels;
48 [
Attribute(defvalue:
"300", uiwidget: UIWidgets.Auto,
category:
"Defender Spawner",
desc:
"Time in seconds between unit respawns")]
49 protected float m_fRespawnDelay;
52 protected bool m_bEnableSpawning;
54 [
Attribute(defvalue:
"30",
params:
"0 inf",
desc:
"Minimum distance to hostile players for spawner to work",
"et")]
55 protected float m_fMinHostilePlayerDistance;
58 protected bool m_bSuppliesConsumptionEnabled;
61 protected int m_iCustomSupplies;
66 protected RplComponent m_RplComponent;
68 protected SCR_ResourceComponent m_ResourceComponent;
69 protected SCR_SpawnerAIGroupManagerComponent m_GroupSpawningManager;
70 protected WorldTimestamp m_fNextRespawnTime;
73 protected AIWaypoint m_Waypoint;
74 protected int m_iDespawnedGroupMembers;
75 protected ref array<ref Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp>> m_aUnitsOnMove = {};
76 protected ref array<ResourceName> m_aRefillQueue;
77 protected ref array<SCR_AIGroup> m_aPreviousGroups;
78 protected ref array<IEntity> m_aOldUnits;
82 protected static const int SPAWN_CHECK_INTERVAL = 1000;
83 protected static const int SPAWN_GROUP_JOIN_TIMEOUT = 20;
84 protected static const int SPAWN_RADIUS_MIN = Math.Pow(500, 2);
85 protected static const int SPAWN_RADIUS_MAX = Math.Pow(1000, 2);
91 return m_eMinimumRankForEnabling;
98 bool UserRankCheck(IEntity user)
106 return rank >= m_eMinimumRankForEnabling;
113 if (!m_OnDefenderGroupSpawned)
116 return m_OnDefenderGroupSpawned;
130 protected void AssignDefenderGroupDataFromOwningFaction()
139 Print(
"SCR_Faction Config lacks catalog of GROUP type. SCR_DefenderSpawnerComponent won't work without it", LogLevel.ERROR);
143 array<SCR_EntityCatalogEntry> entityEntries = {};
144 array<typename> includedDataClasses = {};
150 if (!entityEntries.IsEmpty())
151 m_GroupEntry = entityEntries[0];
159 m_SupplyComponent = supplyComp;
162 m_fNextRespawnTime =
null;
169 return m_SupplyComponent;
174 float GetSpawnerSupplies()
185 GetGame().GetResourceGrid().UpdateInteractor(consumer);
187 return consumer.GetAggregatedResourceValue();
193 void AddSupplies(
float value)
197 SCR_ResourceInteractor interactor;
206 interactor = generator;
208 generator.RequestGeneration(value);
217 interactor = consumer;
219 consumer.RequestConsumtion(-value);
227 Replication.BumpMe();
232 bool IsSpawningEnabled()
234 return m_bEnableSpawning;
241 void EnableSpawning(
bool enable,
int playerID)
244 if (!playerController)
247 SCR_ChimeraCharacter chimeraCharacter = SCR_ChimeraCharacter.Cast(playerController.GetControlledEntity());
248 if (!chimeraCharacter || chimeraCharacter.GetFaction() !=
GetFaction())
251 if (!UserRankCheck(chimeraCharacter))
254 m_bEnableSpawning = enable;
255 Replication.BumpMe();
269 protected void SpawnUnit(ResourceName unitResource,
bool consumeSupplies =
true)
274 if (!m_GroupEntry || !m_AIgroup || !m_AIgroup.m_aUnitPrefabSlots || m_AIgroup.m_aUnitPrefabSlots.IsEmpty())
277 AIWorld aiWorld =
GetGame().GetAIWorld();
285 SCR_EntitySpawnerSlotComponent slot = GetFreeSlot();
291 if (m_AIgroup.m_aUnitPrefabSlots.Count() > 0)
292 spawnCost = entityData.GetSupplyCost() / m_AIgroup.m_aUnitPrefabSlots.Count();
294 EntitySpawnParams spawnParams =
new EntitySpawnParams();
295 spawnParams.TransformMode = ETransformMode.WORLD;
296 slot.GetOwner().GetTransform(spawnParams.Transform);
297 Resource res = Resource.Load(unitResource);
299 IEntity ai =
GetGame().SpawnEntityPrefab(res,
null, spawnParams);
304 m_aRefillQueue.RemoveItem(unitResource);
308 AddSupplies(-spawnCost);
310 AIControlComponent control = AIControlComponent.Cast(ai.FindComponent(AIControlComponent));
312 control.ActivateAI();
314 AIAgent agent = control.GetAIAgent();
324 SCR_EntityLabelPointComponent rallyPointComponent = slot.GetRallyPoint();
325 if (rallyPointComponent && prefabData)
327 IEntity rallyPointEntity = rallyPointComponent.GetOwner();
330 SCR_AIUtilityComponent utilityComponent = SCR_AIUtilityComponent.Cast(agent.FindComponent(SCR_AIUtilityComponent));
332 vector rpTransform[4];
333 rallyPointEntity.GetWorldTransform(rpTransform);
335 AIFormationDefinition formationDefiniton = aiWorld.GetFormation(prefabData.GetExitFormation());
338 if (m_aUnitsOnMove && formationDefiniton)
340 vector formationOffset = 0.5 * formationDefiniton.GetOffsetPosition(m_aUnitsOnMove.Count());
341 posLocal = posLocal + formationOffset;
344 vector posWorld = posLocal.Multiply4(rpTransform);
349 m_AIgroup.AddAgent(agent);
353 utilityComponent.AddAction(moveBehavior);
360 m_aUnitsOnMove.Insert(
new Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp>(moveBehavior, agent, world.GetServerTimestamp()));
362 moveBehavior.m_OnActionCompleted.Insert(OnMoveFinished);
368 charController.m_OnPlayerDeathWithParam.Insert(OnMovingCharacterDeath);
373 m_AIgroup.AddAgent(agent);
380 if (!characterController)
383 IEntity character = characterController.GetOwner();
387 foreach (
int index, Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp> AIaction : m_aUnitsOnMove)
389 if (AIaction.param2.GetControlledEntity() != character)
392 m_aUnitsOnMove.Remove(
index);
396 if (m_aUnitsOnMove.IsEmpty())
397 m_aUnitsOnMove =
null;
409 foreach (
int i, Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp> AIaction : m_aUnitsOnMove)
411 if (AIaction.param1 != action)
414 agent = AIaction.param2;
419 SCR_ChimeraCharacter agentEntity = SCR_ChimeraCharacter.Cast(agent.GetControlledEntity());
425 charController.m_OnPlayerDeathWithParam.Remove(OnMovingCharacterDeath);
427 if (!agent || !m_AIgroup)
432 m_aOldUnits.Insert(agentEntity);
437 if (agentEntity.GetFaction() == m_AIgroup.GetFaction())
438 m_AIgroup.AddAgent(agent);
440 m_aUnitsOnMove.Remove(groupIndex);
441 if (m_aUnitsOnMove.IsEmpty())
442 m_aUnitsOnMove =
null;
444 if ((m_AIgroup.GetAgentsCount() == m_AIgroup.m_aUnitPrefabSlots.Count()) || (!m_aUnitsOnMove))
445 m_AIgroup.AddWaypoint(GetWaypoint());
450 SCR_EntitySpawnerSlotComponent GetFreeSlot()
452 foreach (SCR_EntitySpawnerSlotComponent slot :
m_aChildSlots)
454 if (!slot.IsOccupied())
458 foreach (SCR_EntitySpawnerSlotComponent slot :
m_aNearSlots)
460 if (!slot.IsOccupied())
469 protected void ReinforceGroup()
471 array<ResourceName> unitPrefabs = {};
472 unitPrefabs.Copy(m_AIgroup.m_aUnitPrefabSlots);
474 array<AIAgent> outAgents = {};
475 m_AIgroup.GetAgents(outAgents);
477 foreach (AIAgent agent : outAgents)
479 IEntity controlledEntity = agent.GetControlledEntity();
480 if (!controlledEntity)
483 EntityPrefabData prefabData = controlledEntity.GetPrefabData();
487 ResourceName aiResource = prefabData.GetPrefabName();
488 unitPrefabs.RemoveItem(aiResource);
494 foreach (Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp> groups : m_aUnitsOnMove)
497 ent = groups.param2.GetControlledEntity();
502 EntityPrefabData prefabData = ent.GetPrefabData();
506 unitPrefabs.RemoveItem(prefabData.GetPrefabName());
510 m_aRefillQueue = unitPrefabs;
516 protected void SpawnGroup()
528 float supplies = GetSpawnerSupplies();
529 int spawnCost = entityData.GetSupplyCost();
533 SCR_EntitySpawnerSlotComponent slot = GetFreeSlot();
537 EntitySpawnParams
params =
new EntitySpawnParams();
538 params.TransformMode = ETransformMode.WORLD;
539 slot.GetOwner().GetTransform(
params.Transform);
541 Resource res = Resource.Load(m_GroupEntry.GetPrefab());
547 if (!group || group.m_aUnitPrefabSlots.Count() == 0)
555 if (m_OnDefenderGroupSpawned)
556 m_OnDefenderGroupSpawned.Invoke(
this, group);
562 protected AIWaypoint GetWaypoint()
567 SCR_EntitySpawnerSlotComponent slot = GetFreeSlot();
571 EntitySpawnParams
params =
new EntitySpawnParams();
572 params.TransformMode = ETransformMode.WORLD;
574 SCR_EntityLabelPointComponent rallyPoint = slot.GetRallyPoint();
577 rallyPoint.GetOwner().GetTransform(
params.Transform);
579 slot.GetOwner().GetTransform(
params.Transform);
585 Resource wpRes = Resource.Load(prefabData.GetDefaultWaypointPrefab());
586 if (!wpRes.IsValid())
593 m_Waypoint.SetCompletionRadius(prefabData.GetDefaultWaypointCompletionRadius());
600 protected SCR_EDefenderSpawnerState GetPlayerDistanceState()
602 array<int> players = {};
603 GetGame().GetPlayerManager().GetPlayers(players);
605 float spawnDistanceSq = Math.Clamp(Math.Pow(
GetGame().GetViewDistance() * 0.5, 2), SPAWN_RADIUS_MAX, SPAWN_RADIUS_MIN);
606 float despawnDistanceSq = Math.Clamp(Math.Pow(
GetGame().GetViewDistance() * 0.5, 2), SPAWN_RADIUS_MIN, SPAWN_RADIUS_MAX);
607 float minSpawnDistanceSq = m_fMinHostilePlayerDistance * m_fMinHostilePlayerDistance;
609 SCR_ChimeraCharacter playerEntity;
611 vector origin =
GetOwner().GetOrigin();
613 SCR_EDefenderSpawnerState outState = SCR_EDefenderSpawnerState.DEFENDERS_DESPAWN;
615 foreach (
int player : players)
617 playerEntity = SCR_ChimeraCharacter.Cast(
GetGame().GetPlayerManager().GetPlayerControlledEntity(player));
621 dist = vector.DistanceSq(playerEntity.GetOrigin(), origin);
623 if (outState == SCR_EDefenderSpawnerState.DEFENDERS_DESPAWN)
625 if (dist < spawnDistanceSq)
627 outState = SCR_EDefenderSpawnerState.DEFENDERS_ENABLED;
631 outState = SCR_EDefenderSpawnerState.DEFENDERS_DESPAWN;
636 if (dist < minSpawnDistanceSq && playerEntity.GetFaction() !=
GetFaction())
638 outState = SCR_EDefenderSpawnerState.DEFENDERS_PAUSED_SPAWN;
648 protected void HandleGroup()
651 WorldTimestamp replicationTime = world.GetServerTimestamp();
653 AIWorld aiWorld =
GetGame().GetAIWorld();
658 if ((aiWorld.GetCurrentAmountOfLimitedAIs() + 1) >= aiWorld.GetAILimit())
660 if (m_bEnableSpawning)
662 m_bEnableSpawning =
false;
663 Replication.BumpMe();
665 m_GroupSpawningManager.SetIsAtAILimit(
true);
671 SCR_EDefenderSpawnerState distanceState = GetPlayerDistanceState();
673 if (m_bEnableSpawning && (distanceState == SCR_EDefenderSpawnerState.DEFENDERS_ENABLED))
678 int count = m_aUnitsOnMove.Count()-1;
679 for (
int i = count; i >= 0; i--)
681 if (!m_aUnitsOnMove[i].param2)
683 m_aUnitsOnMove.Remove(i);
687 if (!m_aUnitsOnMove[i].param3.PlusSeconds(SPAWN_GROUP_JOIN_TIMEOUT).GreaterEqual(replicationTime))
688 OnMoveFinished(m_aUnitsOnMove[i].param1);
693 if (m_iDespawnedGroupMembers > 0 || replicationTime.Greater(m_fNextRespawnTime))
695 m_fNextRespawnTime = replicationTime.PlusSeconds(m_fRespawnDelay);
696 if (m_AIgroup && (m_AIgroup.m_aUnitPrefabSlots.Count() != m_AIgroup.GetAgentsCount()))
704 if (m_aRefillQueue && !m_aRefillQueue.IsEmpty())
706 if (m_iDespawnedGroupMembers > 0)
708 SpawnUnit(m_aRefillQueue[0],
false);
709 m_iDespawnedGroupMembers--;
713 SpawnUnit(m_aRefillQueue[0]);
719 if (distanceState == SCR_EDefenderSpawnerState.DEFENDERS_DESPAWN)
723 m_iDespawnedGroupMembers = m_AIgroup.GetAgentsCount();
728 if (m_aPreviousGroups)
735 m_aPreviousGroups =
null;
741 int index = m_aOldUnits.Count()-1;
753 foreach (Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp> groupWaypoint : m_aUnitsOnMove)
755 SCR_EntityHelper.DeleteEntityAndChildren(groupWaypoint.param2.GetControlledEntity());
756 m_iDespawnedGroupMembers++;
759 m_aUnitsOnMove =
null;
765 protected override void OnFactionChanged(FactionAffiliationComponent owner,
Faction previousFaction,
Faction faction)
770 if (!m_aPreviousGroups)
771 m_aPreviousGroups = {};
773 m_aPreviousGroups.Insert(m_AIgroup);
778 AssignDefenderGroupDataFromOwningFaction();
782 protected override void EOnInit(IEntity owner)
787 super.EOnInit(owner);
794 BaseGameMode gameMode =
GetGame().GetGameMode();
798 m_GroupSpawningManager = SCR_SpawnerAIGroupManagerComponent.Cast(gameMode.FindComponent(SCR_SpawnerAIGroupManagerComponent));
799 if (!m_GroupSpawningManager)
801 Print(
"SCR_DefenderSpawnerComponent requires SCR_SpawnerAIGroupManagerComponent attached to gamemode to work properly!", LogLevel.ERROR);
809 GetGame().GetCallqueue().CallLater(HandleGroup, SPAWN_CHECK_INTERVAL,
true);
813 protected override void OnPostInit(IEntity owner)
818 super.OnPostInit(owner);
820 SetEventMask(owner, EntityEvent.INIT);
833 foreach (Tuple3<SCR_AIActionBase, AIAgent, WorldTimestamp> groupWaypoint : m_aUnitsOnMove)
835 if (!groupWaypoint.param2)
838 SCR_EntityHelper.DeleteEntityAndChildren(groupWaypoint.param2.GetControlledEntity());
843 if (m_aPreviousGroups)
845 int index = m_aPreviousGroups.Count()-1;
855 int index = m_aOldUnits.Count()-1;
862 GetGame().GetCallqueue().Remove(HandleGroup);
866 enum SCR_EDefenderSpawnerState