5
__ = require 'underscore'
6
--inspect = require 'inspect'
14
elseif dir == 'y' then
17
if STRICT then error('dir '..dir) end
22
Player = Animation:extend {
9
23
image = 'data/player.png',
27
stand = { frames = { 1 }, fps = 1 },
28
walk = { frames = { 2, 3 }, fps = 5 },
29
jump = { frames = { 4 }, fps = 1 },
30
climbLeft = { frames = { 5, 6 }, fps = 5 },
31
climbRight = { frames = { 7, 8 }, fps = 5 }
10
36
onNew = function (self)
11
37
self.velocity.y = 0
38
self.maxVelocity.y = 400
13
onUpdate = function (self)
16
if self.velocity.y >= TERM_VEL then
17
self.velocity.y = TERM_VEL
18
self.acceleration.y = 0
20
self.acceleration.y = 800
23
if the.keys:pressed('left') then
24
self.velocity.x = -200
25
elseif the.keys:pressed('right') then
29
if the.keys:justPressed('up') then
30
self.velocity.y = -400
40
doPhysics = function (self, dir, elapsed)
41
local vel = self.velocity
42
local acc = self.acceleration
43
local drag = self.drag
44
local minVel = self.minVelocity
45
local maxVel = self.maxVelocity
47
-- check existence of properties
50
assert(vel, 'active sprite has no velocity property')
51
assert(acc, 'active sprite has no acceleration property')
52
assert(drag, 'active sprite has no drag property')
53
assert(minVel, 'active sprite has no minVelocity property')
54
assert(maxVel, 'active sprite has no maxVelocity property')
55
assert(__.include({'x','y','rotation'}, dir), 'direction should be x, y, or rotation')
60
vel.rotation = vel.rotation or 0
64
if acc[dir] and acc[dir] ~= 0 then
65
vel[dir] = vel[dir] + acc[dir] * elapsed
69
vel[dir] = vel[dir] - drag[dir] * elapsed
70
if vel[dir] < 0 then vel[dir] = 0 end
71
elseif vel[dir] < 0 then
72
vel[dir] = vel[dir] + drag[dir] * elapsed
73
if vel[dir] > 0 then vel[dir] = 0 end
78
if minVel[dir] and vel[dir] < minVel[dir] then vel[dir] = minVel[dir] end
79
if maxVel[dir] and vel[dir] > maxVel[dir] then vel[dir] = maxVel[dir] end
81
-- ugly hack for falling through floor on really slow frames
82
if math.abs(vel[dir] * elapsed) > 32 then
87
if vel[dir] ~= 0 then self[dir] = self[dir] + vel[dir] * elapsed end
89
if self[dir] < 0 then self[dir] = 0 end
90
local edge = the.view.map[util.dim(dir)] -
91
the.player[util.dim(dir)]
92
-- TODO: take map position into account
93
if self[dir] > edge then self[dir] = edge end
95
onStartFrame = function (self)
96
-- this is all in startframe so it happens before
97
-- physics calc at beginning of update
99
-- jumping/falling updates could go in EndFrame...
100
self.falling = self.velocity.y > 0
101
if self.falling then self.jumping = false end
102
--print(self.jumping, self.falling)
104
if (not self.onGround) and (not self.onWall) then
108
self.acceleration.y = 800
111
self.acceleration.y = 0
113
if self.onWall == 'right' then
114
self:play('climbRight')
115
elseif self.onWall == 'left' then
116
self:play('climbLeft')
119
if the.keys:pressed('up') then
120
self.velocity.y = -200
121
elseif the.keys:pressed('down') then
122
self.velocity.y = 200
125
self:freeze(self.sequences[self.currentName].frames[1])
129
if the.keys:pressed('left') then
130
self.velocity.x = -200
131
if self.onGround then self:play('walk') end
132
if self.onWall == 'right' then
134
self.leftWallAt = love.timer.getTime()
136
elseif the.keys:pressed('right') then
137
self.velocity.x = 200
138
if self.onGround then self:play('walk') end
139
if self.onWall == 'left' then
141
self.leftWallAt = love.timer.getTime()
144
if not self.onWall then
145
if self.onGround then self:play('stand') end
150
if the.keys:justPressed('up') and
151
(self.onGround or the.console.visible or
152
(love.timer.getTime() - self.leftWallAt < .1) ) then
153
self.velocity.y = -400
157
update = function (self, elapsed)
158
-- NOTE: this is an override, not a callback
160
self:doPhysics('x', elapsed)
161
self:collide(the.view.map)
163
-- handle X collisions
165
for _, col in ipairs(self.collisions) do
166
col.other:displaceDir(self, 'x')
167
if self.velocity.x > 0 then
168
self.onWall = 'right'
169
elseif self.velocity.x < 0 then
176
self.onGround = false -- right before Y collision callbacks
177
self:doPhysics('y', elapsed)
178
self:collide(the.view.map)
180
-- handle Y collisions
181
for _, col in ipairs(self.collisions) do
182
if self.velocity.y > 0 then
186
col.other:displaceDir(self, 'y')
192
if not self.text then
193
self.text = Text:new{wordWrap = true, width = 50, tint = {0,0,0}}
194
self.textfill = Fill:new{width = 54, border = {0,0,255}}
195
--the.view:add(self.textfill)
196
--the.view:add(self.text)
198
self.text.text = "Blah blah big text etc etc wrapping"
199
self.text:centerAround(self.x+16, self.y+16, 'horizontal')
200
_, texth = self.text:getSize()
201
self.text.y = self.y - texth - 4
202
self.textfill.x = self.text.x - 2
203
self.textfill.y = self.text.y - 2
204
self.textfill.height = texth + 4
206
Animation.update(self, elapsed)
208
collide = function (self, ...)
210
Animation.collide(self, ...)
211
-- I could return a true/false value here if I wanted to...
213
onCollide = function (self, other, xOverlap, yOverlap)
214
if other == the.view.map then return end
216
table.insert(self.collisions, {other = other,
218
yOverlap = yOverlap })
222
-- displace on a specific axis (monkey patch Sprite)
223
function Sprite:displaceDir(other, dir)
224
if not self.solid or self == other or not other.solid then return end
225
if STRICT then assert(other:instanceOf(Sprite), 'asked to displace a non-sprite') end
227
if other.sprites then
230
for _, spr in pairs(other.sprites) do
231
self:displace(spr, dir)
235
local dim = util.dim(dir)
237
local negMove = (other[dir] - self[dir]) + other[dim]
238
local posMove = (self[dir] + self[dim]) - other[dir]
240
-- TODO: re-add hinting?
241
if negMove < posMove then
248
other[dir] = other[dir] + chg
251
-- don't use zoetrope physics
252
function Sprite:update (elapsed)
253
if self.onUpdate then self:onUpdate(elapsed) end
36
256
GameView = View:extend {
37
257
onNew = function (self)
38
258
self:loadLayers('data/map.lua')
39
259
self.focus = the.player
40
260
self:clampTo(self.map)
262
the.recorder = Recorder:new{mousePosInterval = 9999}
263
the.app.meta:add(the.recorder)
264
if the.app.record then
265
the.recorder:startRecording()
266
elseif the.app.playback then
267
local storage = Storage:new{filename = 'record.lua'}
269
--print(inspect(storage.data))
270
the.recorder.record = storage.data
271
the.recorder:startPlaying()
274
onUpdate = function (self)
275
--print('drawTook: ', the.drawTook)
277
--the.player:collide(self.map)
278
--self.map:collide(the.player)
280
-- draw = function (self, x, y)
281
-- View.draw(self, x, y)
43
onEndFrame = function (self, dt)
44
self.map:subdisplace(the.player)
283
-- love.graphics.print('FPS:' .. love.timer.getFPS(), 20, 20)
48
287
the.app = App:new {
49
289
onRun = function (self)
50
290
self.view = GameView:new()
291
self.console:watch('onGround', 'the.player.onGround')
292
self.console:watch('onWall', 'the.player.onWall')
293
self.console:watch('updateTook', 'the.updateTook')
294
self.console:watch('drawTook', 'the.drawTook')
295
self.console:watch('recorder state', 'the.recorder.state')
297
--the.profiler = newProfiler('time', 2000)
298
--the.profiler = newProfiler()
299
--the.profiler:start()
52
301
onUpdate = function (self, dt)
53
302
if the.keys:justPressed('escape') then
305
local outfile = io.open( "profile.txt", "w+" )
306
the.profiler:report( outfile )
311
if not love.filesystem.remove('record.lua') then
312
print('could not remove record.lua')
314
local storage = Storage:new{
315
data = the.recorder.record,
316
filename = 'record.lua'
319
--print(inspect(the.recorder.record))
b'\\ No newline at end of file'
325
update = function (self, dt)
326
the.updateStart = love.timer.getMicroTime()
328
if the.updateStart then
329
the.updateTook = love.timer.getMicroTime() - the.updateStart
334
function love.load (arg)
335
opts = getopt(arg, '')
337
the.app.playback = true
338
the.app.record = false
339
elseif opts['r'] then
340
the.app.record = true
b'\\ No newline at end of file'