/zoeplat

To get this branch, use:
bzr branch http://9ix.org/bzr/zoeplat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
STRICT = true
DEBUG = true

require 'zoetrope'
__ = require 'underscore'
--inspect = require 'inspect'
require 'pepperprof'

Player = Animation:extend {
   image = 'data/player.png',
   height = 32,
   width = 32,
   sequences = {
      stand = { frames = { 1 }, fps = 1 },
      walk = { frames = { 2, 3 }, fps = 5 },
      jump = { frames = { 4 }, fps = 1 },
      climbLeft = { frames = { 5, 6 }, fps = 5 },
      climbRight = { frames = { 7, 8 }, fps = 5 }
   },
   collisions = {},
   onWall = false,
   leftWallAt = 0,
   onNew = function (self)
              self.velocity.y = 0
              self.maxVelocity.y = 400
           end,
   doPhysics = function (self, dir, elapsed)
                  local vel = self.velocity
                  local acc = self.acceleration
                  local drag = self.drag
                  local minVel = self.minVelocity
                  local maxVel = self.maxVelocity

                  -- check existence of properties

                  if STRICT then
                     assert(vel, 'active sprite has no velocity property')
                     assert(acc, 'active sprite has no acceleration property')
                     assert(drag, 'active sprite has no drag property')
                     assert(minVel, 'active sprite has no minVelocity property')
                     assert(maxVel, 'active sprite has no maxVelocity property')
                     assert(__.include({'x','y','rotation'}, dir), 'direction should be x, y, or rotation')
                  end

                  vel.x = vel.x or 0
                  vel.y = vel.y or 0
                  vel.rotation = vel.rotation or 0

                  -- physics

                  if acc[dir] and acc[dir] ~= 0 then
                     vel[dir] = vel[dir] + acc[dir] * elapsed
                  else
                     if drag[dir] then
                        if vel[dir] > 0 then
                           vel[dir] = vel[dir] - drag[dir] * elapsed
                           if vel[dir] < 0 then vel[dir] = 0 end
                        elseif vel[dir] < 0 then
                           vel[dir] = vel[dir] + drag[dir] * elapsed
                           if vel[dir] > 0 then vel[dir] = 0 end
                        end
                     end
                  end

                  if minVel[dir] and vel[dir] < minVel[dir] then vel[dir] = minVel[dir] end
                  if maxVel[dir] and vel[dir] > maxVel[dir] then vel[dir] = maxVel[dir] end

                  if vel[dir] ~= 0 then self[dir] = self[dir] + vel[dir] * elapsed end
               end,
   onStartFrame = function (self)
                     -- this is all in startframe so it happens before
                     -- physics calc at beginning of update

                     -- jumping/falling updates could go in EndFrame...
                     self.falling = self.velocity.y > 0
                     if self.falling then self.jumping = false end
                     --print(self.jumping, self.falling)

                     if (not self.onGround) and (not self.onWall) then
                        self:play('jump')
                     end

                     self.acceleration.y = 800

                     if self.onWall then
                        self.acceleration.y = 0

                        if self.onWall == 'right' then
                           self:play('climbRight')
                        elseif self.onWall == 'left' then
                           self:play('climbLeft')
                        end

                        if the.keys:pressed('up') then
                           self.velocity.y = -200
                        elseif the.keys:pressed('down') then
                           self.velocity.y = 200
                        else
                           self.velocity.y = 0
                           self:freeze(self.sequences[self.currentName].frames[1])
                        end
                     end

                     if the.keys:pressed('left') then
                        self.velocity.x = -200
                        if self.onGround then self:play('walk') end
                        if self.onWall == 'right' then
                           self.onWall = false
                           self.leftWallAt = love.timer.getTime()
                        end
                     elseif the.keys:pressed('right') then
                        self.velocity.x = 200
                        if self.onGround then self:play('walk') end
                        if self.onWall == 'left' then
                           self.onWall = false
                           self.leftWallAt = love.timer.getTime()
                        end
                     else
                        if not self.onWall then
                           if self.onGround then self:play('stand') end
                           self.velocity.x = 0
                        end
                     end

                     if the.keys:justPressed('up') and
                      (self.onGround or
                       (love.timer.getTime() - self.leftWallAt < .1) ) then
                        self.velocity.y = -400
                        self.jumping = true
                     end
                  end,
   update = function (self, elapsed)
               -- NOTE: this is an override, not a callback

               self:doPhysics('x', elapsed)
               self:collide(the.view.map)

               -- handle X collisions
               self.onWall = false
               for _, col in ipairs(self.collisions) do
                  col.other:displaceDir(self, 'x')
                  if self.velocity.x > 0 then
                     self.onWall = 'right'
                  elseif self.velocity.x < 0 then
                     self.onWall = 'left'
                  else
                     print 'x ??'
                  end
               end

               self.onGround = false -- right before Y collision callbacks
               self:doPhysics('y', elapsed)
               self:collide(the.view.map)

               -- handle Y collisions
               for _, col in ipairs(self.collisions) do
                  if self.velocity.y > 0 then
                     self.onGround = true
                  end

                  col.other:displaceDir(self, 'y')
                  self.velocity.y = 0
                  self.jumping = false
               end

               Animation.update(self, elapsed)
            end,
   collide = function (self, ...)
                self.collisions = {}
                Animation.collide(self, ...)
                -- I could return a true/false value here if I wanted to...
             end,
   onCollide = function (self, other, xOverlap, yOverlap)
                  if other == the.view.map then return end

                  table.insert(self.collisions, {other = other,
                                                 xOverlap = xOverlap,
                                                 yOverlap = yOverlap })
               end
}

