/zoeplat

To get this branch, use:
bzr branch http://9ix.org/bzr/zoeplat
2 by Josh C
basic tiles, map, player, movement
1
STRICT = true
2
DEBUG = true
3
4
require 'zoetrope'
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
5
__ = require 'underscore'
12 by Josh C
only jump when you're on the ground
6
--inspect = require 'inspect'
24 by Josh C
profiling and analysis
7
require 'pepperprof'
2 by Josh C
basic tiles, map, player, movement
8
26 by Josh C
don't go off the edge
9
util = {
10
   dim = function(dir)
11
      if dir == 'x' then
12
         return 'width'
13
      elseif dir == 'y' then
14
         return 'height'
15
      else
16
         print 'dir ??'
17
      end
18
   end
19
}
20
10 by Josh C
make player an animation
21
Player = Animation:extend {
2 by Josh C
basic tiles, map, player, movement
22
   image = 'data/player.png',
10 by Josh C
make player an animation
23
   height = 32,
24
   width = 32,
25
   sequences = {
26
      stand = { frames = { 1 }, fps = 1 },
27
      walk = { frames = { 2, 3 }, fps = 5 },
22 by Josh C
climbing animation
28
      jump = { frames = { 4 }, fps = 1 },
29
      climbLeft = { frames = { 5, 6 }, fps = 5 },
30
      climbRight = { frames = { 7, 8 }, fps = 5 }
10 by Josh C
make player an animation
31
   },
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
32
   collisions = {},
33
   onWall = false,
23 by Josh C
wall jump
34
   leftWallAt = 0,
3 by Josh C
jump
35
   onNew = function (self)
36
              self.velocity.y = 0
5 by Josh C
use built-in maxVelocity system
37
              self.maxVelocity.y = 400
3 by Josh C
jump
38
           end,
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
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
45
46
                  -- check existence of properties
47
48
                  if STRICT then
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')
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
54
                     assert(__.include({'x','y','rotation'}, dir), 'direction should be x, y, or rotation')
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
55
                  end
56
57
                  vel.x = vel.x or 0
58
                  vel.y = vel.y or 0
59
                  vel.rotation = vel.rotation or 0
60
61
                  -- physics
62
63
                  if acc[dir] and acc[dir] ~= 0 then
64
                     vel[dir] = vel[dir] + acc[dir] * elapsed
65
                  else
66
                     if drag[dir] then
67
                        if vel[dir] > 0 then
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
73
                        end
74
                     end
75
                  end
76
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
79
80
                  if vel[dir] ~= 0 then self[dir] = self[dir] + vel[dir] * elapsed end
26 by Josh C
don't go off the edge
81
82
                  if self[dir] < 0 then self[dir] = 0 end
83
                  local edge = the.view.map[util.dim(dir)] -
84
                               the.player[util.dim(dir)]
85
                  -- TODO: take map position into account
86
                  if self[dir] > edge then self[dir] = edge end
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
87
               end,
4 by Josh C
fix jitter caused by focus shift happening in the wrong order. Looks
88
   onStartFrame = function (self)
89
                     -- this is all in startframe so it happens before
90
                     -- physics calc at beginning of update
3 by Josh C
jump
91
12 by Josh C
only jump when you're on the ground
92
                     -- jumping/falling updates could go in EndFrame...
93
                     self.falling = self.velocity.y > 0
94
                     if self.falling then self.jumping = false end
95
                     --print(self.jumping, self.falling)
96
19 by Josh C
climb walls
97
                     if (not self.onGround) and (not self.onWall) then
13 by Josh C
reapply jump animation after Y collision. (there's a frame of no
98
                        self:play('jump')
99
                     end
100
6 by Josh C
whitespace cleanup
101
                     self.acceleration.y = 800
102
19 by Josh C
climb walls
103
                     if self.onWall then
104
                        self.acceleration.y = 0
105
22 by Josh C
climbing animation
106
                        if self.onWall == 'right' then
107
                           self:play('climbRight')
108
                        elseif self.onWall == 'left' then
109
                           self:play('climbLeft')
110
                        end
111
19 by Josh C
climb walls
112
                        if the.keys:pressed('up') then
113
                           self.velocity.y = -200
114
                        elseif the.keys:pressed('down') then
115
                           self.velocity.y = 200
116
                        else
117
                           self.velocity.y = 0
22 by Josh C
climbing animation
118
                           self:freeze(self.sequences[self.currentName].frames[1])
19 by Josh C
climb walls
119
                        end
120
                     end
121
6 by Josh C
whitespace cleanup
122
                     if the.keys:pressed('left') then
123
                        self.velocity.x = -200
15 by Josh C
more reliable onGround calc
124
                        if self.onGround then self:play('walk') end
23 by Josh C
wall jump
125
                        if self.onWall == 'right' then
126
                           self.onWall = false
127
                           self.leftWallAt = love.timer.getTime()
128
                        end
6 by Josh C
whitespace cleanup
129
                     elseif the.keys:pressed('right') then
130
                        self.velocity.x = 200
15 by Josh C
more reliable onGround calc
131
                        if self.onGround then self:play('walk') end
23 by Josh C
wall jump
132
                        if self.onWall == 'left' then
133
                           self.onWall = false
134
                           self.leftWallAt = love.timer.getTime()
135
                        end
10 by Josh C
make player an animation
136
                     else
21 by Josh C
really easy version of knowing when we reached the top of a wall.
137
                        if not self.onWall then
22 by Josh C
climbing animation
138
                           if self.onGround then self:play('stand') end
21 by Josh C
really easy version of knowing when we reached the top of a wall.
139
                           self.velocity.x = 0
140
                        end
6 by Josh C
whitespace cleanup
141
                     end
142
23 by Josh C
wall jump
143
                     if the.keys:justPressed('up') and
25 by Josh C
build more level
144
                      (self.onGround or the.console.visible or
23 by Josh C
wall jump
145
                       (love.timer.getTime() - self.leftWallAt < .1) ) then
6 by Josh C
whitespace cleanup
146
                        self.velocity.y = -400
12 by Josh C
only jump when you're on the ground
147
                        self.jumping = true
6 by Josh C
whitespace cleanup
148
                     end
149
                  end,
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
150
   update = function (self, elapsed)
151
               -- NOTE: this is an override, not a callback
152
153
               self:doPhysics('x', elapsed)
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
154
               self:collide(the.view.map)
155
156
               -- handle X collisions
21 by Josh C
really easy version of knowing when we reached the top of a wall.
157
               self.onWall = false
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
158
               for _, col in ipairs(self.collisions) do
159
                  col.other:displaceDir(self, 'x')
160
                  if self.velocity.x > 0 then
161
                     self.onWall = 'right'
162
                  elseif self.velocity.x < 0 then
163
                     self.onWall = 'left'
164
                  else
165
                     print 'x ??'
166
                  end
167
               end
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
168
169
               self.onGround = false -- right before Y collision callbacks
170
               self:doPhysics('y', elapsed)
171
               self:collide(the.view.map)
18 by Josh C
call Animation.update so we actually get animations
172
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
173
               -- handle Y collisions
174
               for _, col in ipairs(self.collisions) do
175
                  if self.velocity.y > 0 then
176
                     self.onGround = true
177
                  end
178
179
                  col.other:displaceDir(self, 'y')
180
                  self.velocity.y = 0
181
                  self.jumping = false
182
               end
183
18 by Josh C
call Animation.update so we actually get animations
184
               Animation.update(self, elapsed)
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
185
            end,
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
186
   collide = function (self, ...)
187
                self.collisions = {}
188
                Animation.collide(self, ...)
189
                -- I could return a true/false value here if I wanted to...
190
             end,
8 by Josh C
some basic collision (and workarounds)
191
   onCollide = function (self, other, xOverlap, yOverlap)
192
                  if other == the.view.map then return end
193
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
194
                  table.insert(self.collisions, {other = other,
195
                                                 xOverlap = xOverlap,
196
                                                 yOverlap = yOverlap })
197
               end
2 by Josh C
basic tiles, map, player, movement
198
}
199
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
200
-- displace on a specific axis (monkey patch Sprite)
201
function Sprite:displaceDir(other, dir)
202
   if not self.solid or self == other or not other.solid then return end
203
   if STRICT then assert(other:instanceOf(Sprite), 'asked to displace a non-sprite') end
204
205
   if other.sprites then
206
      -- handle groups
207
208
      for _, spr in pairs(other.sprites) do
209
         self:displace(spr, dir)
210
      end
211
   else
212
      -- handle sprites
26 by Josh C
don't go off the edge
213
      local dim = util.dim(dir)
20 by Josh C
fairly major overhaul of collision handling to track whether we're on a
214
215
      local negMove = (other[dir] - self[dir]) + other[dim]
216
      local posMove = (self[dir] + self[dim]) - other[dir]
217
218
      -- TODO: re-add hinting?
219
      if negMove < posMove then
220
         chg = - negMove
221
      else
222
         chg = posMove
223
      end
224
   end
225
226
   other[dir] = other[dir] + chg
227
end
228
2 by Josh C
basic tiles, map, player, movement
229
GameView = View:extend {
230
   onNew = function (self)
231
              self:loadLayers('data/map.lua')
232
              self.focus = the.player
233
              self:clampTo(self.map)
8 by Josh C
some basic collision (and workarounds)
234
           end,
235
   onUpdate = function (self)
236
                 --print('tick')
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
237
                 --the.player:collide(self.map)
8 by Josh C
some basic collision (and workarounds)
238
                 --self.map:collide(the.player)
239
              end
2 by Josh C
basic tiles, map, player, movement
240
}
241
242
the.app = App:new {
243
   onRun = function (self)
244
              self.view = GameView:new()
15 by Josh C
more reliable onGround calc
245
              self.console:watch('onGround', 'the.player.onGround')
16 by Josh C
try to track X collisions. break out Sprite's physics in prep for
246
              self.console:watch('onWall', 'the.player.onWall')
24 by Josh C
profiling and analysis
247
              self.console:watch('updateTook', 'the.updateTook')
248
              self.console:watch('drawTook', 'the.drawTook')
249
250
              --the.profiler = newProfiler('time', 2000)
251
              --the.profiler = newProfiler()
252
              --the.profiler:start()
2 by Josh C
basic tiles, map, player, movement
253
           end,
254
   onUpdate = function (self, dt)
24 by Josh C
profiling and analysis
255
                 if the.keys:justPressed('escape') then
256
                    if the.profiler then
257
                       the.profiler:stop()
258
                       local outfile = io.open( "profile.txt", "w+" )
259
                       the.profiler:report( outfile )
260
                       outfile:close()
261
                    end
262
17 by Josh C
reorganize code - separate X and Y physics so we can collide in each
263
                    self.quit()
12 by Josh C
only jump when you're on the ground
264
                 end
24 by Josh C
profiling and analysis
265
              end,
266
   update = function (self, dt)
267
               the.updateStart = love.timer.getMicroTime()
268
               App.update(self, dt)
269
               if the.updateStart then
270
                  the.updateTook = love.timer.getMicroTime() - the.updateStart
271
               end
272
            end
273
}