13
26
stand = { frames = { 1 }, fps = 1 },
14
27
walk = { frames = { 2, 3 }, fps = 5 },
15
jump = { frames = { 4 }, fps = 1 }
28
jump = { frames = { 4 }, fps = 1 },
29
climbLeft = { frames = { 5, 6 }, fps = 5 },
30
climbRight = { frames = { 7, 8 }, fps = 5 }
17
35
onNew = function (self)
18
36
self.velocity.y = 0
19
37
self.maxVelocity.y = 400
39
doPhysics = function (self, dir, elapsed)
40
local vel = self.velocity
41
local acc = self.acceleration
42
local drag = self.drag
43
local minVel = self.minVelocity
44
local maxVel = self.maxVelocity
46
-- check existence of properties
49
assert(vel, 'active sprite has no velocity property')
50
assert(acc, 'active sprite has no acceleration property')
51
assert(drag, 'active sprite has no drag property')
52
assert(minVel, 'active sprite has no minVelocity property')
53
assert(maxVel, 'active sprite has no maxVelocity property')
54
assert(__.include({'x','y','rotation'}, dir), 'direction should be x, y, or rotation')
59
vel.rotation = vel.rotation or 0
63
if acc[dir] and acc[dir] ~= 0 then
64
vel[dir] = vel[dir] + acc[dir] * elapsed
68
vel[dir] = vel[dir] - drag[dir] * elapsed
69
if vel[dir] < 0 then vel[dir] = 0 end
70
elseif vel[dir] < 0 then
71
vel[dir] = vel[dir] + drag[dir] * elapsed
72
if vel[dir] > 0 then vel[dir] = 0 end
77
if minVel[dir] and vel[dir] < minVel[dir] then vel[dir] = minVel[dir] end
78
if maxVel[dir] and vel[dir] > maxVel[dir] then vel[dir] = maxVel[dir] end
80
-- ugly hack for falling through floor on really slow frames
81
if math.abs(vel[dir] * elapsed) > 32 then
86
if vel[dir] ~= 0 then self[dir] = self[dir] + vel[dir] * elapsed end
88
if self[dir] < 0 then self[dir] = 0 end
89
local edge = the.view.map[util.dim(dir)] -
90
the.player[util.dim(dir)]
91
-- TODO: take map position into account
92
if self[dir] > edge then self[dir] = edge end
21
94
onStartFrame = function (self)
22
95
-- this is all in startframe so it happens before
23
96
-- physics calc at beginning of update
27
100
if self.falling then self.jumping = false end
28
101
--print(self.jumping, self.falling)
30
if not self.onGround then
103
if (not self.onGround) and (not self.onWall) then
35
107
self.acceleration.y = 800
110
self.acceleration.y = 0
112
if self.onWall == 'right' then
113
self:play('climbRight')
114
elseif self.onWall == 'left' then
115
self:play('climbLeft')
118
if the.keys:pressed('up') then
119
self.velocity.y = -200
120
elseif the.keys:pressed('down') then
121
self.velocity.y = 200
124
self:freeze(self.sequences[self.currentName].frames[1])
37
128
if the.keys:pressed('left') then
38
129
self.velocity.x = -200
39
130
if self.onGround then self:play('walk') end
131
if self.onWall == 'right' then
133
self.leftWallAt = love.timer.getTime()
40
135
elseif the.keys:pressed('right') then
41
136
self.velocity.x = 200
42
137
if self.onGround then self:play('walk') end
138
if self.onWall == 'left' then
140
self.leftWallAt = love.timer.getTime()
44
if self.onGround then self:play('stand') end
143
if not self.onWall then
144
if self.onGround then self:play('stand') end
47
if the.keys:justPressed('up') and self.onGround then
149
if the.keys:justPressed('up') and
150
(self.onGround or the.console.visible or
151
(love.timer.getTime() - self.leftWallAt < .1) ) then
48
152
self.velocity.y = -400
49
153
self.jumping = true
52
onUpdate = function (self)
53
-- needs to happen right before collision
156
update = function (self, elapsed)
157
-- NOTE: this is an override, not a callback
159
self:doPhysics('x', elapsed)
160
self:collide(the.view.map)
162
-- handle X collisions
164
for _, col in ipairs(self.collisions) do
165
col.other:displaceDir(self, 'x')
166
if self.velocity.x > 0 then
167
self.onWall = 'right'
168
elseif self.velocity.x < 0 then
175
self.onGround = false -- right before Y collision callbacks
176
self:doPhysics('y', elapsed)
177
self:collide(the.view.map)
179
-- handle Y collisions
180
for _, col in ipairs(self.collisions) do
181
if self.velocity.y > 0 then
185
col.other:displaceDir(self, 'y')
190
Animation.update(self, elapsed)
192
collide = function (self, ...)
194
Animation.collide(self, ...)
195
-- I could return a true/false value here if I wanted to...
56
197
onCollide = function (self, other, xOverlap, yOverlap)
57
-- seriously, why does this even fire?
58
198
if other == the.view.map then return end
60
--print(string.format('col s{x=%i y=%i w=%i h=%i} %s', self.x, self.y, self.width, self.height, tostring(other)))
61
--print('vel.x:'..self.velocity.x.." vel.y:"..self.velocity.y)
63
-- assumption: any other collision is with a solid map tile
64
if yOverlap > xOverlap then
66
elseif xOverlap > yOverlap then
67
-- check if we've moved since collisions were generated
68
local xov, yov = self:overlap(other.x, other.y,
69
other.width, other.height)
70
if xov ~= 0 and yov ~= 0 then
71
--print('y collision')
72
if self.velocity.y > 0 then
200
table.insert(self.collisions, {other = other,
202
yOverlap = yOverlap })
206
-- displace on a specific axis (monkey patch Sprite)
207
function Sprite:displaceDir(other, dir)
208
if not self.solid or self == other or not other.solid then return end
209
if STRICT then assert(other:instanceOf(Sprite), 'asked to displace a non-sprite') end
211
if other.sprites then
214
for _, spr in pairs(other.sprites) do
215
self:displace(spr, dir)
219
local dim = util.dim(dir)
221
local negMove = (other[dir] - self[dir]) + other[dim]
222
local posMove = (self[dir] + self[dim]) - other[dir]
224
-- TODO: re-add hinting?
225
if negMove < posMove then
232
other[dir] = other[dir] + chg
235
-- don't use zoetrope physics
236
function Sprite:update (elapsed)
237
if self.onUpdate then self:onUpdate(elapsed) end
87
240
GameView = View:extend {
88
241
onNew = function (self)
89
242
self:loadLayers('data/map.lua')
91
244
self:clampTo(self.map)
93
246
onUpdate = function (self)
247
--print('drawTook: ', the.drawTook)
95
the.player:collide(self.map)
249
--the.player:collide(self.map)
96
250
--self.map:collide(the.player)
252
draw = function (self, x, y)
253
View.draw(self, x, y)
255
love.graphics.print('FPS:' .. love.timer.getFPS(), 20, 20)
100
259
the.app = App:new {
101
260
onRun = function (self)
102
261
self.view = GameView:new()
103
--print(inspect(_(the.app):keys()))
104
262
self.console:watch('onGround', 'the.player.onGround')
263
self.console:watch('onWall', 'the.player.onWall')
264
self.console:watch('updateTook', 'the.updateTook')
265
self.console:watch('drawTook', 'the.drawTook')
267
--the.profiler = newProfiler('time', 2000)
268
--the.profiler = newProfiler()
269
--the.profiler:start()
106
271
onUpdate = function (self, dt)
107
if the.keys:justPressed('escape') and
108
not self.console.visible then
272
if the.keys:justPressed('escape') then
275
local outfile = io.open( "profile.txt", "w+" )
276
the.profiler:report( outfile )
b'\\ No newline at end of file'
283
update = function (self, dt)
284
the.updateStart = love.timer.getMicroTime()
286
if the.updateStart then
287
the.updateTook = love.timer.getMicroTime() - the.updateStart