Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AIGroup.c
Go to the documentation of this file.
3 
4 [EntityEditorProps(category: "GameScripted/AI")]
6 {
14  static int GetMembers(IEntitySource entitySource, out array<ResourceName> outPrefabs, out array<vector> outOffsets)
15  {
16  //--- Not a group
17  if (!entitySource || !entitySource.GetClassName().ToType().IsInherited(SCR_AIGroup)) return false;
18 
19  ArmaReforgerScripted game = GetGame();
20  if (!game) return 0;
21 
22  AIWorld aiWorld = game.GetAIWorld();
23  if (!aiWorld) return 0;
24 
25  //--- Get formation
26  AIFormationDefinition formation;
27  IEntityComponentSource componentSource;
28  for (int i = 0, count = entitySource.GetComponentCount(); i < count; i++)
29  {
30  componentSource = entitySource.GetComponent(i);
31  if (componentSource.GetClassName().ToType().IsInherited(AIFormationComponent))
32  {
33  string formationName;
34  componentSource.Get("DefaultFormation", formationName);
35  formation = aiWorld.GetFormation(formationName);
36  break;
37  }
38  }
39  if (!formation) return 0;
40 
41  //--- Get member prefabs
42  entitySource.Get("m_aUnitPrefabSlots", outPrefabs);
43 
44  //--- Get offsets
45  outOffsets.Clear();
46  int count = outPrefabs.Count();
47  for (int i = 0; i < count; i++)
48  {
49  outOffsets.Insert(formation.GetOffsetPosition(i));
50  }
51 
52  return count;
53  }
54 };
55 
57 {
58  bool snapToTerrain;
59  int index;
60  ResourceName resourceName;
61  bool editMode;
62 };
63 
64 void ScriptInvoker_AIGroupOnEmpty_Callback(AIGroup group);
66 typedef ScriptInvokerBase<ScriptInvoker_AIGroupOnEmpty_Callback> ScriptInvoker_AIGroupOnEmpty;
67 
68 class SCR_AIGroup : ChimeraAIGroup
69 {
70  [Attribute("", UIWidgets.EditBox, "Faction", category: "Group")]
71  string m_faction;
72 
73  [Attribute(uiwidget: UIWidgets.ResourceAssignArray, desc: "Entities in group non-ai included", params: "et", category: "Group Members")]
74  ref array<ResourceName> m_aUnitPrefabSlots;
75 
76  [Attribute(defvalue: "1", desc: "When true, group members will be spawned above terrain, offset by group's ATL height.\nWhen false, group members will be levelled horizontally with the group.", category: "Group Members")]
77  private bool m_bSnapToTerrain;
78 
79  [Attribute("", UIWidgets.EditBox, "List of Waypoint names found in the level", category: "Group Waypoints")]
80  ref array<string> m_aStaticWaypoints;
81 
82  [Attribute(defvalue: "", UIWidgets.Object, desc: "Waypoints that should be spawned from prefabs", category: "Group Waypoints")]
83  ref array<ref SCR_WaypointPrefabLocation> m_aSpawnedWaypoints;
84 
85  [Attribute(defvalue: "", UIWidgets.EditBox, desc: "List of vehicles to use for movement", category: "Group Vehicles")]
86  ref array<string> m_aStaticVehicles;
87 
88  [Attribute(defvalue: "1", desc: "When true, group members will be spawned durin OnInit, if false spawning must be called manually calling SpawnUnits()", category: "Group Members")]
89  private bool m_bSpawnImmediately;
90 
91  [Attribute(defvalue: "0", UIWidgets.EditBox, desc: "Delay between spawns of individual members (ms)", category: "Group Members")]
92  protected int m_fMemberSpawnDelay;
93 
94  [Attribute(defvalue: "1", UIWidgets.EditBox, desc: "When enabled, the group will be deleted when its last member dies or is deleted.\nThis will *not* delete the group when it starts empty.", category: "Group")]
95  protected bool m_bDeleteWhenEmpty;
96 
97  protected static bool s_bIgnoreSnapToTerrain;
98  protected static bool s_bIgnoreSpawning;
99 
100  protected ref array<IEntity> m_aSceneGroupUnitInstances;
101  protected ref array<IEntity> m_aSceneWaypointInstances;
102  protected ref array<IEntity> m_aUsableVehicles;
103  protected ref array<BaseCompartmentSlot> m_aAllocatedCompartments;
104 
105  protected int m_iMaxUnitsToSpawn = int.MAX;
106  protected ref ScriptInvoker Event_OnInit = new ScriptInvoker;
107  protected ref ScriptInvoker_AIGroupOnEmpty Event_OnEmpty = new ScriptInvoker_AIGroupOnEmpty();
108  protected ref ScriptInvoker Event_OnAgentAdded = new ScriptInvoker;
109  protected ref ScriptInvoker Event_OnAgentRemoved = new ScriptInvoker;
110  protected ref ScriptInvoker Event_OnLeaderChanged = new ScriptInvoker;
111  protected ref ScriptInvoker Event_OnCurrentWaypointChanged = new ScriptInvoker;
112  protected ref ScriptInvoker Event_OnWaypointCompleted = new ScriptInvoker;
113  protected ref ScriptInvoker Event_OnWaypointAdded = new ScriptInvoker;
114  protected ref ScriptInvoker Event_OnWaypointRemoved = new ScriptInvoker;
115  protected ref ScriptInvoker Event_OnFactionChanged = new ScriptInvoker;
116 
117  protected ref ScriptInvoker m_OnGroupMemberStateChange = new ScriptInvoker();
118 
119  protected int m_iNumOfMembersToSpawn;
120 
121  protected ref array<int> m_aAgentIDQueue = {};
122 
123  //player groups variables
124  [Attribute(category: "Player settings")]
125  protected int m_iGroupRadioFrequency;
126 
127  [Attribute(category: "Player settings", params: "1 100 1")]
128  protected int m_iMaxMembers;
129 
130  [Attribute("0", desc: "Can players join this group?", category: "Player settings")]
131  protected bool m_bPlayable, m_bPrivate;
132 
133  [Attribute("1", desc: "Does this group get deleted when empty?", category: "Player settings")]
134  protected bool m_bDeleteIfNoPlayer;
135 
136  protected int m_iGroupID = -1;
137  protected int m_iLeaderID = -1;
138  protected ref SCR_AIGroupUIInfo m_UiInfo;
139 
140  //gamecode uses 0 as invalid playerID
141  protected int m_iDescriptionAuthorID = 0;
142  protected int m_iNameAuthorID = 0;
143 
144 
145  protected string m_sCustomName = "";
146  protected string m_sCustomDescription = "";
147  protected ref array<int> m_aPlayerIDs = {};
148  protected ref array<int> m_aDisconnectedPlayerIDs;
149  protected static ref ScriptInvoker s_OnPlayerAdded = new ScriptInvoker();
150  protected static ref ScriptInvoker s_OnPlayerRemoved = new ScriptInvoker();
151  protected static ref ScriptInvoker<int, int> s_OnPlayerLeaderChanged = new ScriptInvoker();
152  protected static ref ScriptInvoker s_OnPrivateGroupChanged = new ScriptInvoker();
153  protected static ref ScriptInvoker<SCR_AIGroup> s_OnCustomNameChanged = new ScriptInvoker();
154  protected static ref ScriptInvoker s_OnFrequencyChanged = new ScriptInvoker();
155  protected static ref ScriptInvoker s_OnMaxMembersChanged = new ScriptInvoker();
156  protected static ref ScriptInvoker s_OnCustomDescChanged = new ScriptInvoker();
157  protected static ref ScriptInvoker s_OnFlagSelected = new ScriptInvoker();
158  protected static ref ScriptInvoker s_OnJoinPrivateGroupRequest = new ScriptInvoker();
159  protected static ref ScriptInvoker s_OnJoinPrivateGroupConfirm = new ScriptInvoker();
160  protected static ref ScriptInvoker s_OnJoinPrivateGroupCancel = new ScriptInvoker();
161 
162  protected ref array<int> m_aRequesterIDs = {};
163  protected ref array<int> m_aDeniedRequesters = {};
164 
165  [RplProp()]
166  protected int m_iDeployedRadioCount = 0;
167 
168  //commanding variables
169  protected SCR_AIGroup m_SlaveGroup;
170  protected SCR_AIGroup m_MasterGroup;
171  protected ref array<SCR_ChimeraCharacter> m_aAIMembers = {};
172 
173  // entity spawn list
174  protected ref array<ref SCR_AIGroup_DelayedSpawn> m_delayedSpawnList = {};
175  protected ref ScriptInvokerBase<ScriptInvokerAIGroup> Event_OnAllDelayedEntitySpawned;
176 
177  //------------------------------------------------------------------------------------------------
178  int GetNumberOfMembersToSpawn()
179  {
180  return m_iNumOfMembersToSpawn;
181  }
182 
183  //------------------------------------------------------------------------------------------------
184  void SetNumberOfMembersToSpawn(int number)
185  {
186  m_iNumOfMembersToSpawn = number;
187  }
188 
189  //------------------------------------------------------------------------------------------------
190  protected override void EOnFrame(IEntity owner, float timeSlice)
191  {
192  // No more entities in the list? Turn off frame events
193  if (m_delayedSpawnList.IsEmpty())
194  {
195  EndDelayedSpawn();
196  return;
197  }
198 
199  // Spawn a singular AI entity this frame
200  int spawnIndex = m_delayedSpawnList.Count() - 1;
201  if (SpawnDelayedGroupMember(spawnIndex))
202  m_delayedSpawnList.Remove(spawnIndex);
203 
204  // Notify all delayed spawning is done
205  if (m_delayedSpawnList.IsEmpty() && Event_OnAllDelayedEntitySpawned)
206  Event_OnAllDelayedEntitySpawned.Invoke(this);
207  }
208 
209  //------------------------------------------------------------------------------------------------
210  /*
211  Returns false when member couldn't be spawned but we should try again
212  True otherwise
213  */
214  bool SpawnDelayedGroupMember(int spawnIndex)
215  {
216  return SpawnGroupMember(
217  m_delayedSpawnList.Get(spawnIndex).snapToTerrain,
218  m_delayedSpawnList.Get(spawnIndex).index,
219  m_delayedSpawnList.Get(spawnIndex).resourceName,
220  m_delayedSpawnList.Get(spawnIndex).editMode,
221  spawnIndex == 0 // isLast
222  );
223  }
224 
225  //------------------------------------------------------------------------------------------------
226  void SpawnAllImmediately()
227  {
228  for (int spawnIndex = m_delayedSpawnList.Count() - 1; spawnIndex >= 0; spawnIndex--)
229  SpawnDelayedGroupMember(spawnIndex);
230 
231  m_delayedSpawnList.Clear();
232  }
233 
234  //------------------------------------------------------------------------------------------------
235  void BeginDelayedSpawn()
236  {
237  SetEventMask(EntityEvent.FRAME);
238 
239  // Allow this entity to frame/tick while in Game Master Mode
240  ChimeraWorld world = GetGame().GetWorld();
241  if (world)
242  world.RegisterEntityToBeUpdatedWhileGameIsPaused(this);
243  }
244 
245  //------------------------------------------------------------------------------------------------
246  void EndDelayedSpawn()
247  {
248  ClearEventMask(EntityEvent.FRAME);
249 
250  ChimeraWorld world = GetGame().GetWorld();
251  if (world)
252  world.UnregisterEntityToBeUpdatedWhileGameIsPaused(this);
253  }
254 
255  //------------------------------------------------------------------------------------------------
256  bool HasRequesterID(int id)
257  {
258  return m_aRequesterIDs.Contains(id);
259  }
260 
261  //------------------------------------------------------------------------------------------------
262  void RemoveRequester(int playerID)
263  {
264  if (!m_aRequesterIDs.Contains(playerID))
265  return;
266 
267  RPC_DoRemoveRequester(playerID);
268  Rpc(RPC_DoRemoveRequester, playerID);
269  }
270 
271  //------------------------------------------------------------------------------------------------
272  int GetDeniedRequesters(out array<int> valueArray)
273  {
274  return valueArray.Copy(m_aDeniedRequesters);
275  }
276 
277  //------------------------------------------------------------------------------------------------
278  void AddDeniedRequester(int playerID)
279  {
280  if (m_aDeniedRequesters.Contains(playerID))
281  return;
282 
283  RPC_DoAddDeniedRequester(playerID);
284  Rpc(RPC_DoAddDeniedRequester, playerID);
285  }
286 
287  //------------------------------------------------------------------------------------------------
288  void ClearRequesters()
289  {
290  RPC_DoClearRequesterIDs();
291  Rpc(RPC_DoClearRequesterIDs);
292  }
293 
294  //------------------------------------------------------------------------------------------------
295  void ClearDeniedRequester()
296  {
297  RPC_DoClearDeniedRequester();
298  Rpc(RPC_DoClearDeniedRequester);
299  }
300 
301  //------------------------------------------------------------------------------------------------
302  int GetRequesterIDs(out array<int> valueArray)
303  {
304  return valueArray.Copy(m_aRequesterIDs);
305  }
306 
307  //------------------------------------------------------------------------------------------------
308  void AddRequester(int playerID)
309  {
310  if (m_aRequesterIDs.Contains(playerID))
311  return;
312 
313  RPC_DoAddRequester(playerID);
314  Rpc(RPC_DoAddRequester, playerID);
315  }
316 
317  //------------------------------------------------------------------------------------------------
318  void SetFlagIsFromImageSet(bool value)
319  {
320  if (!m_UiInfo)
321  m_UiInfo = new SCR_AIGroupUIInfo();
322 
323  m_UiInfo.SetFlagIsFromImageSet(value);
324  }
325 
326  //------------------------------------------------------------------------------------------------
327  void SetCustomGroupFlag(ResourceName flag)
328  {
329  if (!m_UiInfo)
330  m_UiInfo = new SCR_AIGroupUIInfo();
331 
332  m_UiInfo.SetGroupFlag(flag);
333  }
334 
335  //------------------------------------------------------------------------------------------------
336  bool GetFlagIsFromImageSet()
337  {
338  if (!m_UiInfo)
339  m_UiInfo = new SCR_AIGroupUIInfo();
340 
341  bool flagIsFromSet = m_UiInfo.GetFlagIsFromImageSet();
342  return flagIsFromSet;
343  }
344 
345  //------------------------------------------------------------------------------------------------
346  ResourceName GetGroupFlag()
347  {
348  if (!m_UiInfo)
349  m_UiInfo = new SCR_AIGroupUIInfo();
350 
351  ResourceName name = m_UiInfo.GetGroupFlag();
352  return name;
353  }
354 
355  //------------------------------------------------------------------------------------------------
356  static ScriptInvoker GetOnFlagSelected()
357  {
358  return s_OnFlagSelected;
359  }
360 
361  //------------------------------------------------------------------------------------------------
362  static ScriptInvoker GetOnJoinPrivateGroupRequest()
363  {
364  return s_OnJoinPrivateGroupRequest;
365  }
366 
367  //------------------------------------------------------------------------------------------------
368  static ScriptInvoker GetOnJoinPrivateGroupConfirm()
369  {
370  return s_OnJoinPrivateGroupConfirm;
371  }
372 
373  //------------------------------------------------------------------------------------------------
374  static ScriptInvoker GetOnJoinPrivateGroupCancel()
375  {
376  return s_OnJoinPrivateGroupCancel;
377  }
378 
379  //------------------------------------------------------------------------------------------------
380  bool IsPlayerInGroup(int playerID)
381  {
382  return m_aPlayerIDs.Contains(playerID);
383  }
384 
385  //------------------------------------------------------------------------------------------------
386  bool IsPlayerLeader(int playerID)
387  {
388  return playerID == m_iLeaderID;
389  }
390 
391  //------------------------------------------------------------------------------------------------
392  int GetLeaderID()
393  {
394  return m_iLeaderID;
395  }
396 
397  //------------------------------------------------------------------------------------------------
399  void SetPrivate(bool isPrivate)
400  {
401  RPC_SetPrivate(isPrivate);
402  Rpc(RPC_SetPrivate, isPrivate);
403  }
404 
405  //------------------------------------------------------------------------------------------------
406  bool IsPrivate()
407  {
408  return m_bPrivate;
409  }
410 
411  //------------------------------------------------------------------------------------------------
413  bool GetDeleteIfNoPlayer()
414  {
415  return m_bDeleteIfNoPlayer;
416  }
417 
418  //------------------------------------------------------------------------------------------------
420  void SetCanDeleteIfNoPlayer(bool deleteEmpty)
421  {
422  m_bDeleteIfNoPlayer = deleteEmpty;
423  }
424 
425  //------------------------------------------------------------------------------------------------
426  bool IsFull()
427  {
428  return m_iMaxMembers <= m_aPlayerIDs.Count();
429  }
430 
431  //------------------------------------------------------------------------------------------------
432  bool IsSlave()
433  {
434  if (!m_MasterGroup)
435  return false;
436  return true;
437  }
438 
439  //------------------------------------------------------------------------------------------------
445  int GetPlayerAndAgentCount(bool checkMasterAndSlaves = false)
446  {
447  if (!checkMasterAndSlaves)
448  return GetPlayerCount() + GetAgentsCount();
449 
450  //~ Return count of group and master and slave groups
451  return GetPlayerCount(true) + GetAgentCountIncludingMasterAndSlaves();
452  }
453 
454  //------------------------------------------------------------------------------------------------
458  int GetAgentCountIncludingMasterAndSlaves()
459  {
460  int totalAgentCount = GetAgentsCount();
461 
462  SCR_AIGroup aiGroup = GetSlave();
463  if (aiGroup)
464  totalAgentCount += aiGroup.GetAgentsCount();
465 
466  aiGroup = GetMaster();
467  if (aiGroup)
468  totalAgentCount += aiGroup.GetAgentsCount();
469 
470  return totalAgentCount;
471  }
472 
473  //------------------------------------------------------------------------------------------------
478  int GetTotalAgentCount()
479  {
480  int totalGroupCount = GetAgentsCount();
481 
482  SCR_AIGroup aiGroup = GetSlave();
483  if (aiGroup)
484  totalGroupCount += aiGroup.GetAgentsCount();
485 
486  aiGroup = GetMaster();
487  if (aiGroup)
488  totalGroupCount += aiGroup.GetAgentsCount();
489 
490  return totalGroupCount;
491  }
492 
493  //------------------------------------------------------------------------------------------------
498  int GetTotalPlayerCount()
499  {
500  int totalGroupCount = GetPlayerCount();
501 
502  SCR_AIGroup aiGroup = GetSlave();
503  if (aiGroup)
504  totalGroupCount += aiGroup.GetPlayerCount();
505 
506  aiGroup = GetMaster();
507  if (aiGroup)
508  totalGroupCount += aiGroup.GetPlayerCount();
509 
510  return totalGroupCount;
511  }
512 
513  //------------------------------------------------------------------------------------------------
515  void SetGroupFlag(int flagIndex, bool isFromImageset)
516  {
517  if(!m_UiInfo)
518  m_UiInfo = new SCR_AIGroupUIInfo();
519 
520  RPC_DoSetGroupFlag(flagIndex, isFromImageset);
521  Rpc(RPC_DoSetGroupFlag, flagIndex, isFromImageset);
522  }
523 
524  //------------------------------------------------------------------------------------------------
525  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
526  void RPC_DoSetGroupFlag(int flagIndex, bool isFromImageset)
527  {
528  SCR_GroupsManagerComponent groupManager = SCR_GroupsManagerComponent.GetInstance();
529  if (!groupManager)
530  return;
531 
532  SCR_Faction scrFaction = SCR_Faction.Cast(GetFaction());
533  if (!scrFaction)
534  return;
535 
536  m_UiInfo.SetFlagIsFromImageSet(isFromImageset);
537 
538  if (isFromImageset)
539  {
540  if (flagIndex >= 0)
541  m_UiInfo.SetGroupFlag(scrFaction.GetFlagName(flagIndex));
542  }
543  else
544  {
545  array<ResourceName> textures = {};
546  scrFaction.GetGroupFlagTextures(textures);
547 
548  if (textures.IsIndexValid(flagIndex))
549  m_UiInfo.SetGroupFlag(textures[flagIndex]);
550  }
551 
552  s_OnFlagSelected.Invoke();
553  }
554 
555  //------------------------------------------------------------------------------------------------
556  void SetCustomName(string name, int authorID)
557  {
558  RPC_DoSetCustomName(name, authorID);
559  Rpc(RPC_DoSetCustomName, name, authorID);
560  }
561 
562  //------------------------------------------------------------------------------------------------
563  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
564  void RPC_DoSetCustomName(string name, int authorID)
565  {
566  m_sCustomName = name;
567  m_iNameAuthorID = authorID;
568  s_OnCustomNameChanged.Invoke(this);
569  }
570 
571  //------------------------------------------------------------------------------------------------
573  void SetCustomDescription(string desc, int authorID)
574  {
575  RPC_DoSetCustomDescription(desc, authorID);
576  Rpc(RPC_DoSetCustomDescription, desc, authorID);
577  }
578 
579  //------------------------------------------------------------------------------------------------
580  void SetMaxGroupMembers(int value)
581  {
582  RPC_DoSetMaxGroupMembers(value);
583  Rpc(RPC_DoSetMaxGroupMembers, value);
584  }
585 
586  //------------------------------------------------------------------------------------------------
587  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
588  void RPC_DoSetMaxGroupMembers(int value)
589  {
590  m_iMaxMembers = value;
591  }
592 
593  //------------------------------------------------------------------------------------------------
594  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
595  void RPC_DoSetCustomDescription(string desc, int authorID)
596  {
597  m_sCustomDescription = desc;
598  m_iDescriptionAuthorID = authorID;
599  s_OnCustomDescChanged.Invoke();
600  }
601 
602  //------------------------------------------------------------------------------------------------
603  string GetCustomDescription()
604  {
605  bool canViewContentBy = true;
606 
607  if (m_iDescriptionAuthorID > 0)
608  canViewContentBy = GetGame().GetPlayerController().CanViewContentCreatedBy(m_iDescriptionAuthorID);
609 
610  if (m_sCustomDescription.IsEmpty() || !canViewContentBy)
611  return string.Empty;
612 
613  return m_sCustomDescription;
614  }
615 
616  //------------------------------------------------------------------------------------------------
617  string GetCustomName()
618  {
619  bool canViewContentBy = true;
620 
621  if (m_iNameAuthorID > 0)
622  canViewContentBy = GetGame().GetPlayerController().CanViewContentCreatedBy(m_iNameAuthorID);
623 
624  if (m_sCustomName.IsEmpty() || !canViewContentBy)
625  return string.Empty;
626 
627  return m_sCustomName;
628  }
629 
630  //------------------------------------------------------------------------------------------------
631  int GetDescriptionAuthorID()
632  {
633  return m_iDescriptionAuthorID;
634  }
635 
636  //------------------------------------------------------------------------------------------------
637  int GetNameAuthorID()
638  {
639  return m_iNameAuthorID;
640  }
641 
642  //------------------------------------------------------------------------------------------------
643  string GetCustomNameWithOriginal()
644  {
645  string company, platoon, squad, character, format;
646  GetCallsigns(company, platoon, squad, character, format);
647  string originalName, newName;
648  originalName = string.Format(format, company, platoon, squad, character);
649 
650  if (m_iDescriptionAuthorID < 1 || m_sCustomName.IsEmpty() || !GetGame().GetPlayerController().CanViewContentCreatedBy(m_iNameAuthorID))
651  return originalName;
652 
653  return m_sCustomName + " ( " + originalName + " )";
654  }
655 
656  //------------------------------------------------------------------------------------------------
657  int GetGroupID()
658  {
659  return m_iGroupID;
660  }
661 
662  //------------------------------------------------------------------------------------------------
663  void SetGroupID(int id)
664  {
665  m_iGroupID = id;
666  }
667 
668  //------------------------------------------------------------------------------------------------
669  int GetMaxMembers()
670  {
671  return m_iMaxMembers;
672  }
673 
674  //------------------------------------------------------------------------------------------------
676  void SetMaxMembers(int maxMembers)
677  {
678  if (maxMembers == GetMaxMembers() || maxMembers < 0)
679  return;
680 
681  RPC_DoSetMaxMembers(maxMembers);
682  Rpc(RPC_DoSetMaxMembers, maxMembers);
683  }
684 
685  //------------------------------------------------------------------------------------------------
686  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
687  void RPC_DoSetMaxMembers(int maxMembers)
688  {
689  m_iMaxMembers = maxMembers;
690  s_OnMaxMembersChanged.Invoke();
691  }
692 
693  //------------------------------------------------------------------------------------------------
694  array<int> GetPlayerIDs()
695  {
696  return m_aPlayerIDs;
697  }
698 
699  //------------------------------------------------------------------------------------------------
705  int GetPlayerCount(bool checkMasterAndSlaves = false)
706  {
707  if (!checkMasterAndSlaves)
708  return m_aPlayerIDs.Count();
709 
710  int totalPlayerCount = GetPlayerCount();
711  SCR_AIGroup aiGroup = GetSlave();
712  if (aiGroup)
713  totalPlayerCount += aiGroup.GetPlayerCount();
714 
715  aiGroup = GetMaster();
716  if (aiGroup)
717  totalPlayerCount += aiGroup.GetPlayerCount();
718 
719  return totalPlayerCount;
720  }
721 
722  //------------------------------------------------------------------------------------------------
723  ScriptInvoker GetOnMemberStateChange()
724  {
725  return m_OnGroupMemberStateChange;
726  }
727 
728  //------------------------------------------------------------------------------------------------
729  static ScriptInvoker GetOnPlayerAdded()
730  {
731  return s_OnPlayerAdded;
732  }
733 
734  //------------------------------------------------------------------------------------------------
735  static ScriptInvoker GetOnPlayerRemoved()
736  {
737  return s_OnPlayerRemoved;
738  }
739 
740  //------------------------------------------------------------------------------------------------
741  static ScriptInvoker GetOnPlayerLeaderChanged()
742  {
743  return s_OnPlayerLeaderChanged;
744  }
745 
746  //------------------------------------------------------------------------------------------------
747  static ScriptInvoker GetOnPrivateGroupChanged()
748  {
749  return s_OnPrivateGroupChanged;
750  }
751 
752  //------------------------------------------------------------------------------------------------
753  static ScriptInvoker GetOnCustomNameChanged()
754  {
755  return s_OnCustomNameChanged;
756  }
757 
758  //------------------------------------------------------------------------------------------------
759  static ScriptInvoker GetOnFrequencyChanged()
760  {
761  return s_OnFrequencyChanged;
762  }
763 
764  //------------------------------------------------------------------------------------------------
765  static ScriptInvoker GetOnCustomDescriptionChanged()
766  {
767  return s_OnCustomDescChanged;
768  }
769 
770  //------------------------------------------------------------------------------------------------
771  bool BelongedToGroup(int playerID)
772  {
773  return m_aDisconnectedPlayerIDs != null && m_aDisconnectedPlayerIDs.Contains(playerID);
774  }
775 
776  //------------------------------------------------------------------------------------------------
777  void AddAgentFromControlledEntity(notnull IEntity controlledEntity)
778  {
779  AIControlComponent aiControlComponent = AIControlComponent.Cast(controlledEntity.FindComponent(AIControlComponent));
780  if (!aiControlComponent)
781  return;
782 
783  AIAgent agent = aiControlComponent.GetAIAgent();
784  if (!agent)
785  return;
786 
787  AddAgent(agent);
788 
789  //we send notification to master group players
790  if (IsSlave())
791  NotificateGroupAIChange(controlledEntity, ENotification.GROUPS_AI_JOINED);
792 
793  OnGroupMemberStateChange();
794  }
795 
796  //------------------------------------------------------------------------------------------------
797  void RemoveAgentFromControlledEntity(notnull IEntity controlledEntity)
798  {
799  AIControlComponent aiControlComponent = AIControlComponent.Cast(controlledEntity.FindComponent(AIControlComponent));
800  if (!aiControlComponent)
801  return;
802 
803  AIAgent agent = aiControlComponent.GetAIAgent();
804  if (!agent)
805  return;
806  RemoveAgent(agent);
807 
808  //we send notification to master group players
809  if (IsSlave())
810  NotificateGroupAIChange(controlledEntity, ENotification.GROUPS_AI_LEFT);
811 
812  OnGroupMemberStateChange();
813  }
814 
815  //------------------------------------------------------------------------------------------------
816  void NotificateGroupAIChange(IEntity controlledEntity, ENotification notificationType)
817  {
818  RplId rplId = -1;
819  SCR_EditableEntityComponent editableEntityComp = SCR_EditableEntityComponent.Cast(controlledEntity.FindComponent(SCR_EditableEntityComponent));
820  if (editableEntityComp)
821  rplId = Replication.FindId(editableEntityComp);
822 
823  if (rplId.IsValid())
824  SCR_NotificationsComponent.SendToGroup(GetMaster().GetGroupID(), notificationType, rplId);
825  }
826 
827  //------------------------------------------------------------------------------------------------
829  void OnPlayerDisconnected(int playerID)
830  {
831  // No need to null check m_aPlayerIDs, it always exists together with this entity
832  int index = m_aPlayerIDs.Find(playerID);
833  if (index < 0)
834  return;
835 
836  // We have to check existence of m_aDisconnectedPlayerIDs, because we might be adding the first entry
837  if (!m_aDisconnectedPlayerIDs)
838  m_aDisconnectedPlayerIDs = {};
839 
840  RemovePlayer(playerID);
841  m_aDisconnectedPlayerIDs.Insert(playerID);
842  }
843 
844  //------------------------------------------------------------------------------------------------
846  void OnPlayerConnected(int playerID)
847  {
848  // No need to null check m_aDisconnectedPlayerIDs, this method is only called after entries are added to it
849  int index = m_aDisconnectedPlayerIDs.Find(playerID);
850  if (index < 0)
851  return;
852 
853  m_aDisconnectedPlayerIDs.Remove(index);
854 
855  // Not full, we add the player, else bad luck, find a new group
856  if (!IsFull())
857  AddPlayer(playerID);
858 
859  // No more disconnected players from this group, let's stop listening
860  if (m_aDisconnectedPlayerIDs.Count() == 0)
861  {
863  if (gameMode)
864  gameMode.GetOnPlayerConnected().Remove(OnPlayerConnected);
865  }
866  }
867 
868  //------------------------------------------------------------------------------------------------
869  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
870  void RPC_DoOnGroupMemberStateChange()
871  {
872  GetGame().GetCallqueue().CallLater(m_OnGroupMemberStateChange.Invoke, 1, false, null, null, null, null, null, null, null, null, null);
873  }
874 
875  //------------------------------------------------------------------------------------------------
877  void OnGroupMemberStateChange()
878  {
879  RPC_DoOnGroupMemberStateChange(); //Local call
880  Rpc(RPC_DoOnGroupMemberStateChange); //Broadcast to clients
881  }
882 
883  //------------------------------------------------------------------------------------------------
884  void OnMemberDeath(notnull SCR_CharacterControllerComponent memberController, IEntity killerEntity, Instigator killer)
885  {
886  //This event is only called for members of the group, so it's safe to call QueueAddAgent automatically
887  int playerID = GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(memberController.GetCharacter());
888  QueueAddAgent(playerID);
889  RemovePlayerAgent(playerID);
890  OnGroupMemberStateChange();
891  }
892 
893  //------------------------------------------------------------------------------------------------
894  void ListenToMemberDeath(notnull IEntity groupMember)
895  {
896  SCR_CharacterControllerComponent characterController = SCR_CharacterControllerComponent.Cast(groupMember.FindComponent(SCR_CharacterControllerComponent));
897  if (!characterController)
898  return;
899 
900  characterController.GetOnPlayerDeathWithParam().Insert(OnMemberDeath);
901  }
902 
903  //------------------------------------------------------------------------------------------------
904  void AddOnGadgetsLoadedListener(int playerID, notnull IEntity controlledEntity)
905  {
906  SCR_GadgetManagerComponent.GetOnGadgetInitDoneInvoker().Insert(OnControllableEntitySpawned);
907 
909  if (gameMode)
910  gameMode.GetOnPlayerSpawned().Remove(AddOnGadgetsLoadedListener);
911  }
912 
913  //------------------------------------------------------------------------------------------------
914  void OnControllableEntitySpawned(IEntity controlledEntity, notnull SCR_GadgetManagerComponent gadgetManager)
915  {
916  if (!controlledEntity)
917  return;
918 
919  int playerID = GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(controlledEntity);
920  if (playerID == 0)
921  return;
922 
923  int index = m_aAgentIDQueue.Find(playerID);
924  if (index < 0)
925  return;
926 
927  ListenToMemberDeath(controlledEntity);
928  AddAgentFromControlledEntity(controlledEntity);
929  m_aAgentIDQueue.Remove(index);
930 
931  if (!m_aAgentIDQueue.IsEmpty())
932  return;
933 
934  SCR_GadgetManagerComponent.GetOnGadgetInitDoneInvoker().Remove(OnControllableEntitySpawned);
935  }
936 
937  //------------------------------------------------------------------------------------------------
938  void QueueAddAgent(int playerID)
939  {
940  // Avoiding duplicate entries
941  m_aAgentIDQueue.Insert(playerID);
942  if (m_aAgentIDQueue.Count() != 1)
943  return;
944 
946  if (!gameMode)
947  return;
948 
949  // Waiting for spawn of a controllable entity with correct ID
950  gameMode.GetOnPlayerSpawned().Insert(AddOnGadgetsLoadedListener);
951  }
952 
953  //------------------------------------------------------------------------------------------------
954  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
955  void RPC_DoAddPlayer(int playerID)
956  {
957  m_aPlayerIDs.Insert(playerID);
958  s_OnPlayerAdded.Invoke(this, playerID);
959  }
960 
961  //------------------------------------------------------------------------------------------------
964  void CheckForLeader(int playerID, bool noLeaderAllowed)
965  {
966  //check if leader is still in group, noleaderallowed is preparation for editor integration
967  if (!IsPlayerInGroup(GetLeaderID()) && !noLeaderAllowed && playerID == -1)
968  {
969  if (m_aPlayerIDs.IsEmpty())
970  {
971  s_OnPlayerLeaderChanged.Invoke(m_iGroupID, -1);
972  return;
973  }
974  SetGroupLeader(m_aPlayerIDs.Get(0));
975  return;
976  }
977 
978  //if we have a leader, do nothing
979  if (GetLeaderID() != -1)
980  return;
981 
982  //otherwise appoint provided player as a leader
983  if (playerID != -1)
984  SetGroupLeader(playerID);
985 
986 
987  }
988 
989  //------------------------------------------------------------------------------------------------
991  void SetGroupLeader(int playerID)
992  {
993  SCR_NotificationsComponent.SendToGroup(m_iGroupID, ENotification.GROUPS_PLAYER_PROMOTED_LEADER, playerID);
994  RPC_SetLeaderID(playerID);
995  Rpc(RPC_SetLeaderID, playerID);
996  }
997 
998  //------------------------------------------------------------------------------------------------
999  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1000  void RPC_SetLeaderID(int playerID)
1001  {
1002  if (playerID == m_iLeaderID)
1003  return;
1004  m_iLeaderID = playerID;
1005  s_OnPlayerLeaderChanged.Invoke(m_iGroupID, playerID);
1006  }
1007 
1008  //------------------------------------------------------------------------------------------------
1009  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1010  void RPC_SetPrivate(bool isPrivate)
1011  {
1012  m_bPrivate = isPrivate;
1013  s_OnPrivateGroupChanged.Invoke(m_iGroupID, isPrivate);
1014 
1015  if (!isPrivate)
1016  {
1017  ClearRequesters();
1018  ClearDeniedRequester();
1019  }
1020  }
1021 
1022  //------------------------------------------------------------------------------------------------
1024  void AddPlayer(int playerID)
1025  {
1026  // Avoiding duplicate entries
1027  if (m_aPlayerIDs.Contains(playerID))
1028  return;
1029 
1030  SCR_NotificationsComponent.SendToGroup(m_iGroupID, ENotification.GROUPS_PLAYER_JOINED, playerID);
1031  RPC_DoAddPlayer(playerID);
1032  Rpc(RPC_DoAddPlayer, playerID);
1033  // Did this player re-connect?
1034  if (m_aDisconnectedPlayerIDs)
1035  {
1036  int index = m_aDisconnectedPlayerIDs.Find(playerID);
1037  if (index >= 0)
1038  m_aDisconnectedPlayerIDs.Remove(index);
1039  }
1040 
1041  // Start listening to disconnect events when we add the first player
1042  if (m_aPlayerIDs.Count() == 1)
1043  {
1045  if (gameMode)
1046  gameMode.GetOnPlayerDisconnected().Insert(OnPlayerDisconnected);
1047  }
1048  // Now we need the player character's agent
1049  IEntity controlledEntity = GetGame().GetPlayerManager().GetPlayerControlledEntity(playerID);
1050  if (!controlledEntity)
1051  QueueAddAgent(playerID);
1052  else
1053  AddAgentFromControlledEntity(controlledEntity);
1054 
1055  GetGame().GetCallqueue().CallLater(CheckForLeader, 0, false, playerID, false);
1056  }
1057 
1058  //------------------------------------------------------------------------------------------------
1059  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1060  void RPC_DoAddRequester(int playerID)
1061  {
1062  m_aRequesterIDs.Insert(playerID);
1063  s_OnJoinPrivateGroupRequest.Invoke();
1064  }
1065 
1066  //------------------------------------------------------------------------------------------------
1067  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1068  void RPC_DoRemoveRequester(int playerID)
1069  {
1070  m_aRequesterIDs.RemoveItem(playerID);
1071  }
1072 
1073  //------------------------------------------------------------------------------------------------
1074  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1075  void RPC_DoClearDeniedRequester()
1076  {
1077  m_aDeniedRequesters.Clear();
1078  }
1079 
1080  //------------------------------------------------------------------------------------------------
1081  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1082  void RPC_DoClearRequesterIDs()
1083  {
1084  m_aRequesterIDs.Clear();
1085  }
1086 
1087  //------------------------------------------------------------------------------------------------
1088  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1089  void RPC_DoAddDeniedRequester(int playerID)
1090  {
1091  m_aDeniedRequesters.Insert(playerID);
1092  }
1093 
1094  //------------------------------------------------------------------------------------------------
1095 
1096  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1097  void RPC_DoRemovePlayer(int playerID)
1098  {
1099  m_aPlayerIDs.RemoveItem(playerID);
1100  if (m_iLeaderID == playerID)
1101  m_iLeaderID = -1;
1102 
1103  s_OnPlayerRemoved.Invoke(this, playerID);
1104  }
1105 
1106  //------------------------------------------------------------------------------------------------
1107  void RemovePlayerAgent(int playerID)
1108  {
1109  IEntity controlledEntity = GetGame().GetPlayerManager().GetPlayerControlledEntity(playerID);
1110  if (!controlledEntity)
1111  return;
1112 
1113  AIControlComponent aiControlComponent = AIControlComponent.Cast(controlledEntity.FindComponent(AIControlComponent));
1114  if (!aiControlComponent)
1115  return;
1116 
1117  AIAgent agent = aiControlComponent.GetAIAgent();
1118  if (!agent)
1119  return;
1120 
1121  RemoveAgent(agent);
1122  }
1123 
1124  //------------------------------------------------------------------------------------------------
1126  void RemovePlayer(int playerID)
1127  {
1128  if (!m_aPlayerIDs.Contains(playerID))
1129  return;
1130 
1131  RPC_DoRemovePlayer(playerID);
1132  Rpc(RPC_DoRemovePlayer, playerID);
1133  CheckForLeader(-1, false);
1134  RemovePlayerAgent(playerID);
1135  SCR_NotificationsComponent.SendToGroup(m_iGroupID, ENotification.GROUPS_PLAYER_LEFT, playerID);
1136  }
1137 
1138  //------------------------------------------------------------------------------------------------
1139  void GetCallsigns(out string company, out string platoon, out string squad, out string character, out string format)
1140  {
1141  SCR_CallsignGroupComponent callsignComponent = SCR_CallsignGroupComponent.Cast(FindComponent(SCR_CallsignGroupComponent));
1142  if (!callsignComponent)
1143  return;
1144 
1145  callsignComponent.GetCallsignNames(company, platoon, squad, character, format);
1146  }
1147 
1148  //------------------------------------------------------------------------------------------------
1149  void SetRadioFrequency(int frequency)
1150  {
1151  if (frequency == GetRadioFrequency() || frequency <= 0)
1152  return;
1153 
1154  RPC_DoSetFrequency(frequency);
1155  Rpc(RPC_DoSetFrequency, frequency);
1156  }
1157 
1158  //------------------------------------------------------------------------------------------------
1159  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1160  void RPC_DoSetFrequency(int frequency)
1161  {
1162  m_iGroupRadioFrequency = frequency;
1163  s_OnFrequencyChanged.Invoke();
1164  }
1165 
1166  //------------------------------------------------------------------------------------------------
1167  int GetRadioFrequency()
1168  {
1169  return m_iGroupRadioFrequency;
1170  }
1171 
1172  //------------------------------------------------------------------------------------------------
1173  bool IsPlayable()
1174  {
1175  return m_bPlayable;
1176  }
1177 
1178  //------------------------------------------------------------------------------------------------
1179  protected void CreateUnitEntities(bool editMode, array<ResourceName> entityResourceNames)
1180  {
1181  if (!GetGame().GetAIWorld())
1182  {
1183  Print(string.Format("Cannot spawn team members of group %1, AIWorld is missing in the world!", this), LogLevel.WARNING);
1184  return;
1185  }
1186 
1187 #ifdef WORKBENCH
1188  if (!editMode)
1189  {
1190  //--- Are AI components valid?
1191  AIFormationComponent AIFormation = AIFormationComponent.Cast(FindComponent(AIFormationComponent));
1192 
1193  if (!AIFormation)
1194  Print(string.Format("Group %1 does not have AIFormationComponent! Team members will not be spawned.", this), LogLevel.WARNING);
1195  else
1196  {
1197  AIFormationDefinition formationDefinition = AIFormation.GetFormation();
1198  if (!formationDefinition)
1199  Print(string.Format("Formation of group %1 not found in SCR_AIWorld! Team members will not be spawned.", this), LogLevel.WARNING);
1200  }
1201  }
1202 #endif
1203  //--- Apply global override
1204  bool snapToTerrain = m_bSnapToTerrain;
1205  if (s_bIgnoreSnapToTerrain)
1206  {
1207  snapToTerrain = false;
1208  s_bIgnoreSnapToTerrain = false;
1209  }
1210  if (Replication.IsClient())
1211  return;
1212 
1213  //--- We are in WB, prepare array so previews can be deleted later
1214  if (editMode && !m_aSceneGroupUnitInstances)
1215  m_aSceneGroupUnitInstances = new array<IEntity>;
1216 
1217  m_iNumOfMembersToSpawn = Math.Min(entityResourceNames.Count(), m_iMaxUnitsToSpawn);
1218  //--- Create group members
1219  for (int i = m_iNumOfMembersToSpawn-1; i >= 0; i--)
1220  {
1221  // Spawn group across multiple frames
1223  delaySpawn.snapToTerrain = snapToTerrain;
1224  delaySpawn.index = i;
1225  delaySpawn.resourceName = entityResourceNames[i];
1226  delaySpawn.editMode = editMode;
1227 
1228  m_delayedSpawnList.Insert(delaySpawn);
1229  }
1230 
1231  if (editMode)
1232  {
1233  //--- Edit mode has no game world, spawn immediately
1234  SpawnAllImmediately();
1235  }
1236  else
1237  {
1238  //--- Enable the frame event and frames when paused
1239  BeginDelayedSpawn();
1240  }
1241 
1242  //--- Call group init if it cannot be called by the last spawned entity
1243  if (m_iNumOfMembersToSpawn == 0)
1244  Event_OnInit.Invoke(this);
1245  }
1246  /*
1247  Spawn single Group member.
1248  Returns false when action has to be delayed
1249  */
1250  protected bool SpawnGroupMember(bool snapToTerrain, int index, ResourceName res, bool editMode, bool isLast)
1251  {
1252  if (!GetGame().GetAIWorld().CanLimitedAIBeAdded())
1253  {
1254  if (isLast)
1255  Event_OnInit.Invoke(this);
1256 
1257  //Event_OnLastGroupMemberSpawned.Invoke(this);
1258 
1259  return true;
1260  }
1261  BaseWorld world = GetWorld();
1262  AIFormationDefinition formationDefinition;
1263  AIFormationComponent formationComponent = AIFormationComponent.Cast(this.FindComponent(AIFormationComponent));
1264  if (formationComponent)
1265  formationDefinition = formationComponent.GetFormation();
1266  EntitySpawnParams spawnParams = new EntitySpawnParams;
1267  spawnParams.TransformMode = ETransformMode.WORLD;
1268  GetWorldTransform(spawnParams.Transform);
1269  vector pos = spawnParams.Transform[3];
1270 
1271  if (formationDefinition)
1272  pos = CoordToParent(formationDefinition.GetOffsetPosition(index));
1273  else
1274  pos = CoordToParent(Vector(index, 0, 0));
1275 
1276  if (snapToTerrain)
1277  {
1278  float surfaceY = world.GetSurfaceY(pos[0], pos[2]);
1279  pos[1] = surfaceY;
1280  }
1281 
1282 
1283  //Snap to the nearest navmesh point
1284  AIPathfindingComponent pathFindindingComponent = AIPathfindingComponent.Cast(this.FindComponent(AIPathfindingComponent));
1285 
1286  if (!editMode)
1287  {
1288  NavmeshWorldComponent navmesh = pathFindindingComponent.GetNavmeshComponent();
1289  if (navmesh)
1290  {
1291  if (navmesh.IsTileRequested(pos))
1292  {
1293  return false;
1294  }
1295  if (!navmesh.IsTileLoaded(pos))
1296  {
1297  navmesh.LoadTileIn(pos);
1298  return false;
1299  }
1300  }
1301  }
1302 
1303  if (pathFindindingComponent && pathFindindingComponent.GetClosestPositionOnNavmesh(pos, "10 10 10", pos))
1304  {
1305  float groundHeight = world.GetSurfaceY(pos[0], pos[2]);
1306  if (pos[1] < groundHeight)
1307  pos[1] = groundHeight;
1308  vector outWaterSurfacePoint;
1309  EWaterSurfaceType waterSurfaceType;
1310  vector transformWS[4];
1311  vector obbExtents;
1312  if (ChimeraWorldUtils.TryGetWaterSurface(GetWorld(), pos, outWaterSurfacePoint, waterSurfaceType, transformWS, obbExtents))
1313  {
1314  pos = outWaterSurfacePoint;
1315  }
1316  }
1317 
1318  spawnParams.Transform[3] = pos;
1319 
1320  IEntity member = GetGame().SpawnEntityPrefab(Resource.Load(res), world, spawnParams);
1321  if (!member)
1322  return true;
1323 
1324  // Move in to vehicle
1325  SCR_EditableEntityComponent editableEntity = SCR_EditableEntityComponent.Cast(member.FindComponent(SCR_EditableEntityComponent));
1326 
1327 
1328  if (editMode)
1329  m_aSceneGroupUnitInstances.Insert(member);
1330 
1331  // Even same null-check is above, in some situations, member can get deleted and it would result in VME
1332  if (!member)
1333  return true;
1334 
1335  AddAIEntityToGroup(member);
1336 
1337  FactionAffiliationComponent factionAffiliation = FactionAffiliationComponent.Cast(member.FindComponent(FactionAffiliationComponent));
1338 
1339  if (factionAffiliation)
1340  factionAffiliation.SetAffiliatedFactionByKey(m_faction);
1341 
1342  if (isLast)
1343  Event_OnInit.Invoke(this);
1344  return true;
1345  }
1346 
1347  //------------------------------------------------------------------------------------------------
1348  void SetWaypointParams(out AIWaypoint wp, SCR_WaypointPrefabLocation prefabParams)
1349  {
1350  if ( SCR_TimedWaypoint.Cast(wp) && !float.AlmostEqual(prefabParams.m_WPTimeOverride,0.0))
1351  SCR_TimedWaypoint.Cast(wp).SetHoldingTime(prefabParams.m_WPTimeOverride);
1352  if ( !float.AlmostEqual(prefabParams.m_WPRadiusOverride,0.0) )
1353  wp.SetCompletionRadius(prefabParams.m_WPRadiusOverride);
1354  wp.SetName(prefabParams.m_WPInstanceName);
1355  }
1356 
1357  //------------------------------------------------------------------------------------------------
1358  void AddWaypointsDynamic(out array<IEntity> entityInstanceList, array<ref SCR_WaypointPrefabLocation> prefabs)
1359  {
1360  entityInstanceList = new array<IEntity>;
1361  EntitySpawnParams spawnParams = new EntitySpawnParams;
1362  spawnParams.TransformMode = ETransformMode.WORLD;
1363  vector mat[4];
1364  Math3D.MatrixIdentity4(mat);
1365  for (int i = 0, length = prefabs.Count(); i < length; i++)
1366  {
1367  IEntity entity;
1368  AIWaypoint wp;
1369  mat[3] = prefabs[i].m_WPWorldLocation;
1370  spawnParams.Transform = mat;
1371 
1372 #ifdef WORKBENCH //includes game mode run from WB
1373  WorldEditorAPI m_API = _WB_GetEditorAPI();
1374  if (m_API)
1375  {
1376  Print(prefabs[i].m_WPPrefabName);
1377  entity = GetGame().SpawnEntityPrefab(Resource.Load(prefabs[i].m_WPPrefabName), m_API.GetWorld(), spawnParams);
1378  entityInstanceList.Insert(entity);
1379  wp = AIWaypoint.Cast(entity);
1380  }
1381  else
1382  {
1383  entity = GetGame().SpawnEntityPrefab(Resource.Load(prefabs[i].m_WPPrefabName), GetGame().GetWorld(), spawnParams);
1384  wp = AIWaypoint.Cast(entity);
1385  AddWaypoint(wp);
1386  }
1387 #else // game run from build
1388  entity = GetGame().SpawnEntityPrefab(Resource.Load(prefabs[i].m_WPPrefabName), GetGame().GetWorld(), spawnParams);
1389  wp = AIWaypoint.Cast(entity);
1390  AddWaypoint(wp);
1391 #endif
1392  if ( wp )
1393  SetWaypointParams(wp,prefabs[i]);
1394  }
1395  }
1396 
1397  //------------------------------------------------------------------------------------------------
1398  void AddWaypointsStatic(array<string> aWaypointNames)
1399  {
1400  for (int i = 0, length = aWaypointNames.Count(); i < length; i++)
1401  {
1402 #ifdef WORKBENCH
1403  WorldEditorAPI m_API = _WB_GetEditorAPI();
1404  if (m_API)
1405  {
1406  AddWaypoint(AIWaypoint.Cast(m_API.GetWorld().FindEntityByName(aWaypointNames[i])));
1407  }
1408  else
1409  {
1410  AddWaypoint(AIWaypoint.Cast(GetGame().GetWorld().FindEntityByName(aWaypointNames[i])));
1411  }
1412 #else
1413  AddWaypoint(AIWaypoint.Cast(GetGame().GetWorld().FindEntityByName(aWaypointNames[i])));
1414 #endif
1415  }
1416  }
1417 
1418  //------------------------------------------------------------------------------------------------
1419  void AddVehiclesStatic(array<string> aVehicleNames)
1420  {
1421  for (int i = 0, length = aVehicleNames.Count(); i < length; i++)
1422  {
1423 #ifdef WORKBENCH
1424  WorldEditorAPI m_API = _WB_GetEditorAPI();
1425  if (m_API)
1426  {
1427  AddUsableVehicle(m_API.GetWorld().FindEntityByName(aVehicleNames[i]));
1428  }
1429  else
1430  {
1431  AddUsableVehicle(GetGame().GetWorld().FindEntityByName(aVehicleNames[i]));
1432  }
1433 #else
1434  AddUsableVehicle(GetGame().GetWorld().FindEntityByName(aVehicleNames[i]));
1435 #endif
1436  }
1437  }
1438 
1439  //------------------------------------------------------------------------------------------------
1440  protected void DestroyEntities(out array<IEntity> entityList)
1441  {
1442  if (!entityList)
1443  return;
1444 
1445  for (int i=0, length = entityList.Count(); i < length; i++)
1446  {
1447  if ( AIWaypoint.Cast(entityList[i]) )
1448  RemoveWaypointFromGroup(AIWaypoint.Cast(entityList[i]));
1449  else
1450  RemoveAIEntityFromGroup(entityList[i]);
1451  delete entityList[i];
1452  }
1453  entityList.Clear();
1454  entityList = null;
1455  }
1456 
1457  //------------------------------------------------------------------------------------------------
1458  void RemoveStaticWaypointRefs(array<string> aWaypointNames)
1459  {
1460  if (!aWaypointNames)
1461  return;
1462 
1463  for (int i=0, length = aWaypointNames.Count(); i < length; i++)
1464  {
1465 #ifdef WORKBENCH
1466  WorldEditorAPI m_API = _WB_GetEditorAPI();
1467  if (m_API)
1468  {
1469  RemoveWaypointFromGroup(AIWaypoint.Cast(m_API.GetWorld().FindEntityByName(aWaypointNames[i])));
1470  }
1471  else
1472  {
1473  RemoveWaypointFromGroup(AIWaypoint.Cast(GetGame().GetWorld().FindEntityByName(aWaypointNames[i])));
1474  }
1475 #else
1476  RemoveWaypointFromGroup(AIWaypoint.Cast(GetGame().GetWorld().FindEntityByName(aWaypointNames[i])));
1477 #endif
1478  }
1479  }
1480 
1481  //------------------------------------------------------------------------------------------------
1482  void ClearRefs(out array<IEntity> entityList)
1483  {
1484  if ( entityList )
1485  entityList.Clear();
1486  entityList = null;
1487  }
1488 
1489  //------------------------------------------------------------------------------------------------
1490  bool AddAIEntityToGroup(IEntity entity)
1491  {
1492  if (!entity) return false;
1493 
1494  AIControlComponent control = AIControlComponent.Cast(entity.FindComponent(AIControlComponent));
1495  if (!control) return false;
1496 
1497  AIAgent agent = control.GetControlAIAgent();
1498  if (!agent) return false;
1499 
1500  control.ActivateAI();
1501 
1502  if (!agent.GetParentGroup())
1503  AddAgent(agent); //--- Add to group only if some other system (e.g., component on the group member) wasn't faster
1504 
1505  return true;
1506  }
1507 
1508  //------------------------------------------------------------------------------------------------
1509  bool RemoveAIEntityFromGroup(IEntity entity)
1510  {
1511 #ifdef WORKBENCH
1512  WorldEditorAPI m_API = _WB_GetEditorAPI();
1513  if ( !m_API && entity && entity.FindComponent(AIControlComponent))
1514  {
1515  ref AIAgent agent = AIControlComponent.Cast(entity.FindComponent(AIControlComponent)).GetControlAIAgent();
1516  RemoveAgent(agent);
1517  return true;
1518  }
1519 #else
1520  if ( entity && entity.FindComponent(AIControlComponent))
1521  {
1522  ref AIAgent agent = AIControlComponent.Cast(entity.FindComponent(AIControlComponent)).GetControlAIAgent();
1523  RemoveAgent(agent);
1524  return true;
1525  }
1526 #endif
1527  return false;
1528  }
1529 
1530  //------------------------------------------------------------------------------------------------
1531  void AddWaypointToGroup(AIWaypoint waypoint)
1532  {
1533  if ( waypoint )
1534  {
1535  AddWaypoint(waypoint);
1536  }
1537  }
1538 
1539  //------------------------------------------------------------------------------------------------
1540  void RemoveWaypointFromGroup(AIWaypoint waypoint)
1541  {
1542  if ( waypoint )
1543  {
1544  RemoveWaypoint(waypoint)
1545  }
1546  }
1547 
1548  //------------------------------------------------------------------------------------------------
1549  void AddUsableVehicle(IEntity vehicle)
1550  {
1551  if (Vehicle.Cast(vehicle) && m_aUsableVehicles.Find(vehicle) < 0)
1552  m_aUsableVehicles.Insert(vehicle);
1553  }
1554 
1555  //------------------------------------------------------------------------------------------------
1556  void RemoveUsableVehicle(IEntity vehicle)
1557  {
1558  if (Vehicle.Cast(vehicle) && m_aUsableVehicles.Find(vehicle) > -1)
1559  m_aUsableVehicles.RemoveItem(vehicle);
1560  }
1561 
1562  //------------------------------------------------------------------------------------------------
1563  bool IsUsableVehicle(IEntity vehicle)
1564  {
1565  return m_aUsableVehicles.Contains(vehicle);
1566  }
1567 
1568  //------------------------------------------------------------------------------------------------
1569  int GetUsableVehicles(out array<IEntity> usableVehicles)
1570  {
1571  return usableVehicles.Copy(m_aUsableVehicles);
1572  }
1573 
1574  //------------------------------------------------------------------------------------------------
1575  int GetUsableVehiclesCount()
1576  {
1577  return m_aUsableVehicles.Count();
1578  }
1579 
1580  //------------------------------------------------------------------------------------------------
1581  void GetAllocatedCompartments(out array<BaseCompartmentSlot> allocatedCompartments)
1582  {
1583  allocatedCompartments = m_aAllocatedCompartments;
1584  }
1585 
1586  //------------------------------------------------------------------------------------------------
1587  void AllocateCompartment(BaseCompartmentSlot compartment)
1588  {
1589  if (!compartment)
1590  return;
1591  if (m_aAllocatedCompartments.Find(compartment) > -1)
1592  {
1593  Print("Trying to allocate same compartment twice!", LogLevel.WARNING);
1594  return;
1595  };
1596  m_aAllocatedCompartments.Insert(compartment);
1597  compartment.SetCompartmentAccessible(false);
1598  }
1599 
1600  //------------------------------------------------------------------------------------------------
1601  void ReleaseCompartment(BaseCompartmentSlot compartment)
1602  {
1603  int index = m_aAllocatedCompartments.Find(compartment);
1604  if (index > -1)
1605  m_aAllocatedCompartments.Remove(index);
1606  else
1607  Print("Trying to remove compartment that is not allocated!", LogLevel.WARNING);
1608  }
1609 
1610  //------------------------------------------------------------------------------------------------
1611  void ReleaseCompartments()
1612  {
1613  foreach (BaseCompartmentSlot comp : m_aAllocatedCompartments)
1614  {
1615  if (comp)
1616  comp.SetCompartmentAccessible(true);
1617  }
1618  m_aAllocatedCompartments.Clear();
1619  }
1620 
1621  //------------------------------------------------------------------------------------------------
1627  bool InitFactionKey(string factionKey)
1628  {
1629  if (m_faction != "") return false;
1630  m_faction = factionKey;
1631  return true;
1632  }
1633 
1634  //------------------------------------------------------------------------------------------------
1641  bool SetFaction(Faction faction)
1642  {
1643  if (Replication.IsClient())
1644  return false;
1645 
1646  // If playable, we don't want to allow changing the faction.
1647  if (m_bPlayable && m_faction)
1648  return false;
1649 
1650  if (!faction)
1651  return false;
1652 
1653  m_faction = faction.GetFactionKey();
1654 
1655  array<AIAgent> agents = new array<AIAgent>;
1656  GetAgents(agents);
1657  array<IEntity> updatedVehicles = new array<IEntity>;
1658  //array<RplId> entitiesInVehiclesIds = new array<RplId>;
1659  IEntity vehicle;
1660  IEntity charEntity;
1661 
1662  foreach (AIAgent agent: agents)
1663  {
1664  charEntity = agent.GetControlledEntity();
1665 
1666  if (!charEntity)
1667  continue;
1668 
1669  FactionAffiliationComponent factionAffiliation = FactionAffiliationComponent.Cast(charEntity.FindComponent(FactionAffiliationComponent));
1670  if (factionAffiliation)
1671  factionAffiliation.SetAffiliatedFaction(faction);
1672  }
1673 
1674  //Send out event
1675  Event_OnFactionChanged.Invoke(faction);
1676 
1677  FactionManager factionManager = GetGame().GetFactionManager();
1678  int factionIndex = factionManager.GetFactionIndex(faction);
1679  if (factionManager)
1680  GetGame().GetCallqueue().CallLater(SetFactionDelayed, 1, false, factionIndex);
1681 
1682  return true;
1683  }
1684 
1685  //------------------------------------------------------------------------------------------------
1686  protected void SetFactionDelayed(int factionIndex)
1687  {
1688  Rpc(BroadCastSetFaction, factionIndex);
1689  }
1690 
1691  //------------------------------------------------------------------------------------------------
1692  //Send to update Editor UI
1693  [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
1694  protected void BroadCastSetFaction(int factionIndex)
1695  {
1696  FactionManager factionManager = GetGame().GetFactionManager();
1697  if (!factionManager)
1698  return;
1699 
1700  Faction faction = factionManager.GetFactionByIndex(factionIndex);
1701  m_faction = faction.GetFactionKey();
1702  Event_OnFactionChanged.Invoke(faction);
1703  }
1704 
1705  //------------------------------------------------------------------------------------------------
1710  string GetFactionName()
1711  {
1712  return m_faction;
1713  }
1714 
1715  //------------------------------------------------------------------------------------------------
1720  Faction GetFaction()
1721  {
1722  ArmaReforgerScripted game = GetGame();
1723  if (!game) return null;
1724 
1725  FactionManager factionManager = game.GetFactionManager();
1726  if (!factionManager) return null;
1727 
1728  return factionManager.GetFactionByKey(m_faction);
1729  }
1730 
1735  int GetFactionIndex()
1736  {
1737  FactionManager factionManager = GetGame().GetFactionManager();
1738  if (!factionManager)
1739  return -1;
1740 
1741  return factionManager.GetFactionIndex(GetFaction());
1742  }
1743 
1744  //------------------------------------------------------------------------------------------------
1751  static void IgnoreSnapToTerrain(bool ignore)
1752  {
1753  s_bIgnoreSnapToTerrain = ignore;
1754  }
1761  static void IgnoreSpawning(bool ignore)
1762  {
1763  s_bIgnoreSpawning = ignore;
1764  }
1765 
1766  //------------------------------------------------------------------------------------------------
1773  ScriptInvoker GetOnInit()
1774  {
1775  return Event_OnInit;
1776  }
1777 
1784  ScriptInvoker_AIGroupOnEmpty GetOnEmpty()
1785  {
1786  return Event_OnEmpty;
1787  }
1788 
1789  //------------------------------------------------------------------------------------------------
1796  ScriptInvoker GetOnAgentAdded()
1797  {
1798  return Event_OnAgentAdded;
1799  }
1800 
1801  //------------------------------------------------------------------------------------------------
1808  ScriptInvoker GetOnAgentRemoved()
1809  {
1810  return Event_OnAgentRemoved;
1811  }
1812 
1813  //------------------------------------------------------------------------------------------------
1820  ScriptInvoker GetOnLeaderChanged()
1821  {
1822  return Event_OnLeaderChanged;
1823  }
1824 
1825  //------------------------------------------------------------------------------------------------
1832  ScriptInvoker GetOnCurrentWaypointChanged()
1833  {
1834  return Event_OnCurrentWaypointChanged;
1835  }
1836 
1837  //------------------------------------------------------------------------------------------------
1844  ScriptInvoker GetOnWaypointCompleted()
1845  {
1846  return Event_OnWaypointCompleted;
1847  }
1848 
1849  //------------------------------------------------------------------------------------------------
1856  ScriptInvoker GetOnWaypointAdded()
1857  {
1858  return Event_OnWaypointAdded;
1859  }
1860 
1861  //------------------------------------------------------------------------------------------------
1868  ScriptInvoker GetOnWaypointRemoved()
1869  {
1870  return Event_OnWaypointRemoved;
1871  }
1872 
1873  //------------------------------------------------------------------------------------------------
1878  ScriptInvoker GetOnFactionChanged()
1879  {
1880  return Event_OnFactionChanged;
1881  }
1882 
1883  //------------------------------------------------------------------------------------------------
1884  ScriptInvokerBase<ScriptInvokerAIGroup> GetOnAllDelayedEntitySpawned()
1885  {
1886  if (!Event_OnAllDelayedEntitySpawned)
1887  Event_OnAllDelayedEntitySpawned = new ScriptInvokerBase<ScriptInvokerAIGroup>();
1888 
1889  return Event_OnAllDelayedEntitySpawned;
1890  }
1891 
1892  //------------------------------------------------------------------------------------------------
1893  override void OnEmpty()
1894  {
1895  Event_OnEmpty.Invoke(this);
1896 
1897  //--- Delete after delay, doing it directly in this event would be unsafe
1898  if (m_bDeleteWhenEmpty)
1899  GetGame().GetCallqueue().CallLater(SCR_EntityHelper.DeleteEntityAndChildren, 1, false, this);
1900  }
1901 
1902  //------------------------------------------------------------------------------------------------
1903  override void OnAgentAdded(AIAgent child)
1904  {
1905  Event_OnAgentAdded.Invoke(child);
1906 
1907  SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(child);
1908  if (agent)
1909  {
1910  agent.OnGroupWaypointChanged(GetCurrentWaypoint());
1911  }
1912  }
1913 
1914  //------------------------------------------------------------------------------------------------
1915  override void OnAgentRemoved(AIAgent child)
1916  {
1917  Event_OnAgentRemoved.Invoke(this, child);
1918  }
1919 
1920  //------------------------------------------------------------------------------------------------
1921  override void OnLeaderChanged(AIAgent currentLeader, AIAgent prevLeader)
1922  {
1923  Event_OnLeaderChanged.Invoke(currentLeader, prevLeader);
1924 
1925  if (currentLeader)
1926  {
1927  ChimeraCharacter character = ChimeraCharacter.Cast(currentLeader.GetControlledEntity());
1928  if (character)
1929  {
1930  SCR_CharacterControllerComponent controller = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
1931  if (controller)
1932  controller.m_OnLifeStateChanged.Insert(LeaderLifeStateChanged);
1933  }
1934  }
1935 
1936  if (!prevLeader)
1937  return;
1938 
1939  ChimeraCharacter character = ChimeraCharacter.Cast(prevLeader.GetControlledEntity());
1940  if (character)
1941  {
1942  SCR_CharacterControllerComponent controller = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
1943  if (controller)
1944  controller.m_OnLifeStateChanged.Remove(LeaderLifeStateChanged);
1945  }
1946  }
1947 
1948  //------------------------------------------------------------------------------------------------
1949  override void OnCurrentWaypointChanged(AIWaypoint currentWP, AIWaypoint prevWP)
1950  {
1951  InvokeSubagentsOnWaypointChanged(currentWP);
1952 
1953  Event_OnCurrentWaypointChanged.Invoke(currentWP, prevWP);
1954  }
1955 
1956  //------------------------------------------------------------------------------------------------
1957  override void OnWaypointCompleted(AIWaypoint wp)
1958  {
1959  InvokeSubagentsOnWaypointChanged(null);
1960 
1961  Event_OnWaypointCompleted.Invoke(wp);
1962  }
1963 
1964  //------------------------------------------------------------------------------------------------
1965  override void OnWaypointAdded(AIWaypoint wp)
1966  {
1967  Event_OnWaypointAdded.Invoke(wp);
1968  }
1969 
1970  //------------------------------------------------------------------------------------------------
1971  override void OnWaypointRemoved(AIWaypoint wp, bool isCurrentWaypoint)
1972  {
1973  InvokeSubagentsOnWaypointChanged(null);
1974 
1975  Event_OnWaypointRemoved.Invoke(wp, isCurrentWaypoint);
1976  }
1977 
1978  //------------------------------------------------------------------------------------------------
1981  protected void InvokeSubagentsOnWaypointChanged(AIWaypoint newWaypoint)
1982  {
1983  array<AIAgent> agents = {};
1984  GetAgents(agents);
1985  foreach (AIAgent agent : agents)
1986  {
1987  SCR_ChimeraAIAgent _agent = SCR_ChimeraAIAgent.Cast(agent);
1988  if (_agent)
1989  _agent.OnGroupWaypointChanged(newWaypoint);
1990  }
1991  }
1992 
1993  //------------------------------------------------------------------------------------------------
1994 #ifdef WORKBENCH
1995  override bool _WB_OnKeyChanged(BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
1996  {
1997  if (key == "coords")
1998  {
1999  DestroyEntities(m_aSceneGroupUnitInstances);
2000  CreateUnitEntities(true,m_aUnitPrefabSlots);
2001  }
2002  return false;
2003  }
2004 #endif
2005 
2006  //------------------------------------------------------------------------------------------------
2007  override void EOnInit(IEntity owner)
2008  {
2009  m_aUsableVehicles = new array<IEntity>;
2010  m_aAllocatedCompartments = new array<BaseCompartmentSlot>;
2011 
2012  if (s_bIgnoreSpawning)
2013  {
2014  //--- Instantly mark as initialized if no team members are to be spawned
2015  Event_OnInit.Invoke(this);
2016  }
2017  else if (m_bSpawnImmediately)
2018  {
2019  SpawnUnits();
2020  }
2021 
2022  s_bIgnoreSpawning = false;
2023  }
2024 
2025  //------------------------------------------------------------------------------------------------
2026  bool GetSpawnImmediately()
2027  {
2028  return m_bSpawnImmediately;
2029  }
2030 
2031  //------------------------------------------------------------------------------------------------
2032  void SetSpawnImmediately(bool spawnImmediately)
2033  {
2034  m_bSpawnImmediately = spawnImmediately;
2035  }
2036 
2037  //------------------------------------------------------------------------------------------------
2038  void SetMaxUnitsToSpawn(int cnt)
2039  {
2040  m_iMaxUnitsToSpawn = cnt;
2041  }
2042 
2043  //------------------------------------------------------------------------------------------------
2049  void SetMemberSpawnDelay(int memberSpawnDelay)
2050  {
2051  m_fMemberSpawnDelay = memberSpawnDelay;
2052  }
2053 
2054  //------------------------------------------------------------------------------------------------
2055  void SpawnUnits()
2056  {
2057  if (SCR_Global.IsEditMode(this))
2058  {
2059  CreateUnitEntities(true, m_aUnitPrefabSlots);
2060  AddVehiclesStatic(m_aStaticVehicles);
2061  AddWaypointsStatic(m_aStaticWaypoints);
2062  AddWaypointsDynamic(m_aSceneWaypointInstances, m_aSpawnedWaypoints);
2063  }
2064  else
2065  {
2066  //--- Don't hardcode members array - it may change in run-time, and we don't want to delete members who meanwhile joined other group
2067  CreateUnitEntities(false, m_aUnitPrefabSlots);
2068  AddVehiclesStatic(m_aStaticVehicles);
2069  AddWaypointsStatic(m_aStaticWaypoints);
2070  AddWaypointsDynamic(null, m_aSpawnedWaypoints);
2071  }
2072  }
2073 
2074  //------------------------------------------------------------------------------------------------
2078  SCR_AIGroup GetSlave()
2079  {
2080  return m_SlaveGroup;
2081  }
2082 
2083  //------------------------------------------------------------------------------------------------
2087  void SetSlave(SCR_AIGroup group)
2088  {
2089  m_SlaveGroup = group;
2090  group.SetMaster(this);
2091  }
2092 
2093  //------------------------------------------------------------------------------------------------
2097  SCR_AIGroup GetMaster()
2098  {
2099  return m_MasterGroup;
2100  }
2101 
2102  //------------------------------------------------------------------------------------------------
2106  void SetMaster(SCR_AIGroup group)
2107  {
2108  m_MasterGroup = group;
2109  }
2110 
2111  //------------------------------------------------------------------------------------------------
2112  array<SCR_ChimeraCharacter> GetAIMembers()
2113  {
2114  return m_aAIMembers;
2115  }
2116 
2117  //------------------------------------------------------------------------------------------------
2118  /*
2119  returns true if SCR_ChimeraCharacter is a member of slave subgroup that is linked to this playable SCR_AIGroup
2120  */
2121  bool IsAIControlledCharacterMember(SCR_ChimeraCharacter character)
2122  {
2123  if (!character)
2124  return false;
2125  //if group doesnt have slave group for AIs, AI is automatically not a member
2126  if (!m_SlaveGroup)
2127  return false;
2128  return m_SlaveGroup.m_aAIMembers.Find(character) != -1;
2129  }
2130 
2131  //------------------------------------------------------------------------------------------------
2132  override bool RplSave(ScriptBitWriter writer)
2133  {
2134  if (!m_UiInfo)
2135  m_UiInfo = new SCR_AIGroupUIInfo();
2136 
2137  int factionIndex = -1;
2138  FactionManager factionManager = GetGame().GetFactionManager();
2139  if (factionManager)
2140  factionIndex = factionManager.GetFactionIndex(GetFaction());
2141 
2142  writer.WriteInt(factionIndex);
2143  writer.WriteInt(m_iGroupRadioFrequency);
2144  writer.WriteInt(m_iGroupID);
2145 
2146  int count = m_aPlayerIDs.Count();
2147  writer.WriteInt(count);
2148  for (int i = count - 1; i >= 0; i--)
2149  {
2150  writer.WriteInt(m_aPlayerIDs[i]);
2151  }
2152  writer.WriteInt(m_iLeaderID);
2153  writer.WriteBool(m_bPrivate);
2154 
2155  writer.WriteString(m_sCustomDescription);
2156  writer.WriteString(m_sCustomName);
2157 
2158  writer.WriteString(m_UiInfo.GetGroupFlag());
2159  writer.WriteBool(m_UiInfo.GetFlagIsFromImageSet());
2160 
2161  RplId groupID;
2162  groupID = Replication.FindId(m_MasterGroup);
2163  writer.WriteRplId(groupID);
2164  groupID = Replication.FindId(m_SlaveGroup);
2165  writer.WriteRplId(groupID);
2166 
2167  writer.WriteInt(m_iDescriptionAuthorID);
2168  writer.WriteInt(m_iNameAuthorID);
2169  writer.WriteInt(m_iMaxMembers);
2170 
2171  //do rpcs for players join/leave
2172  //add invokers for players join/leave
2173 
2174  return true;
2175  }
2176 
2177  //------------------------------------------------------------------------------------------------
2178  override bool RplLoad(ScriptBitReader reader)
2179  {
2180  int factionIndex;
2181  reader.ReadInt(factionIndex);
2182  if (factionIndex >= 0)
2183  BroadCastSetFaction(factionIndex);
2184 
2185  reader.ReadInt(m_iGroupRadioFrequency);
2186  reader.ReadInt(m_iGroupID);
2187 
2188  int count, playerID;
2189  reader.ReadInt(count);
2190  for (int i = count - 1; i >= 0; i--)
2191  {
2192  reader.ReadInt(playerID);
2193  m_aPlayerIDs.Insert(playerID);
2194  }
2195 
2196  if (m_bPlayable)
2197  {
2198  SCR_GroupsManagerComponent groupsManager = SCR_GroupsManagerComponent.GetInstance();
2199  if (groupsManager)
2200  {
2201  groupsManager.RegisterGroup(this);
2202  groupsManager.ClaimFrequency(GetRadioFrequency(), GetFaction());
2203  groupsManager.OnGroupCreated(this);
2204  }
2205  }
2206 
2207  int temp;
2208  reader.ReadInt(temp);
2209  RPC_SetLeaderID(temp);
2210 
2211  reader.ReadBool(m_bPrivate);
2212 
2213  reader.ReadString(m_sCustomDescription);
2214  reader.ReadString(m_sCustomName);
2215 
2216  if (!m_UiInfo)
2217  m_UiInfo = new SCR_AIGroupUIInfo();
2218 
2219  string flag;
2220  reader.ReadString(flag);
2221  m_UiInfo.SetGroupFlag(flag);
2222 
2223  bool isFromImageSet;
2224  reader.ReadBool(isFromImageSet);
2225  m_UiInfo.SetFlagIsFromImageSet(isFromImageSet);
2226 
2227  RplId groupID;
2228  reader.ReadRplId(groupID);
2229  m_MasterGroup = SCR_AIGroup.Cast(Replication.FindItem(groupID));
2230  reader.ReadRplId(groupID);
2231  m_SlaveGroup = SCR_AIGroup.Cast(Replication.FindItem(groupID));
2232 
2233  reader.ReadInt(m_iDescriptionAuthorID);
2234  reader.ReadInt(m_iNameAuthorID);
2235  reader.ReadInt(m_iMaxMembers);
2236 
2237  return true;
2238  }
2239 
2240  //------------------------------------------------------------------------------------------------
2241  void LeaderLifeStateChanged(ECharacterLifeState previousLifeState, ECharacterLifeState newLifeState)
2242  {
2243  if (newLifeState == ECharacterLifeState.INCAPACITATED)
2244  SetNewConsciousLeader();
2245  }
2246 
2247  //------------------------------------------------------------------------------------------------
2248  void SetNewConsciousLeader()
2249  {
2250  array<AIAgent> groupAgents = {};
2251  GetAgents(groupAgents);
2252 
2253  for (int i = 1; i < groupAgents.Count(); i++)
2254  {
2255  ChimeraCharacter character = ChimeraCharacter.Cast(groupAgents.Get(i).GetControlledEntity());
2256  if (!character)
2257  continue;
2258 
2259  CharacterControllerComponent charController = character.GetCharacterController();
2260  if (!charController)
2261  continue;
2262 
2263  if (!charController.IsUnconscious())
2264  {
2265  SetNewLeader(groupAgents.Get(i));
2266  return;
2267  }
2268  }
2269  }
2270 
2271  //------------------------------------------------------------------------------------------------
2272  void IncreaseDeployedRadioCount()
2273  {
2274  m_iDeployedRadioCount++;
2275  Replication.BumpMe();
2276  }
2277 
2278  //------------------------------------------------------------------------------------------------
2279  void DecreaseDeployedRadioCount()
2280  {
2281  if(m_iDeployedRadioCount < 1)
2282  return;
2283 
2284  m_iDeployedRadioCount--;
2285  Replication.BumpMe();
2286  }
2287 
2288  //------------------------------------------------------------------------------------------------
2289  int GetDeployedRadioCount()
2290  {
2291  return m_iDeployedRadioCount;
2292  }
2293 
2294  //------------------------------------------------------------------------------------------------
2295  void SCR_AIGroup(IEntitySource src, IEntity parent)
2296  {
2297  SetEventMask(EntityEvent.INIT);
2298  }
2299 
2300  //------------------------------------------------------------------------------------------------
2301  void ~SCR_AIGroup()
2302  {
2303  // Group is playable so we have to unregister it locally as well
2304  if (m_bPlayable)
2305  {
2306  SCR_GroupsManagerComponent groupsManager = SCR_GroupsManagerComponent.GetInstance();
2307  if (groupsManager)
2308  {
2309  groupsManager.UnregisterGroup(this);
2310  groupsManager.GetOnPlayableGroupRemoved().Invoke(this);
2311  }
2312  }
2313 
2314  DestroyEntities(m_aSceneGroupUnitInstances);
2315  DestroyEntities(m_aSceneWaypointInstances);
2316  RemoveStaticWaypointRefs(m_aStaticWaypoints);
2317  ClearRefs(m_aUsableVehicles);
2318  }
2319 };
2320 
2321 //------------------------------------------------------------------------
2323 {
2324  IDLE = 0,
2326  MOVING = 2,
2333  FOLLOW = 9,
2334 };
2335 
2336 //------------------------------------------------------------------------
2338 {
2339  Wedge,
2340  Line,
2341  Column,
2343 }
2344 
2345 //------------------------------------------------------------------------
2346 [BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
2347 class SCR_WaypointPrefabLocation
2348 {
2349  [Attribute("{750A8D1695BD6998}AI/Entities/Waypoints/AIWaypoint.et", UIWidgets.ResourceAssignArray, "Prefab for the waypoint")]
2350  ResourceName m_WPPrefabName;
2351 
2352  [Attribute("", UIWidgets.EditBox, "Waypoint name")]
2353  string m_WPInstanceName;
2354 
2355  [Attribute("", UIWidgets.EditBox, "Waypoint location")]
2356  vector m_WPWorldLocation;
2357 
2358  [Attribute("0", UIWidgets.EditBox, "Waypoint completion radius (-1 dont override default)")]
2359  float m_WPRadiusOverride;
2360 
2361  [Attribute("0", UIWidgets.EditBox, "Waypoint completion time (-1 dont override default)")]
2362  float m_WPTimeOverride;
2363 };
m_aPlayerIDs
protected ref set< int > m_aPlayerIDs
Definition: SCR_VotingBase.c:2
ATTACKING
@ ATTACKING
Definition: SCR_AIGroup.c:2325
m_MasterGroup
protected SCR_AIGroup m_MasterGroup
Definition: SCR_CallsignGroupComponent.c:28
ChimeraWorld
Definition: ChimeraWorld.c:12
SCR_BaseGameMode
Definition: SCR_BaseGameMode.c:137
CanViewContentCreatedBy
proto external bool CanViewContentCreatedBy(int playerId)
SCR_AIGroupClass
Definition: SCR_AIGroup.c:5
SCR_EntityHelper
Definition: SCR_EntityHelper.c:1
EntityEditorProps
enum EQueryType EntityEditorProps(category:"GameScripted/Sound", description:"THIS IS THE SCRIPT DESCRIPTION.", color:"0 0 255 255")
Definition: SCR_AmbientSoundsComponent.c:12
ECharacterLifeState
ECharacterLifeState
Definition: ECharacterLifeState.c:12
ScriptInvoker_AIGroupOnEmpty
ScriptInvokerBase< ScriptInvoker_AIGroupOnEmpty_Callback > ScriptInvoker_AIGroupOnEmpty
Definition: SCR_AIGroup.c:66
FOLLOW
@ FOLLOW
Definition: SCR_AIGroup.c:2333
RplProp
SCR_RplTestEntityClass RplProp
Used for handling entity spawning requests for SCR_CatalogEntitySpawnerComponent and inherited classe...
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
SCR_GadgetManagerComponent
Definition: SCR_GadgetManagerComponent.c:138
StaggeredColumn
StaggeredColumn
Definition: SCR_AIGroup.c:71
MOVING
@ MOVING
Definition: SCR_AIGroup.c:2326
ChimeraAIGroupClass
Definition: ChimeraAIGroup.c:12
m_sCustomName
protected string m_sCustomName
Definition: SCR_RewindComponent.c:12
SCR_AIGroupUIInfo
Definition: SCR_AIGroupUIInfo.c:1
RETREATING
@ RETREATING
Definition: SCR_AIGroup.c:2328
desc
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
Definition: SCR_RespawnBriefingComponent.c:17
RplRpc
SCR_AchievementsHandlerClass ScriptComponentClass RplRpc(RplChannel.Reliable, RplRcver.Owner)] void UnlockOnClient(AchievementId achievement)
Definition: SCR_AchievementsHandler.c:11
Wedge
Wedge
Definition: SCR_AIGroup.c:67
FindEntityByName
IEntity FindEntityByName(string name)
Definition: SCR_ScenarioFrameworkActionsGetters.c:40
INVESTIGATING
@ INVESTIGATING
Definition: SCR_AIGroup.c:2327
func
func
Definition: SCR_AIThreatSystem.c:5
SCR_CharacterControllerComponent
Definition: SCR_CharacterControllerComponent.c:35
REQ_SUPPORT
@ REQ_SUPPORT
Definition: SCR_AIGroup.c:2331
GetPlayerController
proto external PlayerController GetPlayerController()
Definition: SCR_PlayerDeployMenuHandlerComponent.c:307
Instigator
Definition: Instigator.c:6
GetGameMode
SCR_BaseGameMode GetGameMode()
Definition: SCR_BaseGameModeComponent.c:15
SCR_TimedWaypoint
Definition: SCR_TimedWaypoint.c:5
REQ_ORDERS
@ REQ_ORDERS
Definition: SCR_AIGroup.c:2332
ENotification
ENotification
Definition: ENotification.c:4
SCR_EAIGroupFormation
SCR_EAIGroupFormation
Definition: SCR_AIGroup.c:2337
Attribute
typedef Attribute
Post-process effect of scripted camera.
SCR_ChimeraAIAgent
Definition: SCR_ChimeraAIAgent.c:5
SCR_AIGroup_DelayedSpawn
Definition: SCR_AIGroup.c:56
m_iGroupID
SCR_PlayerControllerGroupComponentClass m_iGroupID
BaseContainerProps
enum SCR_EAIGroupFormation BaseContainerProps(namingConvention:NamingConvention.NC_MUST_HAVE_NAME)] class SCR_WaypointPrefabLocation
Definition: SCR_AIGroup.c:2346
SCR_EditableEntityComponent
Definition: SCR_EditableEntityComponent.c:13
Faction
Definition: Faction.c:12
SCR_GroupsManagerComponent
void SCR_GroupsManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
Definition: SCR_GroupsManagerComponent.c:1320
index
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition: SCR_DestructionSynchronizationComponent.c:17
SCR_Global
Definition: Functions.c:6
DEFENDING
@ DEFENDING
Definition: SCR_AIGroup.c:2329
ScriptInvoker_AIGroupOnEmpty_Callback
func ScriptInvoker_AIGroupOnEmpty_Callback
Definition: SCR_AIGroup.c:65
ScriptInvokerAIGroup
func ScriptInvokerAIGroup
Definition: SCR_AIGroup.c:2
SCR_AIGroup
Definition: SCR_AIGroup.c:68
params
Configs ServerBrowser KickDialogs params
Definition: SCR_NotificationSenderComponent.c:24
MANEUVERING
@ MANEUVERING
Definition: SCR_AIGroup.c:2330
s_bIgnoreSpawning
SCR_EditorLinkComponentClass s_bIgnoreSpawning
Column
Column
Definition: SCR_AIGroup.c:69
EGroupState
EGroupState
Definition: SCR_AIGroup.c:2322
IDLE
@ IDLE
Definition: SCR_AIGroup.c:2324
SCR_Faction
Definition: SCR_Faction.c:6
ChimeraWorldUtils
Definition: ChimeraWorldUtils.c:7
Line
Line
Definition: SCR_AIGroup.c:68
EWaterSurfaceType
EWaterSurfaceType
Definition: EWaterSurfaceType.c:7
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180