1 module prova.input.binding;
2 
3 import prova.input;
4 import prova.math;
5 
6 /// Allows representation of input from multiple sources in a uniform way
7 class Binding
8 {
9   private Controller controller;
10   private Input input;
11   private Key[][int] boundKeyButtons;
12   private ControllerButton[][int] boundControllerButtons;
13   private Key[4][][int] boundKeySticks;
14   private ThumbStick[][int] boundControllerSticks;
15 
16   /// Forward of deadzone on Controller
17   @property float deadzone()
18   {
19     return controller.deadzone;
20   }
21 
22   /// Forward of deadzone on Controller
23   @property void deadzone(float deadzone)
24   {
25     controller.deadzone = deadzone;
26   }
27 
28   /// Must be called before using any other method
29   void bindInput(Input input)
30   {
31     assert(input, "Input should not be null. Did you bind in a constructor rather than during setup()?");
32 
33     this.input = input;
34   }
35 
36   ///
37   void bindController(int index)
38   {
39     assert(input, "Input should be bound");
40     
41     controller = input.getController(index);
42   }
43 
44   ///
45   void bindButton(int button, Key key)
46   {
47     boundKeyButtons[button] ~= key;
48   }
49 
50   ///
51   void bindButton(int button, ControllerButton controllerButton)
52   {
53     boundControllerButtons[button] ~= controllerButton;
54   }
55 
56   /**
57    * Simulates a joystick using four keys
58    *
59    * Uses a WASD format for ordering directions
60    */
61   void bindStick(int stick, Key up, Key left, Key down, Key right)
62   {
63     Key[4] simulatedStick = [up, left, down, right];
64     boundKeySticks[stick] ~= simulatedStick;
65   }
66 
67   ///
68   void bindStick(int stick, ThumbStick joystick)
69   {
70     boundControllerSticks[stick] ~= joystick;
71   }
72 
73   ///
74   bool isButtonDown(int button)
75   {
76     assert(input, "Input should be bound");
77 
78     return isKeyDown(button) || isControllerButtonDown(button);
79   }
80 
81   private bool isKeyDown(int button)
82   {
83     if(!(button in boundKeyButtons))
84       return false;
85 
86     // loop through bound keys and return true if just pressed
87     Key[] keyButtons = boundKeyButtons[button];
88 
89     foreach(Key keyButton; keyButtons)
90       if(input.isKeyDown(keyButton))
91         return true;
92 
93     return false;
94   }
95 
96   private bool isControllerButtonDown(int button)
97   {
98     // do not test controller bindings if the controller isn't set
99     // or if there are no buttons bound
100     if(!controller || !(button in boundControllerButtons))
101       return false;
102 
103     // loop through bound buttons and return true if just pressed
104     ControllerButton[] controllerButtons = boundControllerButtons[button];
105 
106     foreach(ControllerButton controllerButton; controllerButtons)
107       if(controller.isButtonDown(controllerButton))
108         return true;
109 
110     return false;
111   }
112 
113   ///
114   bool isButtonUp(int button)
115   {
116     return !isButtonDown(button);
117   }
118 
119   ///
120   bool buttonJustPressed(int button)
121   {
122     assert(input, "Input should be bound");
123 
124     return keyJustPressed(button) || controllerButtonJustPressed(button);
125   }
126 
127   private bool keyJustPressed(int button)
128   {
129     if(!(button in boundKeyButtons))
130       return false;
131 
132     // loop through bound keys and return true if just pressed
133     Key[] keyButtons = boundKeyButtons[button];
134 
135     foreach(Key keyButton; keyButtons)
136       if(input.keyJustPressed(keyButton))
137         return true;
138 
139     return false;
140   }
141 
142   private bool controllerButtonJustPressed(int button)
143   {
144     // do not test controller bindings if the controller isn't set
145     // or if there are no buttons bound
146     if(!controller || !(button in boundControllerButtons))
147       return false;
148 
149     // loop through bound buttons and return true if just pressed
150     ControllerButton[] controllerButtons = boundControllerButtons[button];
151 
152     foreach(ControllerButton controllerButton; controllerButtons)
153       if(controller.buttonJustPressed(controllerButton))
154         return true;
155 
156     return false;
157   }
158 
159   ///
160   Vector2 getStick(int stick)
161   {
162     assert(input, "Input must be bound");
163     
164     Vector2 result;
165     Vector2 zero;
166 
167     result = getSimulatedStick(stick);
168 
169     if(result != zero)
170       return result;
171 
172     result = getJoyStick(stick);
173 
174     if(result != zero)
175       return result;
176 
177     return zero;
178   }
179 
180   private Vector2 getSimulatedStick(int stick)
181   {
182     Vector2 zero;
183 
184     if(!(stick in boundKeySticks))
185       return zero;
186 
187     // loop through bound sticks and return only if it does not equal zero
188     Key[4][] keySticks = boundKeySticks[stick];
189 
190     foreach(Key[4] keyStick; keySticks) {
191       Vector2 vector = input.simulateStick(
192         keyStick[0], keyStick[1], keyStick[2], keyStick[3]
193       );
194       
195       if(vector != zero)
196         return vector;
197     }
198 
199     return zero;
200   }
201 
202   private Vector2 getJoyStick(int stick)
203   {
204     Vector2 zero;
205 
206     // do not test controller bindings if the controller isn't set,
207     // or if there are no sticks bound to the controller
208     if(!controller || !(stick in boundControllerSticks))
209       return zero;
210 
211     // loop through bound sticks and return only if it does not equal zero
212     ThumbStick[] thumbsticks = boundControllerSticks[stick];
213 
214     foreach(ThumbStick thumbstick; thumbsticks) {
215       Vector2 vector = controller.getStick(thumbstick);
216 
217       if(vector != zero)
218         return vector;
219     }
220 
221     return zero;
222   }
223 }