13 const float SIZE_LARGE = 580;
14 protected const float SIZE_SMALL = 400;
16 protected vector m_vMenuCenterPos;
18 protected float m_fEntriesAngleDistance;
19 protected float m_fEntryAngleOffset;
21 protected float m_fPointingAngle;
22 protected bool m_bIsPointingToCenter;
23 protected float m_fDynamicMouseSelectionTreshold;
25 protected bool m_bActivateContext;
26 protected bool m_bPreventSelectionContext;
31 protected string m_sActionHint;
34 protected ref ScriptInvoker<SCR_RadialMenu, float> m_OnDisplaySizeChange;
35 protected ref ScriptInvoker<SCR_RadialMenu, string> m_OnSetActionHint;
38 protected void InvokeOnDisplaySizeChange(
float size)
40 if (m_OnDisplaySizeChange)
41 m_OnDisplaySizeChange.Invoke(
this, size);
45 ScriptInvoker GetOnDisplaySizeChange()
47 if (!m_OnDisplaySizeChange)
48 m_OnDisplaySizeChange =
new ScriptInvoker();
50 return m_OnDisplaySizeChange;
54 protected void InvokeOnSetActionHint(
string action)
56 if (m_OnSetActionHint)
57 m_OnSetActionHint.Invoke(
this, action);
61 ScriptInvoker GetOnSetActionHint()
63 if (!m_OnSetActionHint)
64 m_OnSetActionHint =
new ScriptInvoker();
66 return m_OnSetActionHint;
78 if (!m_GlobalRadialMenu)
80 PlayerController controller =
GetGame().GetPlayerController();
84 SCR_RadialMenuGameModeComponent rm = SCR_RadialMenuGameModeComponent.Cast(controller.FindComponent(SCR_RadialMenuGameModeComponent));
89 Print(
"[SCR_RadialMenuController] - Can't setup radial menu due to missing RM game mode!");
97 return m_GlobalRadialMenu;
103 if (m_OpenedRadialMenu)
104 m_OpenedRadialMenu.Close();
112 protected override event void OnOpen()
116 if (m_OpenedRadialMenu)
117 m_OpenedRadialMenu.Close();
121 AddActionListeners();
123 GetGame().GetCallqueue().Remove(ReleaseContext);
124 m_bActivateContext =
true;
126 m_OpenedRadialMenu =
this;
132 protected override event void OnClose()
134 super.OnClose(owner);
135 RemoveActionListeners();
137 m_OpenedRadialMenu =
null;
141 GetGame().GetCallqueue().CallLater(ReleaseContext, m_RadialInputs.m_iContextDeactivationTime);
145 protected void ReleaseContext()
147 m_bActivateContext =
false;
151 override void Update(
float timeSlice)
153 if (System.IsConsoleApp())
157 if (m_bActivateContext && !m_bPreventSelectionContext)
158 GetGame().GetInputManager().ActivateContext(m_Inputs.m_sContext);
161 if (m_bActivateContext && m_ControllerInputs)
162 GetGame().GetInputManager().ActivateContext(m_ControllerInputs.m_sControllerContext);
169 override protected void OnUpdate(
float timeSlice)
174 if (!m_bPreventSelectionContext)
193 super.SetMenuDisplay(display);
196 if (m_RadialControllerInputs)
197 ChangeDisplaySize(m_RadialControllerInputs.m_bUseLargeSize, m_RadialControllerInputs.m_fCustomSize);
202 override protected void SelectEntry()
207 entry = HandleSelection(
id);
211 if (m_bIsPointingToCenter && m_RadialControllerInputs.m_bDeselectInCenter)
215 GetGame().GetCallqueue().CallLater(DeselectEntry, m_RadialInputs.m_iGamepadDeselectionDelay);
221 GetGame().GetCallqueue().Remove(DeselectEntry);
224 if (m_SelectedEntry != entry)
226 m_SelectedEntry = entry;
227 InvokeOnSelect(entry,
id);
232 if (entry && !entry.GetInputAction().IsEmpty())
233 SetActionHint(entry.GetInputAction());
240 override protected void InvokeOnUpdateEntries(array<ref SCR_SelectionMenuEntry> entries)
243 m_fEntriesAngleDistance = 360 /
m_aEntries.Count();
245 super.InvokeOnUpdateEntries(entries);
251 super.SetController(owner, controls);
256 if (m_RadialControllerInputs && m_RadialInputs)
257 m_RadialInputs.SetUseRightStick(m_RadialControllerInputs.m_bUseRightStick);
261 DebugSetupControllerInputs(m_RadialControllerInputs);
262 DebugFillWithEntries();
267 override protected void OnPerformInput()
269 super.OnPerformInput();
271 if (!m_SelectedEntry)
273 if (m_ControllerInputs.m_bCloseOnPerform)
280 override protected void AddActionListeners()
282 super.AddActionListeners();
287 if (!m_RadialControllerInputs.m_bUseQuickActions)
290 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot1", EActionTrigger.DOWN, OnQuickAction1);
291 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot2", EActionTrigger.DOWN, OnQuickAction2);
292 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot3", EActionTrigger.DOWN, OnQuickAction3);
293 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot4", EActionTrigger.DOWN, OnQuickAction4);
294 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot5", EActionTrigger.DOWN, OnQuickAction5);
295 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot6", EActionTrigger.DOWN, OnQuickAction6);
296 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot7", EActionTrigger.DOWN, OnQuickAction7);
297 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot8", EActionTrigger.DOWN, OnQuickAction8);
298 GetGame().GetInputManager().AddActionListener(
"InventoryQuickSlot9", EActionTrigger.DOWN, OnQuickAction9);
303 override protected void RemoveActionListeners()
305 super.RemoveActionListeners();
310 if (!m_RadialControllerInputs.m_bUseQuickActions)
313 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot1", EActionTrigger.DOWN, OnQuickAction1);
314 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot2", EActionTrigger.DOWN, OnQuickAction2);
315 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot3", EActionTrigger.DOWN, OnQuickAction3);
316 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot4", EActionTrigger.DOWN, OnQuickAction4);
317 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot5", EActionTrigger.DOWN, OnQuickAction5);
318 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot6", EActionTrigger.DOWN, OnQuickAction6);
319 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot7", EActionTrigger.DOWN, OnQuickAction7);
320 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot8", EActionTrigger.DOWN, OnQuickAction8);
321 GetGame().GetInputManager().RemoveActionListener(
"InventoryQuickSlot9", EActionTrigger.DOWN, OnQuickAction9);
325 protected void OnQuickAction1() { QuickActionUse(1); }
326 protected void OnQuickAction2() { QuickActionUse(2); }
327 protected void OnQuickAction3() { QuickActionUse(3); }
328 protected void OnQuickAction4() { QuickActionUse(4); }
329 protected void OnQuickAction5() { QuickActionUse(5); }
330 protected void OnQuickAction6() { QuickActionUse(6); }
331 protected void OnQuickAction7() { QuickActionUse(7); }
332 protected void OnQuickAction8() { QuickActionUse(8); }
333 protected void OnQuickAction9() { QuickActionUse(9); }
336 protected void QuickActionUse(
int id)
345 m_iSelectedEntryId = id;
348 InvokeOnSelect(m_SelectedEntry,
id);
350 m_bEntryPerformed =
true;
351 PerformEntry(m_SelectedEntry);
359 protected int GetSelectedElementIndex(
float angle,
int elementCount)
361 if (elementCount == 0)
364 float angleDeg = Math.Repeat(angle * Math.RAD2DEG, 360) - m_fEntryAngleOffset;
365 float overflow = m_fEntriesAngleDistance * 0.5;
370 angleDeg = Math.Repeat(angleDeg, 360);
373 if (m_fEntriesAngleDistance == 0)
376 DebugPrint(
"GetSelectedElementIndex",
"No distance between entries!");
382 int idx = Math.Round(angleDeg / m_fEntriesAngleDistance);
384 if (idx < 0 || idx >= elementCount)
392 if (angleDeg > (360 - overflow + m_fEntryAngleOffset))
404 protected float AdjustDistanceWithResolution(
float distance)
408 GetGame().GetWorkspace().GetScreenSize(curW, curH);
410 if (curW == 0 || curH == 0)
415 WidgetManager.GetReferenceScreenSize(refW, refH);
417 float ratioX = curW / refW;
418 float ratioY = curH / refH;
420 float ratioDist = vector.Distance(
421 Vector(m_vMenuCenterPos[0] / refW, m_vMenuCenterPos[1] / refH, 0),
422 Vector(curW / refW, curH / refH,
432 if (!m_RadialInputs || !m_RadialControllerInputs)
443 WidgetManager.GetMousePos(mouseX, mouseY);
444 dir = Vector(mouseX, mouseY, 0);
446 dist = vector.Distance(m_vMenuCenterPos, dir);
447 dist = AdjustDistanceWithResolution(dist);
450 dir[0] = mouseX - m_vMenuCenterPos[0];
451 dir[1] = (mouseY - m_vMenuCenterPos[1]) * -1;
455 m_bIsPointingToCenter = (
456 m_RadialControllerInputs.m_bDeselectInCenter && dist < m_fDynamicMouseSelectionTreshold);
458 if (m_bIsPointingToCenter)
465 m_RadialInputs.GetRadialXYInput(x, y);
469 dist = vector.Distance(vector.Zero, Vector(x, y, 0));
472 m_bIsPointingToCenter = (
473 m_RadialControllerInputs.m_bDeselectInCenter && dist < m_RadialInputs.m_fGamepadSelectionTreshhold);
475 if (m_bIsPointingToCenter)
480 float angle = Math.Atan2(dir[0], dir[1]);
481 id = GetSelectedElementIndex(angle,
m_aEntries.Count());
483 m_fPointingAngle = angle * Math.RAD2DEG;
493 protected void DeselectEntry()
495 m_SelectedEntry =
null;
496 InvokeOnSelect(
null, -1);
501 protected void ReactQuickInput()
503 for (
int i = 1; i < 9; i++)
514 m_iSelectedEntryId = id;
517 InvokeOnSelect(m_SelectedEntry,
id);
519 m_bEntryPerformed =
true;
520 PerformEntry(m_SelectedEntry);
529 void ChangeDisplaySize(
bool useLarge =
true,
float customSize = -1)
531 float size = SIZE_LARGE;
539 m_fDynamicMouseSelectionTreshold = m_RadialInputs.m_fMouseSelectionTreshold;
541 if (m_RadialInputs.m_bDynamicMouseTreshold)
542 m_fDynamicMouseSelectionTreshold *= size / SIZE_LARGE;
545 InvokeOnDisplaySizeChange(size);
550 void SetActionHint(
string action)
552 m_sActionHint = action;
553 InvokeOnSetActionHint(m_sActionHint);
558 void HideActionHint()
560 InvokeOnSetActionHint(
"");
568 void SetMenuCenterPos(vector centerPos)
570 m_vMenuCenterPos = centerPos;
574 float GetEntriesAngleDistance()
576 return m_fEntriesAngleDistance;
580 float GetPointingAngle()
582 return m_fPointingAngle;
586 bool IsPointingToCenter()
588 return m_bIsPointingToCenter;
598 override protected void DebugPrint(
string method,
string msg)
600 Print(
string.Format(
"[SCR_RadialMenu] - %1() - '%2'", method, msg));
604 protected void DebugSetupDiagMenu()
606 string rmDiagName =
"Radial menu (refactored)";
608 DiagMenu.RegisterMenu(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_MENU, rmDiagName,
"UI");
610 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CONFIG_DEBUG,
"",
"Override config", rmDiagName);
611 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_RELEASE_OPEN,
"",
"Close on open", rmDiagName);
612 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_PERFORM_ON_CLOSE,
"",
"Perform on close", rmDiagName);
613 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_PERFORM,
"",
"Close on perform", rmDiagName);
614 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_DESELECT_IN_CENTER,
"",
"Delect in center", rmDiagName);
615 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_USE_LARGE_SIZE,
"",
"Large size", rmDiagName);
617 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CUSTOM_SIZE_ENABLE,
"",
"Override size", rmDiagName);
618 DiagMenu.RegisterRange(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CUSTOM_SIZE,
"",
"Sustom size", rmDiagName,
"100, 1000, 100, 1");
620 DiagMenu.RegisterBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_ENTRY_COUNT_ENABLE,
"",
"Entries setup", rmDiagName);
621 DiagMenu.RegisterRange(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_ENTRY_COUNT,
"",
"Entries", rmDiagName,
"2, 32, 2, 1");
625 protected void DebugCleanup()
628 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_RELEASE_OPEN);
629 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_PERFORM_ON_CLOSE);
630 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_PERFORM);
631 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_DESELECT_IN_CENTER);
632 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_USE_LARGE_SIZE);
633 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CUSTOM_SIZE_ENABLE);
635 DiagMenu.Unregister(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_ENTRY_COUNT_ENABLE);
644 if (!DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CONFIG_DEBUG))
647 inputs.m_bCloseOnReleaseOpen = DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_RELEASE_OPEN);
648 inputs.m_bPerformOnClose = DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_PERFORM_ON_CLOSE);
649 inputs.m_bCloseOnPerform = DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CLOSE_ON_PERFORM);
650 inputs.m_bDeselectInCenter = DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_DESELECT_IN_CENTER);
651 inputs.m_bUseLargeSize = DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_USE_LARGE_SIZE);
653 if (DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CUSTOM_SIZE_ENABLE))
654 inputs.m_fCustomSize = DiagMenu.GetRangeValue(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_CUSTOM_SIZE);
656 inputs.m_fCustomSize = -1;
660 protected void DebugFillWithEntries()
662 if (!DiagMenu.GetBool(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_ENTRY_COUNT_ENABLE))
667 int count = DiagMenu.GetRangeValue(
SCR_DebugMenuID.DEBUGUI_RADIALMENU_ENTRY_COUNT);
668 for (
int i = 0; i < count; i++)
671 entry.SetName(
"Debug entry " + i);
684 DebugSetupDiagMenu();
697 void SetPreventSelectionContext(
bool enable)
699 m_bPreventSelectionContext = enable;
703 bool GetPreventSelectionContext()
705 return m_bPreventSelectionContext;
709 bool GetUseQuickActions()
711 if (!m_RadialControllerInputs)
714 return m_RadialControllerInputs.m_bUseQuickActions;
725 protected const string RADIAL_LEFT_CONTEXT =
"RadialMenuLeftContext";
726 protected const string RADIAL_RIGHT_CONTEXT =
"RadialMenuRightContext";
727 protected const string DEFAULT_RADIAL_LEFT_X =
"RadialX";
728 protected const string DEFAULT_RADIAL_LEFT_Y =
"RadialY";
729 protected const string DEFAULT_RADIAL_RIGHT_X =
"RadialX2";
730 protected const string DEFAULT_RADIAL_RIGHT_Y =
"RadialY2";
732 [
Attribute(
"100",
desc:
"How far from center needs to be to select entry. Use when controller m_bDeselectInCenter = true")]
733 float m_fMouseSelectionTreshold;
735 [
Attribute(
"250", UIWidgets.Slider,
desc:
"Delay in ms for how long should context be active - prevents character rotation and other unwanted input",
"0 1000 1")]
736 int m_iContextDeactivationTime;
738 [
Attribute(
"1",
desc:
"Allow dynamically setup inner mouse selection treshold base on menu size from SIZE_LARGE")]
739 bool m_bDynamicMouseTreshold;
741 [
Attribute(
"0.5",
"0 1 0.01",
desc:
"How far from center needs to be to select entry. Use when controller m_bDeselectInCenter = true")]
742 float m_fGamepadSelectionTreshhold;
744 [
Attribute(
"500", UIWidgets.Slider,
desc:
"How long in milisecond should selection stay when moving to center.",
"0 1000 1")]
745 int m_iGamepadDeselectionDelay;
747 protected bool m_bUseRightStick;
754 SetUseRightStick(m_bUseRightStick);
758 void GetRadialXYInput(out
float x, out
float y)
760 InputManager inputManager =
GetGame().GetInputManager();
761 if (m_bUseRightStick)
763 inputManager.ActivateContext(RADIAL_RIGHT_CONTEXT, m_iGamepadDeselectionDelay);
764 x = inputManager.GetActionValue(DEFAULT_RADIAL_RIGHT_X);
765 y = inputManager.GetActionValue(DEFAULT_RADIAL_RIGHT_Y);
769 inputManager.ActivateContext(RADIAL_LEFT_CONTEXT, m_iGamepadDeselectionDelay);
770 x = inputManager.GetActionValue(DEFAULT_RADIAL_LEFT_X);
771 y = inputManager.GetActionValue(DEFAULT_RADIAL_LEFT_Y);
776 void SetUseRightStick(
bool use)
778 m_bUseRightStick = use;
786 const static int MAX_HINTS = 9;
788 [
Attribute(
"1",
desc:
"True = right thumbstick used for menu navigation, otherwise use left thumbstick")]
789 bool m_bUseRightStick;
791 [
Attribute(
"1",
desc:
"Select no entry if selection is pointing into menu center")]
792 bool m_bDeselectInCenter;
794 [
Attribute(
"1",
desc:
"Use large or small size for radial menu visuals")]
795 bool m_bUseLargeSize;
797 [
Attribute(
"-1", UIWidgets.Slider,
desc:
"Base radial menu size. Will ignore m_bUseLargeSize and use custom size instead of presets if it's more than 0",
"-1 1000 1")]
800 [
Attribute(
"0", UIWidgets.Slider,
desc:
"Display the crosshair in the center of the layout")]
801 bool m_bShowCrosshair;
803 [
Attribute(
"1", UIWidgets.Slider,
desc:
"Display inner circle backround if true")]
804 bool m_bShowInnerBackground;
806 [
Attribute(
"0", UIWidgets.Slider,
desc:
"Allows to use entries with quick 1-10 input")]
807 bool m_bUseQuickActions;