/traderous

To get this branch, use:
bzr branch http://9ix.org/bzr/traderous
1 by Josh C
zoetrope 1.4
1
-- Class: Recorder
2
-- This records the user's inputs for playback at a later time,
3
-- or saving to a file. You must start recording *after* all input
4
-- objects are set up, and you should only run one recorder at a time.
5
--
6
-- Extends:
7
--		<Sprite>
8
9
Recorder = Sprite:extend{
10
	-- private property: elapsed
11
	-- either time elapsed while recording or playing back, in seconds
12
	_elapsed = 0,
13
14
	-- private property: mousePosTimer
15
	-- used to make sure mouse motions are recorded even if no other event occurs
16
	_mousePosTimer = 0,
17
18
	-- Constant: IDLE
19
	-- The recorder is currently doing nothing.
20
	IDLE = 'idle',
21
22
	-- Constant: RECORDING
23
	-- The recorder is currently recording user input.
24
	RECORDING = 'recording',
25
26
	-- Constant: PLAYING
27
	-- The recorder is currently playing back user input.
28
	PLAYING = 'playing',
29
30
	-- Property: mousePosInterval
31
	-- How often, in seconds, we should capture the mouse position.
32
	-- This records the mouse position when other events occur, too, so
33
	-- it's possible the interval will be even higher in reality.
34
	-- Setting this number lower will burn memory.
35
	mousePosInterval = 0.05,
36
37
	-- Property: state
38
	-- One of the state constants, indicates what the recorder is currently doing.
39
40
	-- Property: record
41
	-- A table of inputs with timing information.
42
43
	new = function (self, obj)
44
		obj = self:extend(obj)
45
		obj.state = Recorder.IDLE
46
47
		Sprite.new(obj)
48
		return obj
49
	end,
50
51
	-- Method: startRecording
52
	-- Begins recording user inputs. If the recorder is already recording,
53
	-- this has no effect.
54
	--
55
	-- Arguments:
56
	--		record - Record to use. Any existing data is appended to.
57
	--				 If omitted, the current record is used. If the current
58
	--				 record is unset, this creates a new record.
59
	--
60
	-- Returns:
61
	--		nothing
62
63
	startRecording = function (self, record)
64
		if self.state ~= Recorder.IDLE then return end
65
66
		-- set up properties
67
		self.record = record or self.record or {}
68
		self.state = Recorder.RECORDING
69
		self._elapsed = 0
70
		self._mousePosTimer = 0
71
72
		-- insert ourselves into event handlers
73
		self:stealInputs()
74
	end,
75
76
	-- Method: stopRecording
77
	-- Stops recording user inputs. If the recorder wasn't recording anyway,
78
	-- this does nothing.
79
	-- 
80
	-- Arguments:
81
	--		none
82
	-- 
83
	-- Returns:
84
	--		nothing
85
86
	stopRecording = function (self)
87
		if self.state ~= Recorder.RECORDING then return end
88
89
		self.state = Recorder.IDLE
90
		self:restoreInputs()
91
		love.keypressed = self.origKeyPressed
92
		love.keyreleased = self.origKeyReleased
93
	end,
94
95
	-- Method: startPlaying
96
	-- Starts playing back user inputs. If this is already playing
97
	-- a recording, this restarts it.
98
	--
99
	-- Arguments:
100
	--		record - Record to play back. If omitted, this uses
101
	--				 the recorder's record property.
102
	--
103
	-- Returns:
104
	--		nothing
105
106
	startPlaying = function (self, record)
107
		record = record or self.record
108
109
		-- if we are currently recording, ignore the request
110
111
		if self.state == Recorder.RECORDING then return end
112
113
		-- restart if needed
114
115
		if self.state == Recorder.PLAYING then
116
			self:stopPlaying()	
117
		end
118
119
		self.state = Recorder.PLAYING 
120
121
		self._elapsed = 0
122
		self.playbackIndex = 1
123
		self:stealInputs()
124
	end,
125
126
	-- Method: stopPlaying
127
	-- Stops playing back user inputs.
128
	--
129
	-- Arguments:
130
	--		none
131
	--
132
	-- Returns:
133
	--		nothing
134
135
	stopPlaying = function (self)
136
		if not self.state == Recorder.PLAYING then return end
137
		self.state = Recorder.IDLE
138
		self:restoreInputs()
139
	end,
140
141
	stealInputs = function (self)
142
		local this = self
143
144
		self.origKeyPressed = love.keypressed
145
		love.keypressed = function (key, code) this:recordKeyPress(key, code) end
146
		self.origKeyReleased = love.keyreleased
147
		love.keyreleased = function (key, code) this:recordKeyRelease(key, code) end
148
		self.origMousePressed = love.mousepressed
149
		love.mousepressed = function (x, y, button) this:recordMousePress(x, y, button) end
150
		self.origMouseReleased = love.mousereleased
151
		love.mousereleased = function (x, y, button) this:recordMouseRelease(x, y, button) end
152
	end,
153
154
	restoreInputs = function (self)
155
		love.keypressed = self.origKeyPressed
156
		love.keyreleased = self.origKeyReleased
157
		love.mousepressed = self.origMousePressed
158
		love.mousereleased = self.origMouseReleased
159
	end,
160
	
161
	recordKeyPress = function (self, key, unicode)
162
		table.insert(self.record, { self._elapsed, the.mouse.x, the.mouse.y, 'keypress', key, unicode })
163
		self._mousePosTimer = 0
164
165
		if self.origKeyPressed then
166
			self.origKeyPressed(key, unicode)
167
		end
168
	end,
169
170
	recordKeyRelease = function (self, key, unicode)
171
		table.insert(self.record, { self._elapsed, the.mouse.x, the.mouse.y, 'keyrelease', key, unicode })
172
		self._mousePosTimer = 0
173
174
		if self.origKeyReleased then
175
			self.origKeyReleased(key, unicode)
176
		end
177
	end,
178
179
	recordMousePress = function (self, x, y, button)
180
		table.insert(self.record, { self._elapsed, x, y, 'mousepress', button })
181
		self._mousePosTimer = 0
182
183
		if self.origMousePressed then
184
			self.origMousePressed(x, y, button)
185
		end
186
	end,
187
188
	recordMouseRelease = function (self, x, y, button)
189
		table.insert(self.record, { self._elapsed, x, y, 'mouserelease', button })
190
		self._mousePosTimer = 0
191
192
		if self.origMouseReleased then
193
			self.origMouseReleased(x, y, button)
194
		end
195
	end,
196
197
	update = function (self, elapsed)
198
		-- increment timers
199
200
		if self.state ~= Recorder.IDLE then
201
			self._elapsed = self._elapsed + elapsed
202
		end
203
204
		-- record mouse position if the timer has expired
205
206
		if self.state == Recorder.RECORDING then
207
			self._mousePosTimer = self._mousePosTimer + elapsed
208
209
			if self._mousePosTimer > self.mousePosInterval then
210
				table.insert(self.record, { self._elapsed, the.mouse.x, the.mouse.y })
211
212
				self._mousePosTimer = 0
213
			end
214
		end
215
216
		-- handle playback
217
218
		if self.state == Recorder.PLAYING and self._elapsed >= self.record[self.playbackIndex][1] then
219
			local event = self.record[self.playbackIndex]
220
	
221
			love.mouse.setPosition(event[2], event[3])
222
223
			if event[4] == 'keypress' and self.origKeyPressed then
224
				self.origKeyPressed(event[5], event[6])
225
			elseif event[4] == 'keyrelease' and self.origKeyReleased then
226
				self.origKeyReleased(event[5], event[6])
227
			elseif event[4] == 'mousepress' and self.origMousePressed then
228
				self.origMousePressed(event[2], event[3], event[5])
229
			elseif event[4] == 'mouserelease' and self.origMouseReleased then
230
				self.origMouseReleased(event[2], event[3], event[5])
231
			end
232
233
			self.playbackIndex = self.playbackIndex + 1
234
235
			if self.playbackIndex > #self.record then
236
				self:stopPlaying()
237
			end
238
		end
239
	end
240
}