/ld26-basecode

To get this branch, use:
bzr branch /bzr/ld26-basecode

« back to all changes in this revision

Viewing changes to zoetrope/input/gamepad.lua

  • Committer: Josh C
  • Date: 2013-04-23 15:22:15 UTC
  • Revision ID: josh@9ix.org-20130423152215-hacpxl91eiiq2ras
zoetrope 1.4

Show diffs side-by-side

added added

removed removed

 
1
-- Class: Gamepad
 
2
-- This represents a single gamepad connected to the user's computer. This offers the 
 
3
-- usual functions to check on the status of buttons; you can also inspect other
 
4
-- controls, like analog joysticks, by checking properties like <axes> and <hats>.
 
5
-- (Incidentally, a hat is used for things like a digital control pad.) 
 
6
--
 
7
-- Normally, gamepad buttons are indexed by number. This class also adds virtual buttons
 
8
-- named 'left', 'right', 'up', 'down'. This consults the first two analog axes
 
9
-- and the first hat of the gamepad (if they exist) to set these.
 
10
--
 
11
-- A gamepad is only recognized if it is plugged in when LOVE starts up. There's no way
 
12
-- to tell if the user has unplugged it after that; it just acts inert. A gamepad object
 
13
-- with no connected hardware at startup will exist and respond to calls like <pressed()>,
 
14
-- but it will always return false. You can tell it's unplugged because its active property
 
15
-- will be false, and its name will always be 'NO DEVICE CONNECTED'.
 
16
--
 
17
-- Extends:
 
18
--              <Sprite>
 
19
 
 
20
Gamepad = Sprite:extend
 
