/traderous

To get this branch, use:
bzr branch /bzr/traderous
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
-- Class: Gamepad
-- This represents a single gamepad connected to the user's computer. This offers the 
-- usual functions to check on the status of buttons; you can also inspect other
-- controls, like analog joysticks, by checking properties like <axes> and <hats>.
-- (Incidentally, a hat is used for things like a digital control pad.) 
--
-- Normally, gamepad buttons are indexed by number. This class also adds virtual buttons
-- named 'left', 'right', 'up', 'down'. This consults the first two analog axes
-- and the first hat of the gamepad (if they exist) to set these.
--
-- A gamepad is only recognized if it is plugged in when LOVE starts up. There's no way
-- to tell if the user has unplugged it after that; it just acts inert. A gamepad object
-- with no connected hardware at startup will exist and respond to calls like <pressed()>,
-- but it will always return false. You can tell it's unplugged because its active property
-- will be false, and its name will always be 'NO DEVICE CONNECTED'.
--
-- Extends:
--		<Sprite>

Gamepad = Sprite:extend
{
	-- Property: number
	-- The index of the gamepad, starting at 1.

	-- Property: name
	-- The name of the gamepad, e.g. "XBox Controller".

	-- Property: numAxes
	-- The number of available axes, e.g. for analog controls.

	-- Property: numBalls
	-- The number of available balls.

	-- Property: numButtons
	-- The number of available buttons.

	-- Property: numHats
	-- The number of available hats, e.g. digital movement controls.

	-- Property: axes
	-- The state of all analog axes on the gamepad, indexed by number.
	-- Values range from -1 to 1, where 0 is completely neutral.

	-- Property: balls
	-- The amount of motion by a each ball on the gamepad, indexed by number.
	-- Not sure what the range of values is here.

	-- Property: hats
	-- The state of each hat on the gamepad, indexed by number. Each one has
	-- one of these values: https://love2d.org/wiki/JoystickConstant

	-- Property: deadZone
	-- Any motion by an analog control (from 0 to 1) less than this value is
	-- ignored when simulating digital controls.

	deadZone = 0.1,

	-- private property: _thisFrame
	-- what keys are pressed this frame
	-- if you are interested in this, use allPressed() instead

	_thisFrame = {},

	-- private property: _lastFrame
	-- what buttons were pressed last frame

	_lastFrame = {},

	new = function (self, obj)
		obj = self:extend(obj)
		assert(type(obj.number) == 'number', 'must set a gamepad number')

		obj.axes = {}
		obj.balls = {}
		obj.hats = {}

		if obj.number <= love.joystick.getNumJoysticks() then
			if not love.joystick.isOpen(obj.number) then love.joystick.open(obj.number) end
			obj.name = love.joystick.getName(obj.number)
			obj.numAxes = love.joystick.getNumAxes(obj.number)
			obj.numBalls = love.joystick.getNumBalls(obj.number)
			obj.numButtons = love.joystick.getNumButtons(obj.number)
			obj.numHats = love.joystick.getNumHats(obj.number)

			-- set initial values for axes and balls
			-- hat values are strings so nil comparisons are safe

			for i = 1, obj.numAxes do
				obj.axes[i] = 0
			end

			for i = 1, obj.numBalls do
				obj.balls[i] = { x = 0, y = 0 }
			end
		else
			obj.name = 'NO DEVICE CONNECTED'
			obj.numAxes = 0
			obj.numBalls = 0
			obj.numButtons = 0
			obj.numHats = 0
		end

		love.joystickpressed = Gamepad._dispatchPress
		love.joystickreleased = Gamepad._dispatchRelease
		if obj.onNew then obj:onNew() end
		return obj
	end,

	-- Method: pressed
	-- Are *any* of the buttons passed held down this frame?
	--
	-- Arguments:
	--		button numbers passed as individual arguments
	--
	-- Returns:
	-- 		boolean

	pressed = function (self, ...)
		local buttons = {...}

		for _, value in pairs(buttons) do
			if self._thisFrame[value] then
				return true
			end
		end
		
		return false
	end,

	-- Method: justPressed
	-- Are *any* of the buttons passed pressed for the first time this frame?
	--
	-- Arguments:
	--		button numbers passed as individual arguments
	--
	-- Returns:
	-- 		boolean

	justPressed = function (self, ...)
		local buttons = {...}

		for _, value in pairs(buttons) do
			if self._thisFrame[value] and not self._lastFrame[value] then
				return true
			end
		end
		
		return false
	end,

	-- Method: released
	-- Are *all* of the buttons passed not held down this frame?
	--
	-- Arguments:
	--		button numbers passed as individual arguments
	--
	-- Returns:
	-- 		boolean

	released = function (self, ...)
		local buttons = {...}
	
		for _, value in pairs(buttons) do
			if self._thisFrame[value] then
				return false
			end
		end
		
		return true
	end,

	-- Method: justReleased
	-- Are *any* of the buttons passed released after being held last frame?
	--
	-- Arguments:
	--		button numbers passed as individual arguments
	--
	-- Returns:
	-- 		boolean

	justReleased = function (self, ...)
		local buttons = {...}
	
		for _, value in pairs(buttons) do
			if self._lastFrame[value] and not self._thisFrame[value] then
				return true
			end
		end
		
		return false
	end,

	-- Method: allPressed
	-- Returns all buttons currently pressed this frame.
	--
	-- Arguments:
	--		none
	--
	-- Returns:
	--		string button descriptions; if nothing is pressed, nil

	allPressed = function (self)
		local result = {}

		for key, value in pairs(self._thisFrame) do
			if value then table.insert(result, key) end
		end
		
		return unpack(result)
	end,

	-- Method: allJustPressed
	-- Returns all buttons just pressed this frame.
	--
	-- Arguments:
	--		none
	--
	-- Returns:
	--		string button descriptions; if nothing is just pressed, nil

	allJustPressed = function (self)
		local result = {}

		for key, value in pairs(self._thisFrame) do
			if value and not self._lastFrame[key] then table.insert(result, key) end
		end
		
		return unpack(result)
	end,

	-- Method: allJustReleased
	-- Returns all buttons just released this frame.
	--
	-- Arguments:
	--		none
	--
	-- Returns:
	--		string button descriptions; if nothing is just pressed, nil

	allJustReleased = function (self)
		local result = {}

		for key, value in pairs(self._thisFrame) do
			if not value and self._lastFrame[key] then table.insert(result, key) end
		end
		
		return unpack(result)
	end,

	buttonPressed = function (self, button)
		self._thisFrame[button] = true
	end,

	buttonReleased = function (self, button)
		self._thisFrame[button] = false
	end,

	endFrame = function (self, elapsed)
		-- move button values to the previous frame

		for key, value in pairs(self._thisFrame) do
			self._lastFrame[key] = value
		end

		-- set values

		for i = 1, self.numAxes do
			self.axes[i] = love.joystick.getAxis(self.number, i)
		end

		for i = 1, self.numBalls do
			self.balls[i].x, self.balls[i].y = love.joystick.getBall(self.number, i)
		end

		for i = 1, self.numHats do
			self.hats[i] = love.joystick.getHat(self.number, i)
		end

		-- simulate digital controls

		self._thisFrame['up'] = false
		self._thisFrame['down'] = false
		self._thisFrame['left'] = false
		self._thisFrame['right'] = false

		if self.numHats > 0 then
			local hat = self.hats[1]

			if hat == 'u' then
				self._thisFrame['up'] = true
			elseif hat == 'd' then
				self._thisFrame['down'] = true
			elseif hat == 'l' then
				self._thisFrame['left'] = true
			elseif hat == 'r' then
				self._thisFrame['right'] = true
			elseif hat == 'lu' then
				self._thisFrame['up'] = true
				self._thisFrame['left'] = true
			elseif hat == 'ru' then
				self._thisFrame['up'] = true
				self._thisFrame['right'] = true
			elseif hat == 'ld' then
				self._thisFrame['down'] = true
				self._thisFrame['left'] = true
			elseif hat == 'rd' then
				self._thisFrame['down'] = true
				self._thisFrame['right'] = true
			end
		end

		if self.numAxes > 1 then
			local xAxis = self.axes[1]
			local yAxis = self.axes[2]

			if math.abs(xAxis) > self.deadZone then
				if xAxis < 0 then
					self._thisFrame['left'] = true
				else
					self._thisFrame['right'] = true
				end
			end

			if math.abs(yAxis) > self.deadZone then
				if yAxis < 0 then
					self._thisFrame['up'] = true
				else
					self._thisFrame['down'] = true
				end
			end
		end
	
		Sprite.endFrame(self)
	end,

	-- private method: _dispatchPress
	-- receives a Love joystickpressed callback and hands it off
	-- to the appropriate gamepad.

	_dispatchPress = function (number, button)
		if the.gamepads[number] then
			the.gamepads[number]:buttonPressed(button)
		end
	end,

	-- private method: _dispatchRelease
	-- receives a Love joystickreleased callback and hands it off
	-- to the appropriate gamepad.

	_dispatchRelease = function (number, button)
		if the.gamepads[number] then
			the.gamepads[number]:buttonReleased(button)
		end
	end
}