A game about forced loneliness, made by TACStudios
1using System.ComponentModel;
2using UnityEngine.InputSystem.Layouts;
3using UnityEngine.InputSystem.Utilities;
4using UnityEngine.Scripting;
5
6////TODO: remove this once we can break the API
7
8namespace UnityEngine.InputSystem.Composites
9{
10 /// <summary>
11 /// A button with two additional modifiers. The button only triggers when
12 /// both modifiers are pressed.
13 /// </summary>
14 /// <remarks>
15 /// This composite can be used to require two other buttons to be held while
16 /// using the control that triggers the action. This is most commonly used
17 /// on keyboards to require two of the modifier keys (shift, ctrl, or alt)
18 /// to be held in combination with another key, e.g. "CTRL+SHIFT+1".
19 ///
20 /// <example>
21 /// <code>
22 /// // Create a button action that triggers when CTRL+SHIFT+1
23 /// // is pressed on the keyboard.
24 /// var action = new InputAction(type: InputActionType.Button);
25 /// action.AddCompositeBinding("TwoModifiers")
26 /// .With("Modifier1", "<Keyboard>/leftCtrl")
27 /// .With("Modifier1", "<Keyboard>/rightCtrl")
28 /// .With("Modifier2", "<Keyboard>/leftShift")
29 /// .With("Modifier2", "<Keyboard>/rightShift")
30 /// .With("Button", "<Keyboard>/1")
31 /// </code>
32 /// </example>
33 ///
34 /// Note that this is not restricted to the keyboard and will preserve
35 /// the full value of the button.
36 ///
37 /// <example>
38 /// <code>
39 /// // Create a button action that requires the A and X button on the
40 /// // gamepad to be held and will then trigger from the gamepad's
41 /// // left trigger button.
42 /// var action = new InputAction(type: InputActionType.Button);
43 /// action.AddCompositeBinding("ButtonWithTwoModifiers")
44 /// .With("Modifier1", "<Gamepad>/buttonSouth")
45 /// .With("Modifier2", "<Gamepad>/buttonWest")
46 /// .With("Button", "<Gamepad>/leftTrigger");
47 /// </code>
48 /// </example>
49 /// </remarks>
50 /// <seealso cref="ButtonWithOneModifier"/>
51 [DesignTimeVisible(false)] // Obsoleted by TwoModifiersComposite
52 [DisplayStringFormat("{modifier1}+{modifier2}+{button}")]
53 public class ButtonWithTwoModifiers : InputBindingComposite<float>
54 {
55 /// <summary>
56 /// Binding for the first button that acts as a modifier, e.g. <c><Keyboard/leftCtrl</c>.
57 /// </summary>
58 /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
59 /// <remarks>
60 /// This property is automatically assigned by the input system.
61 /// </remarks>
62 // ReSharper disable once MemberCanBePrivate.Global
63 // ReSharper disable once FieldCanBeMadeReadOnly.Global
64 // ReSharper disable once UnassignedField.Global
65 [InputControl(layout = "Button")] public int modifier1;
66
67 /// <summary>
68 /// Binding for the second button that acts as a modifier, e.g. <c><Keyboard/leftCtrl</c>.
69 /// </summary>
70 /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
71 /// <remarks>
72 /// This property is automatically assigned by the input system.
73 /// </remarks>
74 // ReSharper disable once MemberCanBePrivate.Global
75 // ReSharper disable once FieldCanBeMadeReadOnly.Global
76 // ReSharper disable once UnassignedField.Global
77 [InputControl(layout = "Button")] public int modifier2;
78
79 /// <summary>
80 /// Binding for the button that is gated by <see cref="modifier1"/> and <see cref="modifier2"/>.
81 /// The composite will assume the value of this button while both of the modifiers are pressed.
82 /// </summary>
83 /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
84 /// <remarks>
85 /// This property is automatically assigned by the input system.
86 /// </remarks>
87 // ReSharper disable once MemberCanBePrivate.Global
88 // ReSharper disable once FieldCanBeMadeReadOnly.Global
89 // ReSharper disable once UnassignedField.Global
90 [InputControl(layout = "Button")] public int button;
91
92 /// <summary>
93 /// If set to <c>true</c>, <see cref="modifier1"/> and/or <see cref="modifier2"/> can be pressed after <see cref="button"/>
94 /// and the composite will still trigger. Default is false.
95 /// </summary>
96 /// <remarks>
97 /// By default, <see cref="modifier1"/> and <see cref="modifier2"/> are required to be in pressed state before or at the same
98 /// time that <see cref="button"/> goes into pressed state for the composite as a whole to trigger. This means that binding to,
99 /// for example, <c>Ctrl+Shift+B</c>, the <c>ctrl</c> <c>shift</c> keys have to be pressed before pressing the <c>B</c> key.
100 /// This is the behavior usually expected with keyboard shortcuts.
101 ///
102 /// This parameter can be used to bypass this behavior and allow any timing between <see cref="modifier1"/>, <see cref="modifier2"/>,
103 /// and <see cref="button"/>. The only requirement is for all of them to concurrently be in pressed state.
104 /// </remarks>
105 public bool overrideModifiersNeedToBePressedFirst;
106
107 /// <summary>
108 /// Return the value of the <see cref="button"/> part while both <see cref="modifier1"/> and <see cref="modifier2"/>
109 /// are pressed. Otherwise return 0.
110 /// </summary>
111 /// <param name="context">Evaluation context passed in from the input system.</param>
112 /// <returns>The current value of the composite.</returns>
113 public override float ReadValue(ref InputBindingCompositeContext context)
114 {
115 if (ModifiersArePressed(ref context))
116 return context.ReadValue<float>(button);
117
118 return default;
119 }
120
121 private bool ModifiersArePressed(ref InputBindingCompositeContext context)
122 {
123 var modifiersDown = context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2);
124
125 if (modifiersDown && !overrideModifiersNeedToBePressedFirst)
126 {
127 var timestamp = context.GetPressTime(button);
128 var timestamp1 = context.GetPressTime(modifier1);
129 var timestamp2 = context.GetPressTime(modifier2);
130
131 return timestamp1 <= timestamp && timestamp2 <= timestamp;
132 }
133
134 return modifiersDown;
135 }
136
137 /// <summary>
138 /// Same as <see cref="ReadValue"/> in this case.
139 /// </summary>
140 /// <param name="context">Evaluation context passed in from the input system.</param>
141 /// <returns>A >0 value if the composite is currently actuated.</returns>
142 public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
143 {
144 return ReadValue(ref context);
145 }
146
147 protected override void FinishSetup(ref InputBindingCompositeContext context)
148 {
149 if (!overrideModifiersNeedToBePressedFirst)
150 overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
151 }
152 }
153}