Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_InteractionHandlerComponent.c
Go to the documentation of this file.
1 class SCR_InteractionHandlerComponentClass: InteractionHandlerComponentClass
2 {
3 }
4 
7 enum SCR_NearbyContextDisplayMode
8 {
9  DISABLED = 0,
10  ALWAYS_ON = 1,
11  ON_INPUT_ACTION = 2,
12  ON_FREELOOK = 3
13 }
14 
20 class SCR_InteractionHandlerComponent : InteractionHandlerComponent
21 {
23  protected SCR_BaseInteractionDisplay m_pDisplay;
24 
25  [Attribute("3", UIWidgets.ComboBox, "Display mode", "", ParamEnumArray.FromEnum(SCR_NearbyContextDisplayMode), category: "Nearby Context Properties" )]
26  protected SCR_NearbyContextDisplayMode m_eDisplayMode;
27 
28  [Attribute("", UIWidgets.EditBox, "Action to listen for when SCR_NearbyContextDisplayMode is set to ON_INPUT_ACTION", category: "Nearby Context Properties")]
29  protected string m_sActionName;
30 
31  [Attribute("", UIWidgets.EditBox, "Context to activate when SCR_NearbyContextDisplayMode is set to ON_INPUT_ACTION. Mustn't be empty to be activated.", category: "Nearby Context Properties")]
32  protected string m_sActionContext;
33 
35  protected UserActionContext m_pLastContext;
36 
38  protected BaseUserAction m_pLastUserAction;
39 
41  protected int m_iSelectedActionIndex;
42 
43  protected bool m_bIsPerforming;
44  protected bool m_bPerformAction;
45  protected bool m_bLastInput;
46  protected float m_fSelectAction;
47  protected float m_fCurrentProgress;
48 
50  protected ref array<IEntity> m_aInspectedEntities = {};
51 
52  protected IEntity m_ControlledEntity;
53 
54  //------------------------------------------------------------------------------------------------
56  protected void RegisterActionListeners()
57  {
58  InputManager pInputManager = GetGame().GetInputManager();
59  if (!pInputManager)
60  return;
61 
62  m_bPerformAction = false;
63  m_fSelectAction = 0;
64  pInputManager.AddActionListener("PerformAction", EActionTrigger.DOWN, ActionPerform);
65  pInputManager.AddActionListener("PerformAction", EActionTrigger.UP, ActionPerform);
66  pInputManager.AddActionListener("SelectAction", EActionTrigger.VALUE, ActionScroll);
67  }
68 
69  //------------------------------------------------------------------------------------------------
70  protected void RemoveActionListeners()
71  {
72  InputManager pInputManager = GetGame().GetInputManager();
73  if (!pInputManager)
74  return;
75 
76  m_bPerformAction = false;
77  m_fSelectAction = 0;
78  pInputManager.RemoveActionListener("PerformAction", EActionTrigger.DOWN, ActionPerform);
79  pInputManager.RemoveActionListener("PerformAction", EActionTrigger.UP, ActionPerform);
80  pInputManager.RemoveActionListener("SelectAction", EActionTrigger.VALUE, ActionScroll);
81  }
82 
83  //------------------------------------------------------------------------------------------------
85  void ActionPerform(float value, EActionTrigger reason)
86  {
87  m_bPerformAction = reason == EActionTrigger.DOWN;
88  }
89 
90  //------------------------------------------------------------------------------------------------
91  void ActionScroll(float value, EActionTrigger reason)
92  {
93  if (value == 0)
94  return;
95 
96  m_fSelectAction = value;
97  }
98 
99  //------------------------------------------------------------------------------------------------
103  // HACK: this is added to SCR_PlayerController.OnControlledEntityChanged by SCR_PlayerController.EOnInit() because InteractionHandlerComponent.OnInit() is only called by the host
104  void OnControlledEntityChanged(IEntity from, IEntity to)
105  {
106  PlayerController controller = GetGame().GetPlayerController();
107  if (!controller)
108  return;
109 
110  if (controller.FindComponent(SCR_InteractionHandlerComponent) != this)
111  return;
112 
113  if (from)//Was an owner of the entity that contians this component but that is changing now
115 
116  if (to && to == SCR_PlayerController.GetLocalControlledEntity())//Became an owner of the entity that has this component
117  RegisterActionListeners();
118  }
119 
120  //------------------------------------------------------------------------------------------------
121  override void OnInit(IEntity owner)
122  {
123  DiagMenu.RegisterBool(SCR_DebugMenuID.DEBUGUI_INTERACTION_SKIP_DURATION, "", "Skip action duration", "User Actions");
124  }
125 
126  //------------------------------------------------------------------------------------------------
127  protected SCR_BaseInteractionDisplay FindDisplay(IEntity owner)
128  {
129  PlayerController playerController = PlayerController.Cast(owner);
130  if (!playerController)
131  {
132  Print("InteractionHandler must be attached to a PlayerController!", LogLevel.ERROR);
133  return null;
134  }
135 
136  HUDManagerComponent hudManager = HUDManagerComponent.Cast(playerController.FindComponent(HUDManagerComponent));
137  array<BaseInfoDisplay> displayInfos = {};
138  int count = hudManager.GetInfoDisplays(displayInfos);
139  for (int i = 0; i < count; i++)
140  {
141  SCR_BaseInteractionDisplay current = SCR_BaseInteractionDisplay.Cast(displayInfos[i]);
142  if (current)
143  return current;
144  }
145 
146  Print("InteractionDisplay not found! InteractionDisplay must be stored in HUDManagerComponent of a PlayerController!", LogLevel.WARNING);
147  return null;
148  }
149 
150  //------------------------------------------------------------------------------------------------
159  protected void DoProcessInteraction(
160  ChimeraCharacter user,
161  UserActionContext context,
162  BaseUserAction action,
163  bool canPerform,
164  bool performInput,
165  float timeSlice,
167  {
168  if (action)
169  action.SetActiveContext(context);
170 
171  // Can action be performed?
172  bool isOk = action && canPerform && action == m_pLastUserAction;
173  // We want to perform and action is OK
174  if (performInput && isOk)
175  {
176  // We want to be performing, but we're not yet.
177  // Start the action and dispatch events.
178  if (!m_bIsPerforming && !m_bLastInput)
179  {
180  if (!GetCanInteractScript(user))
181  return;
182 
183  // UI
184  if (display)
185  display.OnActionStart(user, action);
186 
187  // Start the action. Calls action.OnActionStart
188  user.DoStartObjectAction(action);
189 
190  // Set state
191  m_bIsPerforming = true;
192  m_fCurrentProgress = 0.0;
193  }
194  // We want to perform and we already started performing,
195  // update continuous handler state until we're finished
196  else if (m_bIsPerforming)
197  {
198  if (DiagMenu.GetValue(SCR_DebugMenuID.DEBUGUI_INTERACTION_SKIP_DURATION))
199  timeSlice += Math.AbsFloat(action.GetActionDuration());
200 
201  // Update elapsed time
202  ScriptedSignalUserAction signalUserAction = ScriptedSignalUserAction.Cast(action);
203  SCR_ScriptedUserAction scriptedUserAction = SCR_ScriptedUserAction.Cast(action);
204  if (signalUserAction)
205  {
206  SCR_AdjustSignalAction adjustAction = SCR_AdjustSignalAction.Cast(signalUserAction);
207  if (adjustAction && !adjustAction.IsManuallyAdjusted())
208  m_fCurrentProgress += timeSlice;
209  else
210  m_fCurrentProgress = signalUserAction.GetActionProgress();
211  }
212  else if (scriptedUserAction)
213  {
214  if (DiagMenu.GetValue(SCR_DebugMenuID.DEBUGUI_INTERACTION_SKIP_DURATION))
215  timeSlice += scriptedUserAction.GetLoopActionHoldDuration();
216 
217  m_fCurrentProgress = scriptedUserAction.GetActionProgress(m_fCurrentProgress, timeSlice);
218  }
219  else
220  m_fCurrentProgress += timeSlice;
221 
222  // Tick action
223  if (action.ShouldPerformPerFrame())
224  user.DoPerformContinuousObjectAction(action, timeSlice);
225 
226  // Get action duration
227  float duration = action.GetActionDuration();
228 
229  // Update UI
230  if (display)
231  display.OnActionProgress(user, action, m_fCurrentProgress, Math.AbsFloat(duration));
232 
233  // We are finished, dispatch events and reset state
234  if (m_fCurrentProgress >= duration && duration >= 0)
235  {
236  // Update UI
237  if (display)
238  display.OnActionFinish(user, action, ActionFinishReason.FINISHED);
239 
240  // Finally perform action
241  if (!action.ShouldPerformPerFrame())
242  user.DoPerformObjectAction(action);
243 
244  // Reset state
245  m_fCurrentProgress = 0.0;
246  m_bIsPerforming = false;
247  }
248  }
249  }
250  else
251  {
252  // Input was released, we were performing previously,
253  // stop performing and dispatch necessary events.
254  if (m_bIsPerforming)
255  {
256  // Update UI
257  if (display)
258  display.OnActionFinish(user, action, ActionFinishReason.INTERRUPTED);
259 
260  // Cancel the action. Calls action.OnActionCanceled
261  user.DoCancelObjectAction(action);
262 
263  // Reset state
264  m_bIsPerforming = false;
265  m_fCurrentProgress = 0.0;
266  }
267  }
268  }
269 
270  //------------------------------------------------------------------------------------------------
271  protected override bool GetCanInteractScript(IEntity controlledEntity)
272  {
273  ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
274  if (!character)
275  return false;
276 
277  // No interactions when menu is open
278  MenuManager menuManager = GetGame().GetMenuManager();
279  if (menuManager && menuManager.IsAnyMenuOpen())
280  return false;
281 
282  SCR_CharacterControllerComponent characterController = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
283  if (characterController && !characterController.CanInteract())
284  return false;
285 
286  return true;
287  }
288 
289  //------------------------------------------------------------------------------------------------
290  protected override bool GetIsInteractionAvailableScript()
291  {
292  return IsContextAvailable();
293  }
294 
295  //------------------------------------------------------------------------------------------------
296  protected override BaseUserAction GetSelectedActionScript()
297  {
298  return m_pLastUserAction;
299  }
300 
301  //------------------------------------------------------------------------------------------------
302  protected override bool DoIntersectCheck(IEntity controlledEntity)
303  {
304  if (!controlledEntity)
305  return false;
306 
307  ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
308  if (!character)
309  return false;
310 
311  if (character.IsInVehicle())
312  return true;
313 
314  if (character.GetCharacterController().GetInspect())
315  return true;
316 
317  return false;
318  }
319 
320  //------------------------------------------------------------------------------------------------
321  protected override void OnContextChanged(UserActionContext previousContext, UserActionContext newContext)
322  {
323  // Changed, so hide previous
324  if (!newContext || newContext != GetCurrentContext())
325  {
326  m_iSelectedActionIndex = 0;
327  if (m_pDisplay)
328  m_pDisplay.HideDisplay();
329  }
330 
331  // Changed, so show new
332  if (m_pDisplay && newContext)
333  m_pDisplay.ShowDisplay();
334  }
335 
336  //------------------------------------------------------------------------------------------------
337  protected override event bool CanContextChange(UserActionContext currentContext, UserActionContext newContext)
338  {
339  // Setting null context might be desirable in certain cases when state should be cleared, see below:
340  if (!newContext)
341  {
342  // Allow clearing if no entity is controlled, to prevent leaking of contexts
343  if (!m_ControlledEntity)
344  return true;
345 
346  // Allow clearing of context if controlled entity is destroyed, at this point interaction should begone
347  DamageManagerComponent dmg = DamageManagerComponent.Cast(m_ControlledEntity.FindComponent(DamageManagerComponent));
348  if (dmg && dmg.IsDestroyed())
349  return true;
350 
351  // Otherwise continue with the usual
352  }
353 
354  // Check whether we are still in range
355  if (currentContext && m_ControlledEntity)
356  {
357  // We will leave a small error threshold
358  const float threshold = 1.1;
359  // Global action visibility range in meters
360  float visRange = GetVisibilityRange();
361  // Maximum sq distance we can interact at
362  float maxSqDistance = (visRange * visRange) * 1.1;
363  // Sq distance to controlled entity
364  float sqDistance = vector.DistanceSq(currentContext.GetOrigin(), m_ControlledEntity.GetOrigin());
365 
366  if (sqDistance > maxSqDistance)
367  {
368  // We are out of range, context can change safely
369  return true;
370  }
371  }
372 
373  // Suppress context changing when we are interacting with one already
374  if (currentContext && m_bIsPerforming)
375  {
376  // TODO: Validate distance to ctx
377  return false;
378  }
379 
380  return true;
381  }
382 
383  //------------------------------------------------------------------------------------------------
386  protected bool ShouldBeEnabledInFreelook(ChimeraCharacter character)
387  {
388  if (!character)
389  return false;
390 
391  CharacterControllerComponent controller = character.GetCharacterController();
392  if (!controller)
393  return false;
394 
395  // Inspection is priority
396  if (controller.GetInspect())
397  return true;
398 
399  CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent();
400  // Hide blips when in TPP while in the vehicle
401  if (compartmentAccess && compartmentAccess.IsInCompartment() && controller.IsInThirdPersonView())
402  return false;
403 
404  // When freelook is enabled manually, enable the display
405  if (!controller.IsFreeLookEnforced() && controller.IsFreeLookEnabled())
406  return true;
407 
408  // When forced, avoid displaying in certain cases
409  // Suppress display when getting in our out
410  if (compartmentAccess)
411  {
412  if (compartmentAccess.IsGettingIn() || compartmentAccess.IsGettingOut())
413  return false;
414  }
415 
416  // Supress display when in a vehicle while in 3rd person
417  if (character.IsInVehicle())
418  {
419  if (controller.IsInThirdPersonView())
420  return false;
421  }
422 
423  // Supress display when falling
424  if (controller.IsFalling())
425  return false;
426 
427  // Or climbing
428  if (controller.IsClimbing())
429  return false;
430 
431  // Or unconscious
432  if (controller.IsUnconscious())
433  return false;
434 
435  return true;
436  }
437 
438  //------------------------------------------------------------------------------------------------
439  protected bool ShouldBeEnabled(SCR_NearbyContextDisplayMode displayMode, ChimeraCharacter character, bool playerCameraOnly = true)
440  {
441  // Disallow when character is none
442  if (!character)
443  return false;
444 
445  // Disallow out of player camera when true
446  if (playerCameraOnly)
447  {
448  CameraManager cameraManager = GetGame().GetCameraManager();
449  if (cameraManager && !PlayerCamera.Cast(cameraManager.CurrentCamera()))
450  return false;
451  }
452 
453  // Handle different display mode cases
454  switch (displayMode)
455  {
456  // Always off
457  case SCR_NearbyContextDisplayMode.DISABLED:
458  return false;
459 
460  // Always on
461  case SCR_NearbyContextDisplayMode.ALWAYS_ON:
462  return true;
463 
464  // On action
465  case SCR_NearbyContextDisplayMode.ON_INPUT_ACTION:
466  {
467  if (m_sActionName.IsEmpty())
468  return false;
469 
470  InputManager inputManager = GetGame().GetInputManager();
471  if (!m_sActionContext.IsEmpty())
472  inputManager.ActivateContext(m_sActionContext);
473 
474  return inputManager.GetActionValue(m_sActionName) > 0;
475  }
476 
477  // When in freelook
478  case SCR_NearbyContextDisplayMode.ON_FREELOOK:
479  {
480  if (!ShouldBeEnabledInFreelook(character))
481  return false;
482 
483  return character.GetCharacterController().IsFreeLookEnabled();
484  }
485  }
486 
487  // Nope, sorry.
488  return false;
489  }
490 
491  //------------------------------------------------------------------------------------------------
492  override array<IEntity> GetManualOverrideList(IEntity owner, out vector referencePoint)
493  {
494  CameraManager cameraManager = GetGame().GetCameraManager();
495  if (cameraManager)
496  {
497  CameraBase camera = cameraManager.CurrentCamera();
498  vector rayDir = camera.GetWorldTransformAxis(2);
499  vector rayStart = camera.GetOrigin();
500  referencePoint = rayStart + rayDir;
501 
502  // Inspection correction
503  ChimeraCharacter character = ChimeraCharacter.Cast(m_ControlledEntity);
504  if (character)
505  {
506  // During inspection (of a weapon)
507  CharacterControllerComponent controller = character.GetCharacterController();
508  if (controller.GetInspect() && controller.GetInspectCurrentWeapon())
509  {
510  // Assume that while in inspection, weapon is tilted and its
511  // left side is pointed towards the player camera
512  IEntity inspectedEntity = controller.GetInspectEntity();
513 
514  vector origin = inspectedEntity.GetOrigin();
515  vector normal = -inspectedEntity.GetWorldTransformAxis(0);
516 
517  referencePoint = SCR_Math3D.IntersectPlane(rayStart, rayDir, origin, normal);
518  // Shape.CreateSphere(COLOR_RED, ShapeFlags.ONCE, referencePoint, 0.01);
519  }
520  }
521  }
522  else
523  {
524  referencePoint = vector.Zero;
525  }
526 
527  return m_aInspectedEntities;
528  }
529 
530  //------------------------------------------------------------------------------------------------
531  protected void HandleInspection(notnull ChimeraCharacter character, float timeSlice)
532  {
533  if (character.GetCharacterController().GetInspect())
534  {
536  m_aInspectedEntities.Clear();
537 
538  // Weapon is the priority if inspected, including all attachements
539  CharacterControllerComponent ctrlComp = character.GetCharacterController();
540  if (ctrlComp.GetInspectCurrentWeapon())
541  {
542  // Insert all items we can be interested in
543  BaseWeaponManagerComponent weaponManager = BaseWeaponManagerComponent.Cast(character.FindComponent(BaseWeaponManagerComponent));
544  if (weaponManager)
545  {
546  BaseWeaponComponent weapon = weaponManager.GetCurrentWeapon();
547  if (weapon)
548  {
549  m_aInspectedEntities.Insert(weapon.GetOwner());
550 
551  array<AttachmentSlotComponent> attachments = {};
552  weapon.GetAttachments(attachments);
553 
554  foreach (AttachmentSlotComponent attachment : attachments)
555  {
556  IEntity attachedEntity = attachment.GetAttachedEntity();
557  if (attachedEntity)
558  m_aInspectedEntities.Insert(attachedEntity);
559  }
560 
561  BaseMagazineComponent magazineComp = weapon.GetCurrentMagazine();
562  if (magazineComp)
563  m_aInspectedEntities.Insert(magazineComp.GetOwner());
564  }
565  }
566  }
567  else
568  {
569  // Whatever else is inspected kicks in
570  IEntity inspectedItem = ctrlComp.GetInspectEntity();
571  if (inspectedItem)
572  {
573  m_aInspectedEntities.Insert(inspectedItem);
574  }
575  }
576  }
577  else
578  {
580  }
581  }
582 
583  //------------------------------------------------------------------------------------------------
584  protected override void OnPostFrame(IEntity owner, IEntity controlledEntity, float timeSlice)
585  {
586  // TODO@AS: Add a reliable init method and get rid of this monstrosity
587  if (!m_pDisplay)
588  m_pDisplay = FindDisplay(owner);
589 
590  m_ControlledEntity = controlledEntity;
591  // Make sure we have a valid character
592  ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
593 
594  // Nearby context collection?
595  bool enableNearbyCollection = ShouldBeEnabled(m_eDisplayMode, character, true);
596  SetNearbyCollectionEnabled(enableNearbyCollection);
597 
598  // Make sure we have a valid character
599  if (!character)
600  return;
601 
602  HandleInspection(character, timeSlice);
603 
604  UserActionContext currentContext = GetCurrentContext();
605  if (currentContext)
606  {
607  array<BaseUserAction> actions = {};
608  array<bool> canPerform = {};
609  int count = GetFilteredActions(actions, canPerform);
610  if (count > 0)
611  GetGame().GetInputManager().ActivateContext("ActionMenuContext");
612 
613  foreach (BaseUserAction action : actions)
614  {
615  if (m_pDisplay)
616  m_pDisplay.OnActionProgress(character, action, action.GetActionProgress(), action.GetActionDuration());
617  }
618 
619  AggregateActions(actions, canPerform);
620 
621  // First of all, prior to doing any destructive changes,
622  // find the previous selected action (if any) and
623  // update the index, in case it has been shuffled.
624  if (m_pLastUserAction)
625  {
626  BaseUserAction action;
627  for (int i = 0, ac = actions.Count(); i < ac; i++)
628  {
629  action = actions[i];
630  if (action && action == m_pLastUserAction)
631  {
632  m_iSelectedActionIndex = i;
633  break;
634  }
635  }
636  }
637 
638  // Update selection
639  int iScrollAmount = 0;
640  int prevActionIndex = m_iSelectedActionIndex;
641  // But only if player is not performing an action already
642  if (!m_bIsPerforming)
643  {
644  if (Math.AbsFloat(m_fSelectAction) > 0.5)
645  iScrollAmount = Math.Clamp(m_fSelectAction, -1.0, 1.0);
646 
647  if (iScrollAmount != 0)
648  m_iSelectedActionIndex = m_iSelectedActionIndex - iScrollAmount;
649  }
650 
651  // Make sure that selected action is always within bounds
652  int actionsCount = actions.Count();
653  m_iSelectedActionIndex = Math.Clamp(m_iSelectedActionIndex, 0, actionsCount - 1);
654 
655  BaseUserAction selectedAction = null;
656  bool canPerformSelectedAction = false;
657 
658  if (m_bIsPerforming)
659  {
660  selectedAction = m_pLastUserAction;
661  if (actions.Count() > prevActionIndex && actions[prevActionIndex] == m_pLastUserAction)
662  canPerformSelectedAction = canPerform[prevActionIndex];
663  else if (m_pLastUserAction)
664  canPerformSelectedAction = m_pLastUserAction.CanBeShown(character) && m_pLastUserAction.CanBePerformed(character);
665  }
666  else if (actionsCount > 0)
667  {
668  selectedAction = actions[m_iSelectedActionIndex];
669  canPerformSelectedAction = canPerform[m_iSelectedActionIndex];
670  }
671 
672  // Process interaction
673  if (selectedAction)
674  canPerformSelectedAction = canPerformSelectedAction && selectedAction.CanBePerformed(character); // need to call this because the data from GetFilteredActions might not be up to date
675 
676  DoProcessInteraction(character, currentContext, selectedAction, canPerformSelectedAction, m_bPerformAction, timeSlice, m_pDisplay);
677  m_pLastUserAction = selectedAction;
678  SetSelectedAction(selectedAction);
679 
680  // Pass data to display
681  if (m_pDisplay)
682  {
683  ActionsTuple pData = new ActionsTuple();
684  bool canInteract = GetCanInteractScript(character);
685 
686  if (canInteract || m_bIsPerforming)
687  {
688  pData.param1 = actions;
689  pData.param2 = canPerform;
690  }
691  else
692  {
693  pData.Init();
694  }
695 
696  ActionDisplayData pDisplayData = new ActionDisplayData();
697  pDisplayData.pUser = controlledEntity;
698  pDisplayData.pActionsData = pData;
699  pDisplayData.pSelectedAction = selectedAction;
700  pDisplayData.pCurrentContext = currentContext;
701 
702  m_pDisplay.SetDisplayData(pDisplayData);
703  }
704  }
705  // We don't have a context, but we possibly had, thus reset
706  // our current action and make sure to dispatch events.
707  else if (m_pLastContext != currentContext)
708  {
709  // We had valid action
710  if (m_pLastUserAction)
711  {
712  // And we were performing it
713  if (m_bIsPerforming)
714  {
715  // Reset state
716  m_bIsPerforming = false;
717  m_fCurrentProgress = 0.0;
718 
719  // Interruption event
720  character.DoCancelObjectAction(m_pLastUserAction);
721  }
722 
723  // Reset state
724  m_pLastUserAction = null;
725  SetSelectedAction(m_pLastUserAction);
726  }
727  }
728 
729  // Store last input
730  m_bLastInput = m_bPerformAction;
731 
732  // Reset cached inputs to ensure that those actions will not be triggered on next frame
733  m_fSelectAction = 0;
734  if (!m_bIsPerforming)
735  m_bPerformAction = false;
736 
737  // Update last context
738  m_pLastContext = currentContext;
739  }
740 
741  protected ref array<BaseUserAction> m_ActionsBuffer = {};
742  protected ref array<bool> m_PerformBuffer = {};
743  protected ref map<string, ref array<int>> m_IndicesBuffer = new map<string, ref array<int>>();
744 
749  protected void AggregateActions(array<BaseUserAction> actionsList, array<bool> canPerformList)
750  {
751  m_ActionsBuffer.Copy(actionsList);
752  m_PerformBuffer.Copy(canPerformList);
753  m_IndicesBuffer.Clear();
754  actionsList.Clear();
755  canPerformList.Clear();
756 
757  // First pass, filter&gather
758  for (int i = 0, count = m_ActionsBuffer.Count(); i < count; i++)
759  {
760  BaseUserAction action = m_ActionsBuffer[i];
761  if (!action)
762  continue;
763 
764  // For non-aggregated actions, skip this process
765  bool canPerform = m_PerformBuffer[i];
766  if (!action.CanAggregate())
767  continue;
768 
769  // Group actions of same name for aggregation
770  string actionName = action.GetActionName();
771  if (!m_IndicesBuffer.Contains(actionName))
772  m_IndicesBuffer.Insert(actionName, {});
773 
774  m_IndicesBuffer[actionName].Insert(i);
775  }
776 
777  // Second pass, resolve&output
778  for (int i = 0, count = m_ActionsBuffer.Count(); i < count; i++)
779  {
780  BaseUserAction action = m_ActionsBuffer[i];
781  if (!action)
782  continue;
783 
784  // For non-aggregated actions, append the action straight away
785  bool canPerform = m_PerformBuffer[i];
786  if (!action.CanAggregate())
787  {
788  actionsList.Insert(action);
789  canPerformList.Insert(canPerform);
790  continue;
791  }
792 
793  // For aggregated actions, find group of given actions
794  string actionName = action.GetActionName();
795  // If group was sorted, it will be removed from the map,
796  // and no longer present, therefore we can ommit rechecking
797  if (!m_IndicesBuffer.Contains(actionName))
798  continue;
799 
800  // If not resolved yet, resolve by finding available action
801  int availableIndex = m_IndicesBuffer[actionName][0]; // By default the first action
802  foreach (int index : m_IndicesBuffer[actionName])
803  {
804  // First performable hit
805  if (m_PerformBuffer[index])
806  {
807  availableIndex = index;
808  break;
809  }
810  }
811 
812  BaseUserAction aggregatedAction = m_ActionsBuffer[availableIndex];
813  bool aggregatedState = m_PerformBuffer[availableIndex];
814  actionsList.Insert(aggregatedAction);
815  canPerformList.Insert(aggregatedState);
816  // And remove the action from the group map
817  m_IndicesBuffer.Remove(actionName);
818  }
819  }
820 }
SetSelectedAction
ExtBaseInteractionHandlerComponentClass BaseInteractionHandlerComponentClass SetSelectedAction(BaseUserAction action)
SCR_InteractionHandlerComponentClass
Definition: SCR_InteractionHandlerComponent.c:1
SCR_PlayerController
Definition: SCR_PlayerController.c:31
OnContextChanged
event protected void OnContextChanged(UserActionContext previousContext, UserActionContext newContext)
IsContextAvailable
proto external bool IsContextAvailable()
Returns true when there is a gathered context available.
ON_FREELOOK
enum ESlotSize ON_FREELOOK
SCR_BaseInteractionDisplay
Base class for displaying interactions in the UI.
Definition: SCR_BaseInteractionDisplay.c:39
OnControlledEntityChanged
protected void OnControlledEntityChanged(IEntity from, IEntity to)
Runs every time the controlled entity has been changed.
Definition: SCR_ItemPlacementComponent.c:113
OnPostFrame
event protected void OnPostFrame(IEntity owner, IEntity controlledEntity, float timeSlice)
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
SCR_ScriptedUserAction
A scripted action class having optional logic to check if vehicle is valid.
Definition: SCR_ScriptedUserAction.c:2
ActionPerform
void ActionPerform(SCR_BaseEditorAction action, vector cursorWorldPosition, int flags)
Definition: SCR_BaseActionsEditorComponent.c:288
m_sActionName
protected string m_sActionName
Definition: SCR_ActionsRadialMenuEditorComponent.c:24
GetIsInteractionAvailableScript
event protected bool GetIsInteractionAvailableScript()
SetManualCollectionOverride
proto external void SetManualCollectionOverride(bool enabled)
If set to true, we expect a list of entities to be provided from the user instead.
DoIntersectCheck
event protected bool DoIntersectCheck(IEntity controlledEntity)
GetCanInteractScript
event protected bool GetCanInteractScript(IEntity controlledEntity)
ON_INPUT_ACTION
SCR_InteractionHandlerComponentClass ON_INPUT_ACTION
Nearby display will show on provided input action.
SCR_CharacterControllerComponent
Definition: SCR_CharacterControllerComponent.c:35
SCR_Math3D
Contains various scripted 3D math functions.
Definition: SCR_Math3D.c:2
ActionFinishReason
ActionFinishReason
Reason for why an action ended. Used in SCR_BaseInteractionDisplay and derived classes.
Definition: SCR_BaseInteractionDisplay.c:3
ActionsTuple
Definition: SCR_BaseInteractionDisplay.c:13
Attribute
typedef Attribute
Post-process effect of scripted camera.
BaseWeaponComponent
Definition: BaseWeaponComponent.c:12
attachment
IEntity attachment
Definition: SCR_AttachementAction.c:16
GetVisibilityRange
proto external float GetVisibilityRange()
Returns the global actions visibility range value defined by attribute in this component.
GetCurrentContext
proto external UserActionContext GetCurrentContext()
Returns currently gathered (active-preferred) context or null if none.
ActionDisplayData
Data container.
Definition: SCR_BaseInteractionDisplay.c:29
CanContextChange
event protected bool CanContextChange(UserActionContext currentContext, UserActionContext newContext)
GetFilteredActions
proto external int GetFilteredActions(out notnull array< BaseUserAction > outActions, out notnull array< bool > outCanBePerformed)
RemoveActionListeners
void RemoveActionListeners()
Definition: game.c:860
SCR_AdjustSignalAction
Definition: SCR_AdjustSignalAction.c:1
index
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition: SCR_DestructionSynchronizationComponent.c:17
DISABLED
SCR_InteractionHandlerComponentClass DISABLED
Nearby display will never be allowed.
GetManualOverrideList
event protected array< IEntity > GetManualOverrideList(IEntity owner, out vector referencePoint)
ALWAYS_ON
SCR_InteractionHandlerComponentClass ALWAYS_ON
Nearby display will always be on (when possible).
BaseUserAction
Definition: BaseUserAction.c:12
GetSelectedActionScript
event protected BaseUserAction GetSelectedActionScript()
SetNearbyCollectionEnabled
proto external void SetNearbyCollectionEnabled(bool enabled)
OnInit
override protected void OnInit(IEntity owner)
Definition: SCR_CharacterCommandHandler_Tests.c:523
DamageManagerComponent
Definition: DamageManagerComponent.c:12
SCR_DebugMenuID
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition: DebugMenuID.c:3
UserActionContext
Definition: UserActionContext.c:15
ScriptedSignalUserAction
This action will take care of the synchronization of the signal value.
Definition: ScriptedSignalUserAction.c:13
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180