27
13
stand = { frames = { 1 }, fps = 1 },
28
14
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 }
15
jump = { frames = { 4 }, fps = 1 }
36
17
onNew = function (self)
37
18
self.velocity.y = 0
38
19
self.maxVelocity.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
21
onStartFrame = function (self)
96
22
-- this is all in startframe so it happens before
97
23
-- physics calc at beginning of update
101
27
if self.falling then self.jumping = false end
102
28
--print(self.jumping, self.falling)
104
if (not self.onGround) and (not self.onWall) then
30
if not self:onGround() then
108
35
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
37
if the.keys:pressed('left') then
130
38
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()
39
if self:onGround() then self:play('walk') end
136
40
elseif the.keys:pressed('right') then
137
41
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()
42
if self:onGround() then self:play('walk') end
144
if not self.onWall then
145
if self.onGround then self:play('stand') end
44
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
47
if the.keys:justPressed('up') and self:onGround() then
153
48
self.velocity.y = -400
154
49
self.jumping = true
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...
52
onEndFrame = function (self)
53
--print(self.velocity.y)
213
55
onCollide = function (self, other, xOverlap, yOverlap)
56
-- seriously, why does this even fire?
214
57
if other == the.view.map then return end
216
table.insert(self.collisions, {other = other,
218
yOverlap = yOverlap })
59
--print(string.format('col s{x=%i y=%i w=%i h=%i} %s', self.x, self.y, self.width, self.height, tostring(other)))
60
--print('vel.x:'..self.velocity.x.." vel.y:"..self.velocity.y)
62
-- assumption: any other collision is with a solid map tile
63
if yOverlap > xOverlap then
65
elseif xOverlap > yOverlap then
66
-- check if we've moved since collisions were generated
67
local xov, yov = self:overlap(other.x, other.y,
68
other.width, other.height)
69
if xov ~= 0 and yov ~= 0 then
79
onGround = function (self)
80
return (not self.jumping) and (not self.falling)
222
function Sprite:displaceDir(other, dir)
223
if not self.solid or self == other or not other.solid then return end
224
if STRICT then assert(other:instanceOf(Sprite), 'asked to displace a non-sprite') end
226
if other.sprites then
229
for _, spr in pairs(other.sprites) do
230
self:displace(spr, dir)
234
local dim = util.dim(dir)
236
local negMove = (other[dir] - self[dir]) + other[dim]
237
local posMove = (self[dir] + self[dim]) - other[dir]
239
-- TODO: re-add hinting?
240
if negMove < posMove then
247
other[dir] = other[dir] + chg
250
function Sprite:update (elapsed)
251
if self.onUpdate then self:onUpdate(elapsed) end
254
84
GameView = View:extend {
255
85
onNew = function (self)
256
86
self:loadLayers('data/map.lua')
257
87
self.focus = the.player
258
88
self:clampTo(self.map)
260
the.recorder = Recorder:new{mousePosInterval = 9999}
261
the.app.meta:add(the.recorder)
262
if the.app.record then
263
the.recorder:startRecording()
264
elseif the.app.playback then
265
local storage = Storage:new{filename = 'record.lua'}
267
--print(inspect(storage.data))
268
the.recorder.record = storage.data
269
the.recorder:startPlaying()
272
90
onUpdate = function (self)
273
--print('drawTook: ', the.drawTook)
275
--the.player:collide(self.map)
92
the.player:collide(self.map)
276
93
--self.map:collide(the.player)
278
-- draw = function (self, x, y)
279
-- View.draw(self, x, y)
281
-- love.graphics.print('FPS:' .. love.timer.getFPS(), 20, 20)
285
97
the.app = App:new {
287
98
onRun = function (self)
288
99
self.view = GameView:new()
289
self.console:watch('onGround', 'the.player.onGround')
290
self.console:watch('onWall', 'the.player.onWall')
291
self.console:watch('updateTook', 'the.updateTook')
292
self.console:watch('drawTook', 'the.drawTook')
293
self.console:watch('recorder state', 'the.recorder.state')
295
--the.profiler = newProfiler('time', 2000)
296
--the.profiler = newProfiler()
297
--the.profiler:start()
100
--print(inspect(_(the.app):keys()))
299
102
onUpdate = function (self, dt)
103
-- TODO: make this not work if debug console is active
300
104
if the.keys:justPressed('escape') then
303
local outfile = io.open( "profile.txt", "w+" )
304
the.profiler:report( outfile )
309
if not love.filesystem.remove('record.lua') then
310
print('could not remove record.lua')
312
local storage = Storage:new{
313
data = the.recorder.record,
314
filename = 'record.lua'
317
--print(inspect(the.recorder.record))
323
update = function (self, dt)
324
the.updateStart = love.timer.getMicroTime()
326
if the.updateStart then
327
the.updateTook = love.timer.getMicroTime() - the.updateStart
332
function love.load (arg)
333
opts = getopt(arg, '')
335
the.app.playback = true
336
the.app.record = false
337
elseif opts['r'] then
338
the.app.record = true
b'\\ No newline at end of file'
b'\\ No newline at end of file'