21
{
 
22
        -- Property: number
 
23
        -- The index of the gamepad, starting at 1.
 
24
 
 
25
        -- Property: name
 
26
        -- The name of the gamepad, e.g. "XBox Controller".
 
27
 
 
28
        -- Property: numAxes
 
29
        -- The number of available axes, e.g. for analog controls.
 
30
 
 
31
        -- Property: numBalls
 
32
        -- The number of available balls.
 
33
 
 
34
        -- Property: numButtons
 
35
        -- The number of available buttons.
 
36
 
 
37
        -- Property: numHats
 
38
        -- The number of available hats, e.g. digital movement controls.
 
39
 
 
40
        -- Property: axes
 
41
        -- The state of all analog axes on the gamepad, indexed by number.
 
42
        -- Values range from -1 to 1, where 0 is completely neutral.
 
43
 
 
44
        -- Property: balls
 
45
        -- The amount of motion by a each ball on the gamepad, indexed by number.
 
46
        -- Not sure what the range of values is here.
 
47
 
 
48
        -- Property: hats
 
49
        -- The state of each hat on the gamepad, indexed by number. Each one has
 
50
        -- one of these values: https://love2d.org/wiki/JoystickConstant
 
51
 
 
52
        -- Property: deadZone
 
53
        -- Any motion by an analog control (from 0 to 1) less than this value is
 
54
        -- ignored when simulating digital controls.
 
55
 
 
56
        deadZone = 0.1,
 
57
 
 
58
        -- private property: _thisFrame
 
59
        -- what keys are pressed this frame
 
60
        -- if you are interested in this, use allPressed() instead
 
61
 
 
62
        _thisFrame = {},
 
63
 
 
64
        -- private property: _lastFrame
 
65
        -- what buttons were pressed last frame
 
66
 
 
67
        _lastFrame = {},
 
68
 
 
69
        new = function (self, obj)
 
70
                obj = self:extend(obj)
 
71
                assert(type(obj.number) == 'number', 'must set a gamepad number')
 
72
 
 
73
                obj.axes = {}
 
74
                obj.balls = {}
 
75
                obj.hats = {}
 
76
 
 
77
                if obj.number <= love.joystick.getNumJoysticks() then
 
78
                        if not love.joystick.isOpen(obj.number) then love.joystick.open(obj.number) end
 
79
                        obj.name = love.joystick.getName(obj.number)
 
80
                        obj.numAxes = love.joystick.getNumAxes(obj.number)
 
81
                        obj.numBalls = love.joystick.getNumBalls(obj.number)
 
82
                        obj.numButtons = love.joystick.getNumButtons(obj.number)
 
83
                        obj.numHats = love.joystick.getNumHats(obj.number)
 
84
 
 
85
                        -- set initial values for axes and balls
 
86
                        -- hat values are strings so nil comparisons are safe
 
87
 
 
88
                        for i = 1, obj.numAxes do
 
89
                                obj.axes[i] = 0
 
90
                        end
 
91
 
 
92
                        for i = 1, obj.numBalls do
 
93
                                obj.balls[i] = { x = 0, y = 0 }
 
94
                        end
 
95
                else
 
96
                        obj.name = 'NO DEVICE CONNECTED'
 
97
                        obj.numAxes = 0
 
98
                        obj.numBalls = 0
 
99
                        obj.numButtons = 0
 
100
                        obj.numHats = 0
 
101
                end
 
102
 
 
103
                love.joystickpressed = Gamepad._dispatchPress
 
104
                love.joystickreleased = Gamepad._dispatchRelease
 
105
                if obj.onNew then obj:onNew() end
 
106
                return obj
 
107
        end,
 
108
 
 
109
        -- Method: pressed
 
110
        -- Are *any* of the buttons passed held down this frame?
 
111
        --
 
112
        -- Arguments:
 
113
        --              button numbers passed as individual arguments
 
114
        --
 
115
        -- Returns:
 
116
        --              boolean
 
117
 
 
118
        pressed = function (self, ...)
 
119
                local buttons = {...}
 
120
 
 
121
                for _, value in pairs(buttons) do
 
122
                        if self._thisFrame[value] then
 
123
                                return true
 
124
                        end
 
125
                end
 
126
                
 
127
                return false
 
128
        end,
 
129
 
 
130
        -- Method: justPressed
 
131
        -- Are *any* of the buttons passed pressed for the first time this frame?
 
132
        --
 
133
        -- Arguments:
 
134
        --              button numbers passed as individual arguments
 
135
        --
 
136
        -- Returns:
 
137
        --              boolean
 
138
 
 
139
        justPressed = function (self, ...)
 
140
                local buttons = {...}
 
141
 
 
142
                for _, value in pairs(buttons) do
 
143
                        if self._thisFrame[value] and not self._lastFrame[value] then
 
144
                                return true
 
145
                        end
 
146
                end
 
147
                
 
148
                return false
 
149
        end,
 
150
 
 
151
        -- Method: released
 
152
        -- Are *all* of the buttons passed not held down this frame?
 
153
        --
 
154
        -- Arguments:
 
155
        --              button numbers passed as individual arguments
 
156
        --
 
157
        -- Returns:
 
158
        --              boolean
 
159
 
 
160
        released = function (self, ...)
 
161
                local buttons = {...}
 
162
        
 
163
                for _, value in pairs(buttons) do
 
164
                        if self._thisFrame[value] then
 
165
                                return false
 
166
                        end
 
167
                end
 
168
                
 
169
                return true
 
170
        end,
 
171
 
 
172
        -- Method: justReleased
 
173
        -- Are *any* of the buttons passed released after being held last frame?
 
174
        --
 
175
        -- Arguments:
 
176
        --              button numbers passed as individual arguments
 
177
        --
 
178
        -- Returns:
 
179
        --              boolean
 
180
 
 
181
        justReleased = function (self, ...)
 
182
                local buttons = {...}
 
183
        
 
184
                for _, value in pairs(buttons) do
 
185
                        if self._lastFrame[value] and not self._thisFrame[value] then
 
186
                                return true
 
187
                        end
 
188
                end
 
189
                
 
190
                return false
 
191
        end,
 
192
 
 
193
        -- Method: allPressed
 
194
        -- Returns all buttons currently pressed this frame.
 
195
        --
 
196
        -- Arguments:
 
197
        --              none
 
198
        --
 
199
        -- Returns:
 
200
        --              string button descriptions; if nothing is pressed, nil
 
201
 
 
202
        allPressed = function (self)
 
203
                local result = {}
 
204
 
 
205
                for key, value in pairs(self._thisFrame) do
 
206
                        if value then table.insert(result, key) end
 
207
                end
 
208
                
 
209
                return unpack(result)
 
210
        end,
 
211
 
 
212
        -- Method: allJustPressed
 
213
        -- Returns all buttons just pressed this frame.
 
214
        --
 
215
        -- Arguments:
 
216
        --              none
 
217
        --
 
218
        -- Returns:
 
219
        --              string button descriptions; if nothing is just pressed, nil
 
220
 
 
221
        allJustPressed = function (self)
 
222
                local result = {}
 
223
 
 
224
                for key, value in pairs(self._thisFrame) do
 
225
                        if value and not self._lastFrame[key] then table.insert(result, key) end
 
226
                end
 
227
                
 
228
                return unpack(result)
 
229
        end,
 
230
 
 
231
        -- Method: allJustReleased
 
232
        -- Returns all buttons just released this frame.
 
233
        --
 
234
        -- Arguments:
 
235
        --              none
 
236
        --
 
237
        -- Returns:
 
238
        --              string button descriptions; if nothing is just pressed, nil
 
239
 
 
240
        allJustReleased = function (self)
 
241
                local result = {}
 
242
 
 
243
                for key, value in pairs(self._thisFrame) do
 
244
                        if not value and self._lastFrame[key] then table.insert(result, key) end
 
245
                end
 
246
                
 
247
                return unpack(result)
 
248
        end,
 
249
 
 
250
        buttonPressed = function (self, button)
 
251
                self._thisFrame[button] = true
 
252
        end,
 
253
 
 
254
        buttonReleased = function (self, button)
 
255
                self._thisFrame[button] = false
 
256
        end,
 
257
 
 
258
        endFrame = function (self, elapsed)
 
259
                -- move button values to the previous frame
 
260
 
 
261
                for key, value in pairs(self._thisFrame) do
 
262
                        self._lastFrame[key] = value
 
263
                end
 
264
 
 
265
                -- set values
 
266
 
 
267
                for i = 1, self.numAxes do
 
268
                        self.axes[i] = love.joystick.getAxis(self.number, i)
 
269
                end
 
270
 
 
271
                for i = 1, self.numBalls do
 
272
                        self.balls[i].x, self.balls[i].y = love.joystick.getBall(self.number, i)
 
273
                end
 
274
 
 
275
                for i = 1, self.numHats do
 
276
                        self.hats[i] = love.joystick.getHat(self.number, i)
 
277
                end
 
278
 
 
279
                -- simulate digital controls
 
280
 
 
281
                self._thisFrame['up'] = false
 
282
                self._thisFrame['down'] = false
 
283
                self._thisFrame['left'] = false
 
284
                self._thisFrame['right'] = false
 
285
 
 
286
                if self.numHats > 0 then
 
287
                        local hat = self.hats[1]
 
288
 
 
289
                        if hat == 'u' then
 
290
                                self._thisFrame['up'] = true
 
291
                        elseif hat == 'd' then
 
292
                                self._thisFrame['down'] = true
 
293
                        elseif hat == 'l' then
 
294
                                self._thisFrame['left'] = true
 
295
                        elseif hat == 'r' then
 
296
                                self._thisFrame['right'] = true
 
297
                        elseif hat == 'lu' then
 
298
                                self._thisFrame['up'] = true
 
299
                                self._thisFrame['left'] = true
 
300
                        elseif hat == 'ru' then
 
301
                                self._thisFrame['up'] = true
 
302
                                self._thisFrame['right'] = true
 
303
                        elseif hat == 'ld' then
 
304
                                self._thisFrame['down'] = true
 
305
                                self._thisFrame['left'] = true
 
306
                        elseif hat == 'rd' then
 
307
                                self._thisFrame['down'] = true
 
308
                                self._thisFrame['right'] = true
 
309
                        end
 
310
                end
 
311
 
 
312
                if self.numAxes > 1 then
 
313
                        local xAxis = self.axes[1]
 
314
                        local yAxis = self.axes[2]
 
315
 
 
316
                        if math.abs(xAxis) > self.deadZone then
 
317
                                if xAxis < 0 then
 
318
                                        self._thisFrame['left'] = true
 
319
                                else
 
320
                                        self._thisFrame['right'] = true
 
321
                                end
 
322
                        end
 
323
 
 
324
                        if math.abs(yAxis) > self.deadZone then
 
325
                                if yAxis < 0 then
 
326
                                        self._thisFrame['up'] = true
 
327
                                else
 
328
                                        self._thisFrame['down'] = true
 
329
                                end
 
330
                        end
 
331
                end
 
332
        
 
333
                Sprite.endFrame(self)
 
334
        end,
 
335
 
 
336
        -- private method: _dispatchPress
 
337
        -- receives a Love joystickpressed callback and hands it off
 
338
        -- to the appropriate gamepad.
 
339
 
 
340
        _dispatchPress = function (number, button)
 
341
                if the.gamepads[number] then
 
342
                        the.gamepads[number]:buttonPressed(button)
 
343
                end
 
344
        end,
 
345
 
 
346
        -- private method: _dispatchRelease
 
347
        -- receives a Love joystickreleased callback and hands it off
 
348
        -- to the appropriate gamepad.
 
349
 
 
350
        _dispatchRelease = function (number, button)
 
351
                if the.gamepads[number] then
 
352
                        the.gamepads[number]:buttonReleased(button)
 
353
                end
 
354
        end
 
355
}