7enum SCR_NearbyContextDisplayMode
23 protected SCR_BaseInteractionDisplay m_pDisplay;
25 [
Attribute(
"3", UIWidgets.ComboBox,
"Display mode",
"", ParamEnumArray.FromEnum(SCR_NearbyContextDisplayMode),
category:
"Nearby Context Properties")]
26 protected SCR_NearbyContextDisplayMode m_eDisplayMode;
28 [
Attribute(
"1", UIWidgets.Slider,
"Distance in percentage the raycast needs to travel until the context counts as visible",
"0 1 0.01",
category:
"Nearby Context Properties")]
29 protected float m_fRaycastThreshold;
31 [
Attribute(
"", UIWidgets.EditBox,
"Action to listen for when SCR_NearbyContextDisplayMode is set to ON_INPUT_ACTION",
category:
"Nearby Context Properties")]
34 [
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")]
35 protected string m_sActionContext;
38 protected UserActionContext m_pLastContext;
41 protected BaseUserAction m_pLastUserAction;
44 protected int m_iSelectedActionIndex;
46 protected bool m_bIsPerforming;
47 protected bool m_bPerformAction;
48 protected bool m_bLastInput;
49 protected float m_fSelectAction;
53 protected ref array<IEntity> m_aCollectedEntities = {};
55 protected ref array<IEntity> m_aCollectedNearbyEntities = {};
57 protected IEntity m_ControlledEntity;
61 protected void RegisterActionListeners()
63 InputManager pInputManager =
GetGame().GetInputManager();
67 m_bPerformAction =
false;
71 pInputManager.AddActionListener(
"SelectAction",
EActionTrigger.VALUE, ActionScroll);
77 InputManager pInputManager =
GetGame().GetInputManager();
81 m_bPerformAction =
false;
85 pInputManager.RemoveActionListener(
"SelectAction",
EActionTrigger.VALUE, ActionScroll);
101 m_fSelectAction = value;
110 PlayerController controller =
GetGame().GetPlayerController();
114 if (controller.FindComponent(SCR_InteractionHandlerComponent) !=
this)
120 if (to && to == SCR_PlayerController.GetLocalControlledEntity())
121 RegisterActionListeners();
127 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_INTERACTION_SKIP_DURATION,
"",
"Skip action duration",
"User Actions");
131 protected SCR_BaseInteractionDisplay FindDisplay(
IEntity owner)
133 PlayerController playerController = PlayerController.Cast(owner);
134 if (!playerController)
136 Print(
"InteractionHandler must be attached to a PlayerController!",
LogLevel.ERROR);
140 HUDManagerComponent hudManager = HUDManagerComponent.Cast(playerController.FindComponent(HUDManagerComponent));
141 array<BaseInfoDisplay> displayInfos = {};
142 int count = hudManager.GetInfoDisplays(displayInfos);
143 for (
int i = 0; i < count; i++)
145 SCR_BaseInteractionDisplay current = SCR_BaseInteractionDisplay.Cast(displayInfos[i]);
150 Print(
"InteractionDisplay not found! InteractionDisplay must be stored in HUDManagerComponent of a PlayerController!",
LogLevel.WARNING);
163 protected void DoProcessInteraction(
164 ChimeraCharacter user,
165 UserActionContext context,
166 BaseUserAction action,
170 SCR_BaseInteractionDisplay display)
176 bool isOk = action && canPerform && action == m_pLastUserAction;
178 if (performInput && isOk)
182 if (!m_bIsPerforming && !m_bLastInput)
189 display.OnActionStart(user, action);
192 user.DoStartObjectAction(action);
195 m_bIsPerforming =
true;
200 else if (m_bIsPerforming)
202 if (DiagMenu.GetValue(
SCR_DebugMenuID.DEBUGUI_INTERACTION_SKIP_DURATION))
204 timeSlice += Math.AbsFloat(action.GetActionDuration());
206 SCR_ScriptedUserAction scrAction = SCR_ScriptedUserAction.Cast(action);
215 if (action.ShouldPerformPerFrame())
216 user.DoPerformContinuousObjectAction(action, timeSlice);
219 float duration = action.GetActionDuration();
234 if (!action.ShouldPerformPerFrame())
235 user.DoPerformObjectAction(action);
239 m_bIsPerforming =
false;
254 user.DoCancelObjectAction(action);
257 m_bIsPerforming =
false;
266 ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
271 MenuManager menuManager =
GetGame().GetMenuManager();
275 SCR_CharacterControllerComponent characterController = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
276 if (characterController && !characterController.
CanInteract())
291 return m_pLastUserAction;
297 if (!controlledEntity)
300 ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
304 if (character.IsInVehicle())
307 if (character.GetCharacterController().GetInspect())
314 protected override void OnContextChanged(UserActionContext previousContext, UserActionContext newContext)
319 m_iSelectedActionIndex = 0;
321 m_pDisplay.HideDisplay();
325 if (m_pDisplay && newContext)
326 m_pDisplay.ShowDisplay();
330 protected override event bool CanContextChange(UserActionContext currentContext, UserActionContext newContext)
336 if (!m_ControlledEntity)
340 DamageManagerComponent dmg = DamageManagerComponent.Cast(m_ControlledEntity.
FindComponent(DamageManagerComponent));
341 if (dmg && dmg.IsDestroyed())
348 if (currentContext && m_ControlledEntity)
351 const float threshold = 1.1;
355 float maxSqDistance = (visRange * visRange) * 1.1;
357 float sqDistance = vector.DistanceSq(currentContext.GetOrigin(), m_ControlledEntity.
GetOrigin());
359 if (sqDistance > maxSqDistance)
367 if (currentContext && m_bIsPerforming)
378 return m_bIsPerforming;
384 protected bool IsFreelookEnabled(ChimeraCharacter character)
389 CharacterControllerComponent controller = character.GetCharacterController();
394 if (controller.GetInspect())
397 CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent();
399 if (compartmentAccess && compartmentAccess.IsInCompartment() && controller.IsInThirdPersonView())
404 if (compartmentAccess)
406 if (compartmentAccess.IsGettingIn() || compartmentAccess.IsGettingOut())
411 if (character.IsInVehicle())
413 if (controller.IsInThirdPersonView())
418 if (controller.IsFalling())
422 if (controller.IsClimbing())
429 if (controller.GetFreeLookInput())
436 protected bool ShouldBeEnabled(SCR_NearbyContextDisplayMode displayMode, ChimeraCharacter character,
bool playerCameraOnly =
true)
443 if (playerCameraOnly)
445 CameraManager cameraManager =
GetGame().GetCameraManager();
446 if (cameraManager && !PlayerCamera.Cast(cameraManager.CurrentCamera()))
450#ifdef NEARBY_INTERACTIONS_CONTEXT_DEBUG
459 case SCR_NearbyContextDisplayMode.DISABLED:
463 case SCR_NearbyContextDisplayMode.ALWAYS_ON:
467 case SCR_NearbyContextDisplayMode.ON_INPUT_ACTION:
472 InputManager inputManager =
GetGame().GetInputManager();
473 if (!m_sActionContext.IsEmpty())
474 inputManager.ActivateContext(m_sActionContext);
480 case SCR_NearbyContextDisplayMode.ON_FREELOOK:
481 return IsFreelookEnabled(character);
489 void GetOverrideListReferencePoint(
IEntity owner, out vector referencePoint)
491 CameraManager cameraManager =
GetGame().GetCameraManager();
494 CameraBase camera = cameraManager.CurrentCamera();
495 vector rayDir = camera.GetWorldTransformAxis(2);
496 vector rayStart = camera.GetOrigin();
497 referencePoint = rayStart + rayDir;
500 ChimeraCharacter character = ChimeraCharacter.Cast(m_ControlledEntity);
504 CharacterControllerComponent controller = character.GetCharacterController();
505 if (controller.GetInspect() && controller.GetInspectCurrentWeapon())
509 IEntity inspectedEntity = controller.GetInspectEntity();
511 vector origin = inspectedEntity.
GetOrigin();
514 referencePoint = SCR_Math3D.IntersectPlane(rayStart, rayDir, origin, normal);
521 referencePoint = vector.Zero;
528 GetOverrideListReferencePoint(owner, referencePoint);
529 return m_aCollectedNearbyEntities;
535 GetOverrideListReferencePoint(owner, referencePoint);
536 return m_aCollectedEntities;
542 protected void HandleOverride(notnull ChimeraCharacter character)
544 m_aCollectedNearbyEntities.Clear();
545 m_aCollectedEntities.Clear();
547 const bool bInspection = HandleInspection(character);
548 const bool bInVehicle = HandleVehicle(character);
553 if (bInspection || bInVehicle)
565 protected bool HandleVehicle(notnull ChimeraCharacter character)
567 if (!character.IsInVehicle())
570 CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent();
572 if (!compartmentAccess || !compartmentAccess.IsInCompartment())
575 BaseCompartmentSlot compartment = compartmentAccess.GetCompartment();
580 IEntity vehicle = compartment.GetOwner().GetRootParent();
582 m_aCollectedNearbyEntities.Insert(vehicle);
584 BaseCompartmentManagerComponent compartmentManager = BaseCompartmentManagerComponent.Cast(vehicle.
FindComponent(BaseCompartmentManagerComponent));
585 if (!compartmentManager)
588 array<BaseCompartmentSlot> compartments = {};
589 compartmentManager.GetCompartments(compartments);
591 foreach (BaseCompartmentSlot comp : compartments)
593 IEntity compOwner = comp.GetOwner();
594 if (compOwner && !m_aCollectedNearbyEntities.Contains(compOwner))
595 m_aCollectedNearbyEntities.Insert(compOwner);
597 IEntity compOccupant = comp.GetOwner();
598 if (compOccupant && !m_aCollectedNearbyEntities.Contains(compOccupant))
599 m_aCollectedNearbyEntities.Insert(compOccupant);
609 protected bool HandleInspection(notnull ChimeraCharacter character)
611 if (!character.GetCharacterController().GetInspect())
615 CharacterControllerComponent ctrlComp = character.GetCharacterController();
616 if (ctrlComp.GetInspectCurrentWeapon())
619 BaseWeaponManagerComponent weaponManager = BaseWeaponManagerComponent.Cast(character.FindComponent(BaseWeaponManagerComponent));
623 BaseWeaponComponent weapon = weaponManager.GetCurrentWeapon();
627 m_aCollectedNearbyEntities.Insert(weapon.GetOwner());
628 m_aCollectedEntities.Insert(weapon.GetOwner());
630 array<AttachmentSlotComponent> attachments = {};
631 weapon.GetAttachments(attachments);
633 foreach (AttachmentSlotComponent attachment : attachments)
635 IEntity attachedEntity = attachment.GetAttachedEntity();
638 m_aCollectedNearbyEntities.Insert(attachedEntity);
639 m_aCollectedEntities.Insert(attachedEntity);
643 BaseMagazineComponent magazineComp = weapon.GetCurrentMagazine();
646 m_aCollectedNearbyEntities.Insert(magazineComp.GetOwner());
647 m_aCollectedEntities.Insert(magazineComp.GetOwner());
655 IEntity inspectedItem = ctrlComp.GetInspectEntity();
658 m_aCollectedNearbyEntities.Insert(inspectedItem);
659 m_aCollectedEntities.Insert(inspectedItem);
672 m_pDisplay = FindDisplay(owner);
674 m_ControlledEntity = controlledEntity;
676 ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
679 bool enableNearbyCollection = ShouldBeEnabled(m_eDisplayMode, character,
true);
683 if (!character || character.IsInVehicleADS())
685 m_bPerformAction =
false;
689 ChimeraWorld world = ChimeraWorld.CastFrom(character.GetWorld());
690 if (!world || world.IsGameTimePaused())
693 HandleOverride(character);
698 array<BaseUserAction> actions = {};
699 array<bool> canPerform = {};
702 GetGame().GetInputManager().ActivateContext(
"ActionMenuContext", 250);
704 foreach (BaseUserAction action : actions)
707 m_pDisplay.OnActionProgress(character, action, action.GetActionProgress(0, 0), action.GetActionDuration());
710 AggregateActions(actions, canPerform);
715 if (m_pLastUserAction)
717 BaseUserAction action;
718 for (
int i = 0, ac = actions.Count(); i < ac; i++)
721 if (action && action == m_pLastUserAction)
723 m_iSelectedActionIndex = i;
730 int iScrollAmount = 0;
731 int prevActionIndex = m_iSelectedActionIndex;
733 if (!m_bIsPerforming)
735 if (Math.AbsFloat(m_fSelectAction) > 0.5)
736 iScrollAmount = Math.Clamp(m_fSelectAction, -1.0, 1.0);
738 if (iScrollAmount != 0)
739 m_iSelectedActionIndex = m_iSelectedActionIndex - iScrollAmount;
743 int actionsCount = actions.Count();
744 m_iSelectedActionIndex = Math.Clamp(m_iSelectedActionIndex, 0, actionsCount - 1);
746 BaseUserAction selectedAction = null;
747 bool canPerformSelectedAction =
false;
751 selectedAction = m_pLastUserAction;
752 if (actions.Count() > prevActionIndex && actions[prevActionIndex] == m_pLastUserAction)
753 canPerformSelectedAction = canPerform[prevActionIndex];
754 else if (m_pLastUserAction)
757 SCR_CharacterControllerComponent controller = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
758 canPerformSelectedAction = canPerformSelectedAction && controller && controller.GetLifeState() ==
ECharacterLifeState.ALIVE;
760 else if (actionsCount > 0)
762 selectedAction = actions[m_iSelectedActionIndex];
763 canPerformSelectedAction = canPerform[m_iSelectedActionIndex];
768 canPerformSelectedAction = canPerformSelectedAction && selectedAction.
CanBePerformed(character);
770 DoProcessInteraction(character, currentContext, selectedAction, canPerformSelectedAction, m_bPerformAction, timeSlice, m_pDisplay);
771 m_pLastUserAction = selectedAction;
777 ActionsTuple pData =
new ActionsTuple();
780 if (canInteract || m_bIsPerforming)
782 pData.param1 = actions;
783 pData.param2 = canPerform;
790 ActionDisplayData pDisplayData =
new ActionDisplayData();
791 pDisplayData.pUser = controlledEntity;
792 pDisplayData.pActionsData = pData;
793 pDisplayData.pSelectedAction = selectedAction;
794 pDisplayData.pCurrentContext = currentContext;
796 m_pDisplay.SetDisplayData(pDisplayData);
801 else if (m_pLastContext != currentContext)
804 if (m_pLastUserAction)
810 m_bIsPerforming =
false;
814 character.DoCancelObjectAction(m_pLastUserAction);
818 m_pLastUserAction = null;
824 m_bLastInput = m_bPerformAction;
828 if (!m_bIsPerforming)
829 m_bPerformAction =
false;
832 m_pLastContext = currentContext;
835 protected ref array<BaseUserAction> m_ActionsBuffer = {};
836 protected ref array<bool> m_PerformBuffer = {};
837 protected ref map<string, ref array<int>> m_IndicesBuffer =
new map<string, ref array<int>>();
843 protected void AggregateActions(array<BaseUserAction> actionsList, array<bool> canPerformList)
845 m_ActionsBuffer.Copy(actionsList);
846 m_PerformBuffer.Copy(canPerformList);
847 m_IndicesBuffer.Clear();
849 canPerformList.Clear();
852 for (
int i = 0, count = m_ActionsBuffer.Count(); i < count; i++)
854 BaseUserAction action = m_ActionsBuffer[i];
859 bool canPerform = m_PerformBuffer[i];
865 if (!m_IndicesBuffer.Contains(actionName))
866 m_IndicesBuffer.Insert(actionName, {});
868 m_IndicesBuffer[actionName].Insert(i);
872 for (
int i = 0, count = m_ActionsBuffer.Count(); i < count; i++)
874 BaseUserAction action = m_ActionsBuffer[i];
879 bool canPerform = m_PerformBuffer[i];
882 actionsList.Insert(action);
883 canPerformList.Insert(canPerform);
891 if (!m_IndicesBuffer.Contains(actionName))
895 int availableIndex = m_IndicesBuffer[actionName][0];
896 foreach (
int index : m_IndicesBuffer[actionName])
899 if (m_PerformBuffer[
index])
901 availableIndex =
index;
906 BaseUserAction aggregatedAction = m_ActionsBuffer[availableIndex];
907 bool aggregatedState = m_PerformBuffer[availableIndex];
908 actionsList.Insert(aggregatedAction);
909 canPerformList.Insert(aggregatedState);
911 m_IndicesBuffer.Remove(actionName);
916 float GetRaycastThreshold()
918 return m_fRaycastThreshold;
930 static bool CanBeShownInVehicle(notnull ChimeraCharacter character, notnull BaseUserAction userAction,
bool pilotOnly =
false,
bool pilotUncapableOverride =
false,
bool interiorOnly =
false, array<int> definedCompartmentsOnly = null, array<int> excludeDefinedCompartments = null)
933 CompartmentAccessComponent compartmentAccess = character.GetCompartmentAccessComponent();
934 if (!compartmentAccess)
937 Vehicle vehicle =
Vehicle.Cast(userAction.GetOwner().GetRootParent());
944 BaseCompartmentSlot slot = compartmentAccess.GetCompartment();
947 slotRootParent = slot.GetOwner().GetRootParent();
949 if (interiorOnly && (!slotRootParent || slotRootParent != vehicle))
953 if (pilotOnly && !PilotCompartmentSlot.Cast(slot))
955 ChimeraCharacter pilot = ChimeraCharacter.Cast(vehicle.GetPilot());
956 if (!pilotUncapableOverride && pilot != character)
959 if (pilot && pilot != character)
961 CharacterControllerComponent controller = pilot.GetCharacterController();
967 int compartmentSection = -1;
971 if (definedCompartmentsOnly && !definedCompartmentsOnly.IsEmpty() && (!definedCompartmentsOnly.Contains(compartmentSection) || vehicle != slotRootParent))
974 if (excludeDefinedCompartments && !excludeDefinedCompartments.IsEmpty() && (excludeDefinedCompartments.Contains(compartmentSection) || vehicle != slotRootParent))
ArmaReforgerScripted GetGame()
void ActionPerform(SCR_BaseEditorAction action, vector cursorWorldPosition, int flags)
ActionFinishReason
Reason for why an action ended. Used in SCR_BaseInteractionDisplay and derived classes.
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
void RemoveActionListeners()
SCR_InteractionHandlerComponentClass ALWAYS_ON
Nearby display will always be on (when possible).
SCR_InteractionHandlerComponentClass ON_INPUT_ACTION
Nearby display will show on provided input action.
enum EVehicleType IEntity
int GetCompartmentSection()
Switching seats is allowed only between compartments with matching area.
proto external void SetActiveContext(UserActionContext context)
Setter for m_pActiveContext.
proto external bool CanBeShown(IEntity user)
Can this action be shown in the UI for the user?
proto external string GetActionName()
proto external bool CanBePerformed(IEntity user)
Can this action be performed by the user?
proto external bool CanAggregate()
Whether action can be aggregated by name, this is a temp workaround for localization.
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
proto external vector GetWorldTransformAxis(int axis)
See IEntity::GetTransformAxis.
bool DoIntersectCheck(IEntity controlledEntity)
BaseUserAction GetSelectedActionScript()
bool GetIsInteractionAvailableScript()
event bool CanContextChange(UserActionContext currentContext, UserActionContext newContext)
bool GetCanInteractScript(IEntity controlledEntity)
float GetLoopActionHoldDuration()
enum EPhysicsLayerPresets Vehicle
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute
@ DISABLED
General event switch.
void OnControlledEntityChanged(IEntity from, IEntity to)
Runs every time the controlled entity has been changed.
proto external void SetManualCollectionOverride(bool enabled)
If set to true, we expect a list of entities to be provided from the user instead.
void OnContextChanged(UserActionContext previousContext, UserActionContext newContext)
bool IsPerformingAction()
array< IEntity > GetManualNearbyOverrideList(IEntity owner, out vector referencePoint)
void OnPostFrame(IEntity owner, IEntity controlledEntity, float timeSlice)
proto external void SetNearbyCollectionEnabled(bool enabled)
ExtBaseInteractionHandlerComponentClass BaseInteractionHandlerComponentClass SetSelectedAction(BaseUserAction action)
array< IEntity > GetManualOverrideList(IEntity owner, out vector referencePoint)
proto external int GetFilteredActions(out notnull array< BaseUserAction > outActions, out notnull array< bool > outCanBePerformed)
proto external void SetManualNearbyCollectionOverride(bool enabled)
If set to true, we expect a list of nearby entities to be provided from the user instead.
proto external float GetVisibilityRange()
Returns the global actions visibility range value defined by attribute in this component.
proto external UserActionContext GetCurrentContext()
Returns currently gathered (active-preferred) context or null if none.
proto external bool IsContextAvailable()
Returns true when there is a gathered context available.