Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AICommsHandler.c
Go to the documentation of this file.
1
6
9typedef 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
19class 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
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 //-------------------------------------------------------------------------------------------------------------
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 {
167 {
168 // Fail all requests
170
171 m_bNeedUpdate = false;
172
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 {
199
200 // Put current request into front of the queue, so we return to it later
202 {
204 m_CurrentRequest = null;
205 }
206 }
207 else
208 {
209 if (m_eState == SCR_EAICommunicationState.SUSPENDED)
210 {
212 }
213 }
214 }
215
216 //-------------------------------------------------------------------------------------------------------------
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 {
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;
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
254 m_CurrentRequest = null;
256 }
257 else
258 {
259 // Still valid, check if we can talk
261 {
265 }
266 }
267
268 break;
269 }
270
271 case SCR_EAICommunicationState.TRANSMITTING:
272 {
273 m_fActiveTimer_ms += timeSlice;
274
276 {
277 // Back to IDLE
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
292 }
293 else
294 {
295 // If we can't transmit this over busy channel, free channel for one update, let others transmit
297 }
298 }
299 else
300 {
302 }
303 }
304
305 break;
306 }
307 }
308
309
310 // Update me more if queue is not empty, or there is a current request
312 }
313
314 //-------------------------------------------------------------------------------------------------------------
316 void Reset()
317 {
318 #ifdef AI_DEBUG
319 AddDebugMessage("Reset");
320 #endif
323 }
324
325 //-------------------------------------------------------------------------------------------------------------
326 // PROTECTED / PRIVATE
327
328
329 //-------------------------------------------------------------------------------------------------------------
330 // Finds highest priority valid request, removes invalid requests along the way
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 //-------------------------------------------------------------------------------------------------------------
357 {
359 {
360 // Check if we can bypass this
362 m_CurrentRequest = null;
364 }
366 {
367 // Check if we can talk right now
371 }
372 else
373 {
374 // We'll be back soon!
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 if (!aiControlComponent)
435 continue;
436
437 AIAgent agent = aiControlComponent.GetControlAIAgent();
438 SCR_ChimeraAIAgent chimeraAgent = SCR_ChimeraAIAgent.Cast(agent);
439 if (!chimeraAgent)
440 continue;
441
442 SCR_AIUtilityComponent utility = chimeraAgent.m_UtilityComponent;
443
444 if (utility.m_CommsHandler.m_eState == SCR_EAICommunicationState.TRANSMITTING)
445 return false;
446 }
447
448 return true;
449 }
450
451 // Transmission rules
452 //-------------------------------------------------------------------------------------------------------------
453
454
455
456 //-------------------------------------------------------------------------------------------------------------
458 {
459 if (newState == m_eState)
460 return;
461
462 #ifdef AI_DEBUG
463 AddDebugMessage(string.Format("SwitchToState: %1", typename.EnumToString(SCR_EAICommunicationState, newState)));
464 #endif
465
467 m_eState = newState;
468 }
469
470 //-------------------------------------------------------------------------------------------------------------
472 {
473 #ifdef AI_DEBUG
474 AddDebugMessage(string.Format("TransmitRequest: %1", rq.GetDebugString()));
475 #endif
476
477 rq.m_eState = SCR_EAITalkRequestState.TRANSMITTING;
478 bool txSuccess = SCR_AISoundHandling.SetSignalsAndTransmit(rq, m_Entity, m_VoNComponent, m_SignalsManagerComponent);
479
480 if (!txSuccess)
481 {
482 string str = string.Format("SCR_AISoundHandling.SetSignalsAndTransmit failed for request: %1", rq.GetDebugString());
483
484 Print(string.Format("SCR_AICommsHandler: %1", str), LogLevel.ERROR);
485
486 #ifdef AI_DEBUG
487 AddDebugMessage(str, LogLevel.ERROR);
488 #endif
489 }
490 }
491
492 //-------------------------------------------------------------------------------------------------------------
494 {
495 #ifdef AI_DEBUG
496 AddDebugMessage(string.Format("FailRequest: %1", rq.GetDebugString()));
497 #endif
498 rq.m_eState = SCR_EAITalkRequestState.FAILED;
499 }
500
501 //-------------------------------------------------------------------------------------------------------------
503 {
504 #ifdef AI_DEBUG
505 AddDebugMessage(string.Format("CompleteRequest: %1", rq.GetDebugString()));
506 #endif
507 rq.m_eState = SCR_EAITalkRequestState.COMPLETED;
508 }
509
510 //-------------------------------------------------------------------------------------------------------------
512 protected void ClearAndFailAllRequests()
513 {
514 #ifdef AI_DEBUG
515 AddDebugMessage("ClearAndFailAllRequests");
516 #endif
517
520 m_CurrentRequest = null;
521
522 foreach (SCR_AITalkRequest request : m_aRequestQueue)
523 FailRequest(request);
524 m_aRequestQueue.Clear();
525
527 }
528
529 //--------------------------------------------------------------------------------------------
530 void EOnDiag(float timeSlice)
531 {
532 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_COMMS_HANDLERS))
533 {
534 vector ownerPos = m_Entity.GetOrigin();
535 vector textPos = ownerPos + Vector(0, 4.5, 0);
536 int color = Color.GRAY;
537
538 string str = string.Format("Queue: %1, State: %2",
539 m_aRequestQueue.Count(),
540 typename.EnumToString(SCR_EAICommunicationState, m_eState));
541
543 {
544 if (m_CurrentRequest.m_eState == SCR_EAITalkRequestState.TRANSMITTING)
545 color = Color.MAGENTA;
546 else
547 color = Color.ORANGE;
548
549 str += string.Format("\n%1", typename.EnumToString(ECommunicationType, m_CurrentRequest.m_eCommType));
550 }
551
552 DebugTextWorldSpace.Create(GetGame().GetWorld(), str, DebugTextFlags.ONCE | DebugTextFlags.CENTER | DebugTextFlags.FACE_CAMERA,
553 textPos[0], textPos[1], textPos[2], color: color, bgColor: Color.BLACK,
554 size: 13.0);
555 }
556 }
557
558 //--------------------------------------------------------------------------------------------
563
564 #ifdef AI_DEBUG
565 //--------------------------------------------------------------------------------------------
566 protected void AddDebugMessage(string str, LogLevel logLevel = LogLevel.NORMAL)
567 {
568 if (!m_Agent)
569 return;
570 SCR_AIInfoBaseComponent infoComp = SCR_AIInfoBaseComponent.Cast(m_Agent.FindComponent(SCR_AIInfoBaseComponent));
571 if (!infoComp)
572 return;
573
574 infoComp.AddDebugMessage(str, msgType: EAIDebugMsgType.COMMS);
575 }
576 #endif
577}
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
ETagCategory
Definition ETagCategory.c:2
ArmaReforgerScripted GetGame()
Definition game.c:1398
int size
SCR_ChimeraAIAgent m_Agent
SCR_EAICommunicationState
@ SUSPENDED
@ WAITING
@ TRANSMITTING
ScriptInvokerBase< SCR_AICommsStateChangedDelegate > SCR_AIOnCommsStateChangedInvoker
func SCR_AICommsStateChangedDelegate
EAIDebugMsgType
SCR_AIGroupInfoComponentClass IDLE
Group has no waypoints and does not engage an enemy.
ECommunicationType
SCR_EAITalkRequestState
void SCR_AITalkRequest(ECommunicationType type, IEntity entity, vector pos, int enumSignal, bool transmitIfNoReceivers, bool transmitIfPassenger, SCR_EAITalkRequestPreset preset)
Definition Color.c:13
Diagnostic and developer menu system.
Definition DiagMenu.c:18
void TransmitRequest(SCR_AITalkRequest rq)
void SwitchToState(SCR_EAICommunicationState newState)
void CompleteRequest(SCR_AITalkRequest rq)
void ClearAndFailAllRequests()
Clears and fails all requests.
void SetSuspended(bool suspended)
void TryTransmitAndSwitchState()
Tries to transmit, if succeedes, switches to ACTIVE state, otherwise to WAITING state.
void Reset()
Fails and clears all requests, resets to initial state.
ref ref SCR_AITalkRequest m_CurrentRequest
void SetMuted(bool mute)
void AddRequest(SCR_AITalkRequest request)
bool CanBypass(SCR_AITalkRequest request=null)
void SCR_AICommsHandler(notnull IEntity entity, notnull AIAgent agent)
ref array< ref SCR_AITalkRequest > m_aRequestQueue
bool CanTransmit(SCR_AITalkRequest request)
Checks if we can transmit with relation to channel state.
void EOnDiag(float timeSlice)
void FailRequest(SCR_AITalkRequest rq)
void Update(float timeSlice)
SCR_AITalkRequest FindValidRequest()
const float NEARBY_SPEAKER_CHECK_RANGE
SCR_EAICommunicationState m_eState
bool IsSlave()
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
DebugTextFlags
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
proto native vector Vector(float x, float y, float z)