-- displace on a specific axis (monkey patch Sprite)
function Sprite:displaceDir(other, dir)
   if not self.solid or self == other or not other.solid then return end
   if STRICT then assert(other:instanceOf(Sprite), 'asked to displace a non-sprite') end

   if other.sprites then
      -- handle groups

      for _, spr in pairs(other.sprites) do
         self:displace(spr, dir)
      end
   else
      -- handle sprites
      local dim
      if dir == 'x' then
         dim = 'width'
      elseif dir == 'y' then
         dim = 'height'
      else
         print 'dir ??'
      end

      local negMove = (other[dir] - self[dir]) + other[dim]
      local posMove = (self[dir] + self[dim]) - other[dir]

      -- TODO: re-add hinting?
      if negMove < posMove then
         chg = - negMove
      else
         chg = posMove
      end
   end

   other[dir] = other[dir] + chg
end

GameView = View:extend {
   onNew = function (self)
              self:loadLayers('data/map.lua')
              self.focus = the.player
              self:clampTo(self.map)
           end,
   onUpdate = function (self)
                 --print('tick')
                 --the.player:collide(self.map)
                 --self.map:collide(the.player)
              end
}

the.app = App:new {
   onRun = function (self)
              self.view = GameView:new()
              self.console:watch('onGround', 'the.player.onGround')
              self.console:watch('onWall', 'the.player.onWall')
              self.console:watch('updateTook', 'the.updateTook')
              self.console:watch('drawTook', 'the.drawTook')

              --the.profiler = newProfiler('time', 2000)
              --the.profiler = newProfiler()
              --the.profiler:start()
           end,
   onUpdate = function (self, dt)
                 if the.keys:justPressed('escape') then
                    if the.profiler then
                       the.profiler:stop()
                       local outfile = io.open( "profile.txt", "w+" )
                       the.profiler:report( outfile )
                       outfile:close()
                    end

                    self.quit()
                 end
              end,
   update = function (self, dt)
               the.updateStart = love.timer.getMicroTime()
               App.update(self, dt)
               if the.updateStart then
                  the.updateTook = love.timer.getMicroTime() - the.updateStart
               end
            end
}