/zoeplat

To get this branch, use:
bzr branch http://9ix.org/bzr/zoeplat

« back to all changes in this revision

Viewing changes to main.lua

  • Committer: Josh C
  • Date: 2013-04-10 01:20:03 UTC
  • Revision ID: josh@9ix.org-20130410012003-9fs124z86hmtd3sw
command line option for playback/record

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
DEBUG = true
3
3
 
4
4
require 'zoetrope'
5
 
 
6
 
TERM_VEL = 400
7
 
 
8
 
Player = Tile:extend {
 
5
__ = require 'underscore'
 
6
--inspect = require 'inspect'
 
7
require 'pepperprof'
 
8
require 'getopt_alt'
 
9
 
 
10
util = {
 
11
   dim = function(dir)
 
12
      if dir == 'x' then
 
13
         return 'width'
 
14
      elseif dir == 'y' then
 
15
         return 'height'
 
16
      else
 
17
         if STRICT then error('dir '..dir) end
 
18
      end
 
19
   end
 
20
}
 
21
 
 
22
Player = Animation:extend {
9
23
   image = 'data/player.png',
 
24
   height = 32,
 
25
   width = 32,
 
26
   sequences = {
 
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 }
 
32
   },
 
33
   collisions = {},
 
34
   onWall = false,
 
35
   leftWallAt = 0,
10
36
   onNew = function (self)
11
37
              self.velocity.y = 0
 
38
              self.maxVelocity.y = 400
12
39
           end,
 
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
 
46
 
 
47
                  -- check existence of properties
 
48
 
 
49
                  if STRICT then
 
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')
 
56
                  end
 
57
 
 
58
                  vel.x = vel.x or 0
 
59
                  vel.y = vel.y or 0
 
60
                  vel.rotation = vel.rotation or 0
 
61
 
 
62
                  -- physics
 
63
 
 
64
                  if acc[dir] and acc[dir] ~= 0 then
 
65
                     vel[dir] = vel[dir] + acc[dir] * elapsed
 
66
                  else
 
67
                     if drag[dir] then
 
68
                        if vel[dir] > 0 then
 
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
 
74
                        end
 
75
                     end
 
76
                  end
 
77
 
 
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
 
80
 
 
81
                  -- ugly hack for falling through floor on really slow frames
 
82
                  if math.abs(vel[dir] * elapsed) > 32 then
 
83
                     print('skip')
 
84
                     return
 
85
                  end
 
86
 
 
87
                  if vel[dir] ~= 0 then self[dir] = self[dir] + vel[dir] * elapsed end
 
88
 
 
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
 
94
               end,
13
95
   onStartFrame = function (self)
14
96
                     -- this is all in startframe so it happens before
15
97
                     -- physics calc at beginning of update
16
98
 
17
 
                 self.velocity.x = 0
18
 
                 -- TODO: this can be replaced with sprite.maxVel.y    
19
 
                 if self.velocity.y >= TERM_VEL then
20
 
                    self.velocity.y = TERM_VEL
21
 
                    self.acceleration.y = 0
22
 
                 else
23
 
                    self.acceleration.y = 800
24
 
                 end
25
 
 
26
 
                 if the.keys:pressed('left') then
27
 
                    self.velocity.x = -200
28
 
                 elseif the.keys:pressed('right') then
29
 
                    self.velocity.x = 200
30
 
                 end
31
 
 
32
 
                 if the.keys:justPressed('up') then
33
 
                    self.velocity.y = -400
34
 
                 end
35
 
              end,
36
 
   onUpdate = function (self)
37
 
                 -- this is called after physics, so this makes sense here
38
 
                 the.view.map:subdisplace(self)
39
 
              end
 
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)
 
103
 
 
104
                     if (not self.onGround) and (not self.onWall) then
 
105
                        self:play('jump')
 
106
                     end
 
107
 
 
108
                     self.acceleration.y = 800
 
109
 
 
110
                     if self.onWall then
 
111
                        self.acceleration.y = 0
 
112
 
 
113
                        if self.onWall == 'right' then
 
114
                           self:play('climbRight')
 
115
                        elseif self.onWall == 'left' then
 
116
                           self:play('climbLeft')
 
117
                        end
 
118
 
 
119
                        if the.keys:pressed('up') then
 
120
                           self.velocity.y = -200
 
121
                        elseif the.keys:pressed('down') then
 
122
                           self.velocity.y = 200
 
123
                        else
 
124
                           self.velocity.y = 0
 
125
                           self:freeze(self.sequences[self.currentName].frames[1])
 
126
                        end
 
127
                     end
 
128
 
 
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
 
133
                           self.onWall = false
 
134
                           self.leftWallAt = love.timer.getTime()
 
135
                        end
 
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
 
140
                           self.onWall = false
 
141
                           self.leftWallAt = love.timer.getTime()
 
142
                        end
 
143
                     else
 
144
                        if not self.onWall then
 
145
                           if self.onGround then self:play('stand') end
 
146
                           self.velocity.x = 0
 
147
                        end
 
148
                     end
 
149
 
 
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
 
154
                        self.jumping = true
 
155
                     end
 
156
                  end,
 
157
   update = function (self, elapsed)
 
158
               -- NOTE: this is an override, not a callback
 
159
 
 
160
               self:doPhysics('x', elapsed)
 
161
               self:collide(the.view.map)
 
162
 
 
163
               -- handle X collisions
 
