Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AICommsHandler.c
Go to the documentation of this file.
1 
9 typedef ScriptInvokerBase<SCR_AICommsStateChangedDelegate> SCR_AIOnCommsStateChangedInvoker;
10 
12 {
13  IDLE, // Not transmitting
14  WAITING, // Waiting for channel to be free to transmit
15  TRANSMITTING, // Transmitting
16  SUSPENDED // Suspended, like sleeping
17 };
18 
19 class SCR_AICommsHandler : Managed
20 {
21  // Data of this speaker
22  IEntity m_Entity;
23  AIAgent m_Agent;
24  SignalsManagerComponent m_SignalsManagerComponent;
25  VoNComponent m_VoNComponent;
26  FactionAffiliationComponent m_FactionComp;
27 
28  protected ref array<ref SCR_AITalkRequest> m_aRequestQueue = {}; // Queue of requests, new requests are added to queue end
29  protected ref ref SCR_AITalkRequest m_CurrentRequest; // Current request which we are transmitting
30  protected float m_fActiveTimer_ms; // How much time we've been in active state
31  bool m_bNeedUpdate = false; // READ ONLY - check if this is true, and if so, call update
32  protected SCR_EAICommunicationState m_eState;
33  protected bool m_bMuted;
34 
35  protected const float SAMPLE_LENGTH_MS = 2000.0; // Length of the sound sample. TODO: read the value from the data
36  protected const float NEARBY_SPEAKER_CHECK_RANGE = 10.0; // Distance for nearby speakers check
37 
38 
39  //-------------------------------------------------------------------------------------------------------------
40  // PUBLIC
41 
42  //-------------------------------------------------------------------------------------------------------------
43  void SCR_AICommsHandler(notnull IEntity entity, notnull AIAgent agent)
44  {
45  m_Entity = entity;
46  m_Agent = agent;
47  m_VoNComponent = VoNComponent.Cast(entity.FindComponent(VoNComponent));
48  m_SignalsManagerComponent = SignalsManagerComponent.Cast(entity.FindComponent(SignalsManagerComponent));
49  m_FactionComp = FactionAffiliationComponent.Cast(entity.FindComponent(FactionAffiliationComponent));
50  }
51 
52  //-------------------------------------------------------------------------------------------------------------
53  void AddRequest(SCR_AITalkRequest request)
54  {
55  #ifdef AI_DEBUG
56  AddDebugMessage(string.Format("AddRequest: %1", request.GetDebugString()));
57  #endif
58 
59  // Ignore and fail if muted
60  if (m_bMuted)
61  {
62  #ifdef AI_DEBUG
63  AddDebugMessage(" Ignored because CommsHandler is muted");
64  #endif
65 
66  FailRequest(request);
67  return;
68  }
69 
70  // Check if we can bypass the request, and if yes, instantly complete it
71  /*
72  if (m_eState != SCR_EAICommunicationState.SUSPENDED && CanBypass(request))
73  {
74  #ifdef AI_DEBUG
75  AddDebugMessage(" Bypassed the request");
76  #endif
77 
78  CompleteRequest(request);
79  return;
80  }
81  */
82 
83  // Add request, sort by priority
84  int newPriority = request.m_iPriority;
85 
86  int idInsertAt = 0;
87  for (int i = m_aRequestQueue.Count()-1; i >= 0; i--)
88  {
89  int priority = m_aRequestQueue[idInsertAt].m_iPriority;
90 
91  if (newPriority > priority)
92  {
93  continue;
94  }
95  else
96  {
97  // Same priority or lower than this - add behind this request
98  idInsertAt = i+1;
99  break;
100  }
101  }
102 
103  if (idInsertAt == m_aRequestQueue.Count())
104  m_aRequestQueue.Insert(request);
105  else
106  m_aRequestQueue.InsertAt(request, idInsertAt);
107 
108  #ifdef AI_DEBUG
109  AddDebugMessage(string.Format(" Added to queue at: %1, new queue size: %2", idInsertAt, m_aRequestQueue.Count()));
110  #endif
111 
112  // We must be updated from now on
113  m_bNeedUpdate = true;
114  }
115 
116  //-------------------------------------------------------------------------------------------------------------
120  bool CanBypass(SCR_AITalkRequest request = null)
121  {
122  // Bypass if LOD is too high
123  if (m_Agent.GetLOD() != 0 || !m_Agent.GetControlComponent().IsAIActivated())
124  return true;
125 
126  // Bypass if there is no VoN component
127  if (!m_VoNComponent)
128  return true;
129 
130  // Bypass if noone's listening and the requests allows it
131  AIGroup myGroup = m_Agent.GetParentGroup();
132  if (request)
133  {
134  if ((!myGroup || myGroup.GetAgentsCount() <= 1) &&
135  !request.m_bTransmitIfNoReceivers)
136  return true;
137  if (!request.m_bTransmitIfPassenger)
138  {
139  ChimeraCharacter char = ChimeraCharacter.Cast(m_Agent.GetControlledEntity());
140  if (char && char.IsInVehicle())
141  {
142  CompartmentAccessComponent compAcc = char.GetCompartmentAccessComponent();
143  if (compAcc && !TurretCompartmentSlot.Cast(compAcc.GetCompartment()))
144  return true;
145  }
146  }
147  }
148 
149  // Bypass if we are a leader of slave group
150  SCR_AIGroup scrAiGroup = SCR_AIGroup.Cast(myGroup);
151  if (scrAiGroup && scrAiGroup.IsSlave() && scrAiGroup.GetLeaderAgent() == m_Agent)
152  return true;
153 
154  return false;
155  }
156 
157  //-------------------------------------------------------------------------------------------------------------
158  void SetMuted(bool mute)
159  {
160  #ifdef AI_DEBUG
161  AddDebugMessage(string.Format("SetMuted: %1", mute));
162  #endif
163 
164  if (mute)
165  {
166  if (m_eState == SCR_EAICommunicationState.TRANSMITTING || m_eState == SCR_EAICommunicationState.WAITING)
167  {
168  // Fail all requests
169  ClearAndFailAllRequests();
170 
171  m_bNeedUpdate = false;
172 
173  SwitchToState(SCR_EAICommunicationState.IDLE);
174  }
175  }
176 
177  m_bMuted = mute;
178  }
179 
180  //-------------------------------------------------------------------------------------------------------------
181  bool GetMuted()
182  {
183  return m_bMuted;
184  }
185 
186  //-------------------------------------------------------------------------------------------------------------
190  void SetSuspended(bool suspended)
191  {
192  #ifdef AI_DEBUG
193  AddDebugMessage(string.Format("SetSuspended: %1", suspended));
194  #endif
195 
196  if (suspended)
197  {
198  SwitchToState(SCR_EAICommunicationState.SUSPENDED);
199 
200  // Put current request into front of the queue, so we return to it later
201  if (m_CurrentRequest)
202  {
203  m_aRequestQueue.InsertAt(m_CurrentRequest, 0);
204  m_CurrentRequest = null;
205  }
206  }
207  else
208  {
209  if (m_eState == SCR_EAICommunicationState.SUSPENDED)
210  {
211  SwitchToState(SCR_EAICommunicationState.IDLE);
212  }
213  }
214  }
215 
216  //-------------------------------------------------------------------------------------------------------------
217  bool GetSuspended()
218  {
219  return m_eState == SCR_EAICommunicationState.SUSPENDED;
220  }
221 
222  //-------------------------------------------------------------------------------------------------------------
223  // UDPATE
224 
225  //-------------------------------------------------------------------------------------------------------------
226  void Update(float timeSlice)
227  {
228  switch (m_eState)
229  {
230  case SCR_EAICommunicationState.IDLE:
231  {
232  // Find top priority request which is still valid
233  SCR_AITalkRequest validRequest = FindValidRequest();
234 
235  // Found a valid request
236  if (validRequest)
237  {
238  m_CurrentRequest = validRequest;
239  TryTransmitAndSwitchState();
240  }
241 
242  break;
243  }
244 
245  case SCR_EAICommunicationState.WAITING:
246  {
247  // Is it still valid?
248  float currentTime_ms = GetGame().GetWorld().GetWorldTime();
249 
250  if (currentTime_ms - m_CurrentRequest.m_fCreatedTimestamp_ms >= m_CurrentRequest.m_fTimeout_ms)
251  {
252  // This request is not valid any more, forget it
253  FailRequest(m_CurrentRequest);
254  m_CurrentRequest = null;
255  SwitchToState(SCR_EAICommunicationState.IDLE);
256  }
257  else
258  {
259  // Still valid, check if we can talk
260  if (CanTransmit(m_CurrentRequest))
261  {
262  TransmitRequest(m_CurrentRequest);
263  SwitchToState(SCR_EAICommunicationState.TRANSMITTING);
264  m_fActiveTimer_ms = 0;
265  }
266  }
267 
268  break;
269  }
270 
271  case SCR_EAICommunicationState.TRANSMITTING:
272  {
273  m_fActiveTimer_ms += timeSlice;
274 
275  if (m_fActiveTimer_ms > SAMPLE_LENGTH_MS)
276  {
277  // Back to IDLE
278  CompleteRequest(m_CurrentRequest);
279  m_CurrentRequest = null;
280 
281  // Immediately try to find next request
282  SCR_AITalkRequest validRequest = FindValidRequest();
283 
284  if (validRequest)
285  {
286  m_CurrentRequest = validRequest;
287  if (m_CurrentRequest.m_bTransmitIfChannelBusy)
288  {
289  // If this request can be transmitted over busy channel, immediately start transmitting it,
290  // we want to keep channel busy, without making it free, so others don't transmit
291  TryTransmitAndSwitchState();
292  }
293  else
294  {
295  // If we can't transmit this over busy channel, free channel for one update, let others transmit
296  SwitchToState(SCR_EAICommunicationState.WAITING);
297  }
298  }
299  else
300  {
301  SwitchToState(SCR_EAICommunicationState.IDLE);
302  }
303  }
304 
305  break;
306  }
307  }
308 
309 
310  // Update me more if queue is not empty, or there is a current request
311  m_bNeedUpdate = !m_aRequestQueue.IsEmpty() || m_CurrentRequest;
312  }
313 
314  //-------------------------------------------------------------------------------------------------------------
316  void Reset()
317  {
318  #ifdef AI_DEBUG
319  AddDebugMessage("Reset");
320  #endif
321  ClearAndFailAllRequests();
322  SwitchToState(SCR_EAICommunicationState.IDLE);
323  }
324 
325  //-------------------------------------------------------------------------------------------------------------
326  // PROTECTED / PRIVATE
327 
328 
329  //-------------------------------------------------------------------------------------------------------------
330  // Finds highest priority valid request, removes invalid requests along the way
331  protected SCR_AITalkRequest FindValidRequest()
332  {
333  float currentTime_ms = GetGame().GetWorld().GetWorldTime();
334  SCR_AITalkRequest request;
335  for (int i = 0; i < m_aRequestQueue.Count(); i++)
336  {
337  SCR_AITalkRequest thisRequest = m_aRequestQueue[0];
338  if (currentTime_ms - thisRequest.m_fCreatedTimestamp_ms < thisRequest.m_fTimeout_ms)
339  {
340  request = thisRequest;
341  m_aRequestQueue.RemoveOrdered(0);
342  break;
343  }
344  else
345  {
346  FailRequest(thisRequest);
347  m_aRequestQueue.RemoveOrdered(0);
348  }
349  }
350 
351  return request;
352  }
353 
354  //-------------------------------------------------------------------------------------------------------------
356  protected void TryTransmitAndSwitchState()
357  {
358  if (CanBypass(m_CurrentRequest))
359  {
360  // Check if we can bypass this
361  CompleteRequest(m_CurrentRequest);
362  m_CurrentRequest = null;
363  SwitchToState(SCR_EAICommunicationState.IDLE);
364  }
365  else if (CanTransmit(m_CurrentRequest))
366  {
367  // Check if we can talk right now
368  TransmitRequest(m_CurrentRequest);
369  SwitchToState(SCR_EAICommunicationState.TRANSMITTING);
370  m_fActiveTimer_ms = 0;
371  }
372  else
373  {
374  // We'll be back soon!
375  SwitchToState(SCR_EAICommunicationState.WAITING);
376  }
377  }
378 
379 
380 
381  //-------------------------------------------------------------------------------------------------------------
382  // Transmission rules
383 
384  //-------------------------------------------------------------------------------------------------------------
386  protected bool CanTransmit(SCR_AITalkRequest request)
387  {
388  if (request.m_bTransmitIfChannelBusy)
389  {
390  // We can always transmit this request, don't care if channel is busy
391  return true;
392  }
393  else
394  {
395  return IsChannelFree();
396  }
397  }
398 
399  //-------------------------------------------------------------------------------------------------------------
402  protected bool IsChannelFree()
403  {
404  ChimeraWorld world = m_Entity.GetWorld();
405  if (!world)
406  return true;
407 
408  array<IEntity> entities = {};
409  TagSystem tm = TagSystem.Cast(world.FindSystem(TagSystem));
410  if (!tm)
411  {
412  Print("SCR_AICommsHandler: TagManager is not present in the world, AI comms might behave incorrect", LogLevel.ERROR);
413  return true;
414  }
415 
416  tm.GetTagsInRange(entities, m_Entity.GetOrigin(), NEARBY_SPEAKER_CHECK_RANGE, ETagCategory.NameTag);
417 
418  Faction myFaction;
419  if (m_FactionComp)
420  myFaction = m_FactionComp.GetAffiliatedFaction();
421  foreach (IEntity ent : entities)
422  {
423  SCR_ChimeraCharacter characterEnt = SCR_ChimeraCharacter.Cast(ent);
424  if (!characterEnt)
425  continue;
426 
427  // Ignore if different faction
428  if (characterEnt.m_pFactionComponent && (characterEnt.m_pFactionComponent.GetAffiliatedFaction() != myFaction))
429  continue;
430 
431  CharacterControllerComponent characterController = characterEnt.GetCharacterController();
432  AIControlComponent aiControlComponent = characterController.GetAIControlComponent();
433 
434  AIAgent agent = aiControlComponent.GetControlAIAgent();
435  SCR_ChimeraAIAgent chimeraAgent = SCR_ChimeraAIAgent.Cast(agent);
436  if (!chimeraAgent)
437  continue;
438 
439  SCR_AIUtilityComponent utility = chimeraAgent.m_UtilityComponent;
440 
441  if (utility.m_CommsHandler.m_eState == SCR_EAICommunicationState.TRANSMITTING)
442  return false;
443  }
444 
445  return true;
446  }
447 
448  // Transmission rules
449  //-------------------------------------------------------------------------------------------------------------
450 
451 
452 
453  //-------------------------------------------------------------------------------------------------------------
454  protected void SwitchToState(SCR_EAICommunicationState newState)
455  {
456  if (newState == m_eState)
457  return;
458 
459  #ifdef AI_DEBUG
460  AddDebugMessage(string.Format("SwitchToState: %1", typename.EnumToString(SCR_EAICommunicationState, newState)));
461  #endif
462 
464  m_eState = newState;
465  }
466 
467  //-------------------------------------------------------------------------------------------------------------
468  protected void TransmitRequest(SCR_AITalkRequest rq)
469  {
470  #ifdef AI_DEBUG
471  AddDebugMessage(string.Format("TransmitRequest: %1", rq.GetDebugString()));
472  #endif
473 
474  rq.m_eState = SCR_EAITalkRequestState.TRANSMITTING;
475  bool txSuccess = SCR_AISoundHandling.SetSignalsAndTransmit(rq, m_Entity, m_VoNComponent, m_SignalsManagerComponent);
476 
477  if (!txSuccess)
478  {
479  string str = string.Format("SCR_AISoundHandling.SetSignalsAndTransmit failed for request: %1", rq.GetDebugString());
480 
481  Print(string.Format("SCR_AICommsHandler: %1", str), LogLevel.ERROR);
482 
483  #ifdef AI_DEBUG
484  AddDebugMessage(str, LogLevel.ERROR);
485  #endif
486  }
487  }
488 
489  //-------------------------------------------------------------------------------------------------------------
490  protected void FailRequest(SCR_AITalkRequest rq)
491  {
492  #ifdef AI_DEBUG
493  AddDebugMessage(string.Format("FailRequest: %1", rq.GetDebugString()));
494  #endif
495  rq.m_eState = SCR_EAITalkRequestState.FAILED;
496  }
497 
498  //-------------------------------------------------------------------------------------------------------------
499  protected void CompleteRequest(SCR_AITalkRequest rq)
500  {
501  #ifdef AI_DEBUG
502  AddDebugMessage(string.Format("CompleteRequest: %1", rq.GetDebugString()));
503  #endif
504  rq.m_eState = SCR_EAITalkRequestState.COMPLETED;
505  }
506 
507  //-------------------------------------------------------------------------------------------------------------
509  protected void ClearAndFailAllRequests()
510  {
511  #ifdef AI_DEBUG
512  AddDebugMessage("ClearAndFailAllRequests");
513  #endif
514 
515  if (m_CurrentRequest)
516  FailRequest(m_CurrentRequest);
517  m_CurrentRequest = null;
518 
519  foreach (SCR_AITalkRequest request : m_aRequestQueue)
520  FailRequest(request);
521  m_aRequestQueue.Clear();
522 
523  SwitchToState(SCR_EAICommunicationState.IDLE);
524  }
525 
526  //--------------------------------------------------------------------------------------------
527  void EOnDiag(float timeSlice)
528  {
529  if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_COMMS_HANDLERS))
530  {
531  vector ownerPos = m_Entity.GetOrigin();
532  vector textPos = ownerPos + Vector(0, 4.5, 0);
533  int color = Color.GRAY;
534 
535  string str = string.Format("Queue: %1, State: %2",
536  m_aRequestQueue.Count(),
537  typename.EnumToString(SCR_EAICommunicationState, m_eState));
538 
539  if(m_CurrentRequest)
540  {
541  if (m_CurrentRequest.m_eState == SCR_EAITalkRequestState.TRANSMITTING)
542  color = Color.MAGENTA;
543  else
544  color = Color.ORANGE;
545 
546  str += string.Format("\n%1", typename.EnumToString(ECommunicationType, m_CurrentRequest.m_eCommType));
547  }
548 
549  DebugTextWorldSpace.Create(GetGame().GetWorld(), str, DebugTextFlags.ONCE | DebugTextFlags.CENTER | DebugTextFlags.FACE_CAMERA,
550  textPos[0], textPos[1], textPos[2], color: color, bgColor: Color.BLACK,
551  size: 13.0);
552  }
553  }
554 
555  //--------------------------------------------------------------------------------------------
556  void ~SCR_AICommsHandler()
557  {
558  ClearAndFailAllRequests();
559  }
560 
561  #ifdef AI_DEBUG
562  //--------------------------------------------------------------------------------------------
563  protected void AddDebugMessage(string str, LogLevel logLevel = LogLevel.NORMAL)
564  {
565  if (!m_Agent)
566  return;
567  SCR_AIInfoBaseComponent infoComp = SCR_AIInfoBaseComponent.Cast(m_Agent.FindComponent(SCR_AIInfoBaseComponent));
568  if (!infoComp)
569  return;
570 
571  infoComp.AddDebugMessage(str, msgType: EAIDebugMsgType.COMMS);
572  }
573  #endif
574 }
ChimeraWorld
Definition: ChimeraWorld.c:12
SUSPENDED
@ SUSPENDED
Definition: SCR_AICommsHandler.c:16
EAIDebugMsgType
EAIDebugMsgType
Definition: SCR_AIDebugMessage.c:1
m_Agent
SCR_ChimeraAIAgent m_Agent
Definition: SCR_AIActivitySmokeCoverFeature.c:42
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
SCR_AICommsStateChangedDelegate
func SCR_AICommsStateChangedDelegate
Definition: SCR_AICommsHandler.c:8
TRANSMITTING
@ TRANSMITTING
Definition: SCR_AICommsHandler.c:15
m_Entity
enum EAITargetInfoCategory m_Entity
func
func
Definition: SCR_AIThreatSystem.c:5
WAITING
@ WAITING
Definition: SCR_AICommsHandler.c:14
TagSystem
Definition: TagSystem.c:12
ETagCategory
ETagCategory
Definition: ETagCategory.c:1
m_eState
EAITargetClusterState m_eState
Definition: SCR_AITargetClusterState.c:24
SCR_AIOnCommsStateChangedInvoker
ScriptInvokerBase< SCR_AICommsStateChangedDelegate > SCR_AIOnCommsStateChangedInvoker
Definition: SCR_AICommsHandler.c:9
SCR_AITalkRequest
void SCR_AITalkRequest(ECommunicationType type, IEntity entity, vector pos, int enumSignal, bool transmitIfNoReceivers, bool transmitIfPassenger, SCR_EAITalkRequestPreset preset)
Definition: SCR_AITalkRequest.c:37
SCR_ChimeraAIAgent
Definition: SCR_ChimeraAIAgent.c:5
TurretCompartmentSlot
Definition: TurretCompartmentSlot.c:12
ECommunicationType
ECommunicationType
Definition: SCR_AISoundHandling.c:1
SCR_EAICommunicationState
SCR_EAICommunicationState
Definition: SCR_AICommsHandler.c:11
IDLE
@ IDLE
Definition: SCR_AICommsHandler.c:13
Faction
Definition: Faction.c:12
m_SignalsManagerComponent
protected SignalsManagerComponent m_SignalsManagerComponent
Definition: SCR_SignalsDebugComponent.c:22
SCR_AIGroup
Definition: SCR_AIGroup.c:68
SCR_AICommsHandler
Definition: SCR_AICommsHandler.c:19
IsInVehicle
proto external bool IsInVehicle()
Returns true if the character is inside a vehicle.
SCR_DebugMenuID
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition: DebugMenuID.c:3
SCR_EAITalkRequestState
SCR_EAITalkRequestState
Definition: SCR_AITalkRequest.c:1