1 module prova.input.input;
2 
3 import core.stdc.string;
4 import derelict.sdl2.sdl;
5 import prova.core;
6 import prova.input;
7 import prova.math;
8 
9 ///
10 class Input
11 {
12   private Game game;
13   private Vector2 mousePosition;
14   private Controller[int] controllers;
15   private bool[] keystate;
16   private bool[] oldKeystate;
17   private bool[Key] repeatedKeyMap;
18   private bool[] buttonState;
19   private bool[] oldButtonState;
20   private string text = "";
21 
22   package(prova) this(Game game)
23   {
24     this.game = game;
25 
26     update();
27     oldKeystate = keystate;
28     oldButtonState = buttonState;
29   }
30 
31   package(prova) void reset()
32   {
33     repeatedKeyMap.clear();
34   }
35 
36   package(prova) void update()
37   {
38     // update controller state
39     foreach(Controller controller; controllers)
40       controller.update();
41 
42     // update keystate
43     oldKeystate = keystate;
44     updateKeystate();
45 
46     // update mouse state
47     int x, y;
48     uint mouseState = SDL_GetMouseState(&x, &y);
49 
50     // update button state
51     oldButtonState = buttonState;
52     updateButtonState(mouseState);
53 
54     mousePosition.x = -1f + x / (game.screen.width * .5f);
55     mousePosition.y = 1f - y / (game.screen.height * .5f);
56   }
57 
58   private void updateKeystate()
59   {
60     int keystateLength;
61     const ubyte* SDLKeystate = SDL_GetKeyboardState(&keystateLength);
62 
63     keystate = [];
64     keystate.length = keystateLength;
65 
66     (cast(ubyte*)keystate)[0 .. keystateLength][] = SDLKeystate[0 .. keystateLength];
67   }
68 
69   private void updateButtonState(uint mouseState)
70   {
71     buttonState = [];
72     buttonState.length = 5;
73 
74     foreach(i; 0 .. 5) {
75       ubyte button = SDL_BUTTON(cast(ubyte) (i + 1));
76       buttonState[i] = cast(bool) (mouseState & button);
77     }
78   }
79 
80   package(prova) void setKeyDown(SDL_Keycode keycode) {
81     Key key = cast(Key) SDL_GetScancodeFromKey(keycode);
82 
83     if(isKeyDown(key)) {
84       repeatedKeyMap[key] = true;
85     }
86   }
87 
88   package(prova) void updateTextInput(string text)
89   {
90     const bool enteredNewLine =
91       keyJustPressed(Key.KP_ENTER) || isKeyRepeated(Key.KP_ENTER) ||
92       keyJustPressed(Key.RETURN) || isKeyRepeated(Key.RETURN);
93 
94     if(enteredNewLine) {
95       text ~= '\n';
96     }
97 
98     this.text = text;
99   }
100 
101   /// 
102   Controller getController(int index)
103   {
104     // controller not found then add it to our list
105     // of updated controllers
106     if(!(index in controllers))
107       controllers[index] = new Controller(index);
108 
109     return controllers[index];
110   }
111 
112   /// Uses the WASD format, returns a normalized vector
113   Vector2 simulateStick(Key up, Key left, Key down, Key right)
114   {
115     Vector2 vector = Vector2(
116       isKeyDown(right) - isKeyDown(left),
117       isKeyDown(up) - isKeyDown(down)
118     );
119 
120     return vector.getNormalized();
121   }
122 
123   ///
124   bool isKeyDown(Key key)
125   {
126     return keystate[cast(int) key];
127   }
128 
129   ///
130   bool isKeyUp(Key key)
131   {
132     return !keystate[cast(int) key];
133   }
134 
135   /// Returns true if the key is pressed and was not pressed in the last tick
136   bool keyJustPressed(Key key)
137   {
138     return !oldKeystate[cast(int) key] && keystate[cast(int) key];
139   }
140 
141   /**
142    * Returns true if a key down event was received before the key was released
143    * Useful for creating text inputs
144    */
145   bool isKeyRepeated(Key key)
146   {
147     return (key in repeatedKeyMap) != null;
148   }
149 
150   ///
151   bool isMouseButtonDown(MouseButton button)
152   {
153     return buttonState[button];
154   }
155 
156   ///
157   bool isMouseButtonUp(MouseButton button)
158   {
159     return !buttonState[button];
160   }
161 
162   ///
163   bool mouseButtonClicked(MouseButton button)
164   {
165     return oldButtonState[button] && !buttonState[button];
166   }
167 
168   ///
169   Vector2 getMousePosition()
170   {
171     return mousePosition;
172   }
173 
174   ///
175   void startTextInput()
176   {
177     SDL_StartTextInput();
178   }
179 
180   ///
181   void stopTextInput()
182   {
183     SDL_StopTextInput();
184     text = "";
185   }
186 
187   ///
188   string getTextInput()
189   {
190     return text;
191   }
192 }