164
               self.onWall = false
 
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
 
170
                     self.onWall = 'left'
 
171
                  else
 
172
                     print 'x ??'
 
173
                  end
 
174
               end
 
175
 
 
176
               self.onGround = false -- right before Y collision callbacks
 
177
               self:doPhysics('y', elapsed)
 
178
               self:collide(the.view.map)
 
179
 
 
180
               -- handle Y collisions
 
181
               for _, col in ipairs(self.collisions) do
 
182
                  if self.velocity.y > 0 then
 
183
                     self.onGround = true
 
184
                  end
 
185
 
 
186
                  col.other:displaceDir(self, 'y')
 
187
                  self.velocity.y = 0
 
188
                  self.jumping = false
 
189
               end
 
190
 
 
191
               -- text blob
 
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)
 
197
               end
 
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
 
205
 
 
206
               Animation.update(self, elapsed)
 
207
            end,
 
208
   collide = function (self, ...)
 
209
                self.collisions = {}
 
210
                Animation.collide(self, ...)
 
211
                -- I could return a true/false value here if I wanted to...
 
212
             end,
 
213
   onCollide = function (self, other, xOverlap, yOverlap)
 
214
                  if other == the.view.map then return end
 
215
 
 
216
                  table.insert(self.collisions, {other = other,
 
217
                                                 xOverlap = xOverlap,
 
218
                                                 yOverlap = yOverlap })
 
219
               end
40
220
}
41
221
 
 
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
 
226
 
 
227
   if other.sprites then
 
228
      -- handle groups
 
229
 
 
230
      for _, spr in pairs(other.sprites) do
 
231
         self:displace(spr, dir)
 
232
      end
 
233
   else
 
234
      -- handle sprites
 
235
      local dim = util.dim(dir)
 
236
 
 
237
      local negMove = (other[dir] - self[dir]) + other[dim]
 
238
      local posMove = (self[dir] + self[dim]) - other[dir]
 
239
 
 
240
      -- TODO: re-add hinting?
 
241
      if negMove < posMove then
 
242
         chg = - negMove
 
243
      else
 
244
         chg = posMove
 
245
      end
 
246
   end
 
247
 
 
248
   other[dir] = other[dir] + chg
 
249
end
 
250
 
 
251
-- don't use zoetrope physics
 
252
function Sprite:update (elapsed)
 
253
   if self.onUpdate then self:onUpdate(elapsed) end
 
254
end
 
255
 
42
256
GameView = View:extend {
43
257
   onNew = function (self)
44
258
              self:loadLayers('data/map.lua')
45
259
              self.focus = the.player
46
260
              self:clampTo(self.map)
47
 
           end
 
261
 
 
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'}
 
268
                 storage:load()
 
269
                 --print(inspect(storage.data))
 
270
                 the.recorder.record = storage.data
 
271
                 the.recorder:startPlaying()
 
272
              end
 
273
           end,
 
274
   onUpdate = function (self)
 
275
                 --print('drawTook: ', the.drawTook)
 
276
                 --print('tick')
 
277
                 --the.player:collide(self.map)
 
278
                 --self.map:collide(the.player)
 
279
              end,
 
280
   -- draw = function (self, x, y)
 
281
   --           View.draw(self, x, y)
 
282
 
 
283
   --           love.graphics.print('FPS:' .. love.timer.getFPS(), 20, 20)
 
284
   --        end
48
285
}
49
286
 
50
287
the.app = App:new {
 
288
   record = true,
51
289
   onRun = function (self)
52
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')
 
296
 
 
297
              --the.profiler = newProfiler('time', 2000)
 
298
              --the.profiler = newProfiler()
 
299
              --the.profiler:start()
53
300
           end,
54
301
   onUpdate = function (self, dt)
55
302
                 if the.keys:justPressed('escape') then
56
 
                    love.event.quit()
 
303
                    if the.profiler then
 
304
                       the.profiler:stop()
 
305
                       local outfile = io.open( "profile.txt", "w+" )
 
306
                       the.profiler:report( outfile )
 
307
                       outfile:close()
 
308
                    end
 
309
 
 
310
                    if self.record then
 
311
                       if not love.filesystem.remove('record.lua') then
 
312
                          print('could not remove record.lua')
 
313
                       end
 
314
                       local storage = Storage:new{
 
315
                          data = the.recorder.record,
 
316
                          filename = 'record.lua'
 
317
                       }
 
318
                       storage:save(false)
 
319
                       --print(inspect(the.recorder.record))
 
320
                    end
 
321
 
 
322
                    self.quit()
57
323
                 end
58
 
              end
59
 
}
 
 
b'\\ No newline at end of file'
 
324
              end,
 
325
   update = function (self, dt)
 
326
               the.updateStart = love.timer.getMicroTime()
 
327
               App.update(self, dt)
 
328
               if the.updateStart then
 
329
                  the.updateTook = love.timer.getMicroTime() - the.updateStart
 
330
               end
 
331
            end
 
332
}
 
333
 
 
334
function love.load (arg)
 
335
   opts = getopt(arg, '')
 
336
   if opts['p'] then
 
337
      the.app.playback = true
 
338
      the.app.record = false
 
339
   elseif opts['r'] then
 
340
      the.app.record = true
 
341
   end
 
342
 
 
343
   the.app:run()
 
344
end
 
 
b'\\ No newline at end of file'