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