/ld27

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

« back to all changes in this revision

Viewing changes to zoetrope/core/group.lua

  • Committer: Josh C
  • Date: 2014-07-05 00:04:06 UTC
  • Revision ID: josh@9ix.org-20140705000406-9h8qk5f0j6oz0xo3
try more gracefully handling different "am" versions

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
-- Event: onUpdate
9
9
-- Called once each frame, with the elapsed time since the last frame in seconds.
10
10
--
 
11
-- Event: onStartFrame
11
12
-- Called once each frame like onUpdate, but guaranteed to fire before any others' onUpdate handlers.
12
13
--
13
14
-- Event: onEndFrame
31
31
        solid = true,
32
32
 
33
33
        -- Property: sprites
34
 
        -- A table of member sprites, in drawing order.
 
34
        -- A table of member sprites, in drawing order. You can iterate over this
 
35
        -- table with <members()>.
35
36
        sprites = {},
36
37
 
37
38
        -- Property: timeScale
42
43
        -- This table's x and y properties shift member sprites' positions when drawn.
43
44
        -- To draw sprites at their normal position, set both x and y to 0.
44
45
        translate = { x = 0, y = 0 },
45
 
        
 
46
 
46
47
        -- Property: translateScale
47
48
        -- This table's x and y properties multiply member sprites'
48
49
        -- positions, which you can use to simulate parallax scrolling. To draw
49
50
        -- sprites at their normal position, set both x and y to 1.
50
51
        translateScale = { x = 1, y = 1 },
51
52
 
 
53
  -- an actual love.graphics.translate call
 
54
  realTranslate = { x = 0, y = 0 },
 
55
 
 
56
        -- Property: scale
 
57
        -- Zooms in or out all drawing operations in the group, centering them with
 
58
        -- respect to the <origin> property.
 
59
        scale = 1,
 
60
 
 
61
        -- Property: distort
 
62
        -- Distorts the group's scale, similar to <Sprite.distort>.
 
63
        distort = { x = 1, y = 1 },
 
64
 
 
65
        -- Property: origin
 
66
        -- Sets the center point for all scaling operations.
 
67
        origin = { x = 0, y = 0 },
 
68
 
52
69
        -- Method: add
53
70
        -- Adds a sprite to the group.
54
71
        --
56
73
        --              sprite - <Sprite> to add
57
74
        --
58
75
        -- Returns:
59
 
        --              nothing
 
76
        --              the sprite added, so you can write things like
 
77
        --              self.player = self:add(Player:new())
60
78
 
61
79
        add = function (self, sprite)
62
80
                assert(sprite, 'asked to add nil to a group')
63
81
                assert(sprite ~= self, "can't add a group to itself")
64
 
        
 
82
 
65
83
                if STRICT and self:contains(sprite) then
66
84
                        local info = debug.getinfo(2, 'Sl')
67
85
                        print('Warning: adding a sprite to a group it already belongs to (' ..
69
87
                end
70
88
 
71
89
                table.insert(self.sprites, sprite)
 
90
                return sprite
72
91
        end,
73
92
 
74
93
        -- Method: remove
75
94
        -- Removes a sprite from the group. If the sprite is
76
95
        -- not in the group, this does nothing.
77
 
        -- 
 
96
        --
78
97
        -- Arguments:
79
98
        --              sprite - <Sprite> to remove
80
 
        -- 
 
99
        --
81
100
        -- Returns:
82
101
        --              nothing
83
102
 
84
103
        remove = function (self, sprite)
85
 
                for i, spr in ipairs(self.sprites) do
 
104
                for i, spr in self:members() do
86
105
                        if spr == sprite then
87
106
                                table.remove(self.sprites, i)
88
107
                                return
89
108
                        end
90
109
                end
91
 
                
 
110
 
92
111
                if STRICT then
93
112
                        local info = debug.getinfo(2, 'Sl')
94
113
                        print('Warning: asked to remove a sprite from a group it was not a member of (' ..
107
126
        --              nothing
108
127
 
109
128
        moveToFront = function (self, sprite)
110
 
                for i, spr in ipairs(self.sprites) do
 
129
                for i, spr in self:members() do
111
130
                        if spr == sprite then
112
131
                                table.remove(self.sprites, i)
113
132
                                table.insert(self.sprites, sprite)
129
148
        --
130
149
        -- Returns:
131
150
        --              nothing
132
 
        
 
151
 
133
152
        moveToBack = function (self, sprite)
134
 
                for i, spr in ipairs(self.sprites) do
 
153
                for i, spr in self:members() do
135
154
                        if spr == sprite then
136
155
                                table.remove(self.sprites, i)
137
156
                                table.insert(self.sprites, 1, sprite)
158
177
                table.sort(self.sprites, func)
159
178
        end,
160
179
 
 
180
        -- Method: members
 
181
        -- A convenience method that iterates over all member sprites.
 
182
        --
 
183
        -- Arguments:
 
184
        --              none
 
185
        --
 
186
        -- Returns:
 
187
        --              order, sprite
 
188
 
 
189
        members = function (self)
 
190
                return ipairs(self.sprites)
 
191
        end,
 
192
 
161
193
        -- Method: collide
162
194
        -- Collides all solid sprites in the group with another sprite or group.
163
195
        -- This calls the <Sprite.onCollide> event handlers on all sprites that
169
201
        -- Arguments:
170
202
        --              ... - any number of <Sprite>s or <Group>s to collide with. If none
171
203
        --                        are specified, the group collides with itself.
172
 
        -- 
 
204
        --
173
205
        -- Returns:
174
206
        --              nothing
175
207
        --
215
247
        setEffect = function (self, filename, effectType)
216
248
                effectType = effectType or 'screen'
217
249
 
218
 
                if love.graphics.isSupported('pixeleffect') and
 
250
                if love.graphics.isSupported('shader') and
219
251
                   (effectType == 'sprite' or love.graphics.isSupported('canvas'))then
220
252
                        if filename then
221
 
                                self.effect = love.graphics.newPixelEffect(Cached:text(filename))
 
253
                                self.effect = love.graphics.newShader(Cached:text(filename))
222
254
                                self.effectType = effectType
223
255
                        else
224
256
                                self.effect = nil
232
264
 
233
265
        -- Method: count
234
266
        -- Counts how many sprites are in this group.
235
 
        -- 
 
267
        --
236
268
        -- Arguments:
237
269
        --              subgroups - include subgroups?
238
 
        -- 
 
270
        --
239
271
        -- Returns:
240
272
        --              integer count
241
273
 
243
275
                if subgroups then
244
276
                        local count = 0
245
277
 
246
 
                        for _, spr in pairs(self.sprites) do
 
278
                        for _, spr in self:members() do
247
279
                                if spr:instanceOf(Group) then
248
280
                                        count = count + spr:count(true)
249
281
                                else
302
334
        contains = function (self, sprite, recurse)
303
335
                if recurse ~= false then recurse = true end
304
336
 
305
 
                for _, spr in pairs(self.sprites) do
 
337
                for _, spr in self:members() do
306
338
                        if spr == sprite then return true end
307
339
 
308
340
                        if recurse and spr:instanceOf(Group) and spr:contains(sprite) then
331
363
        -- Returns:
332
364
        --              nothing
333
365
 
334
 
        loadLayers = function (self, file, tileClass)
335
 
                local ok, data = pcall(loadstring(Cached:text(file)))
336
 
                local _, _, directory = string.find(file, '^(.*[/\\])')
337
 
                directory = directory or ''
338
 
 
339
 
                if ok then
340
 
                        -- store tile properties by gid
341
 
                        
342
 
                        local tileProtos = {}
343
 
 
344
 
                        for _, tileset in pairs(data.tilesets) do
345
 
                                for _, tile in pairs(tileset.tiles) do
346
 
                                        local id = tileset.firstgid + tile.id
347
 
                                        
348
 
                                        for key, value in pairs(tile.properties) do
349
 
                                                tile.properties[key] = tovalue(value)
350
 
                                        end
351
 
 
352
 
                                        tileProtos[id] = tile
353
 
                                        tileProtos[id].width = tileset.tilewidth
354
 
                                        tileProtos[id].height = tileset.tileheight
355
 
                                end
356
 
                        end
357
 
 
358
 
                        for _, layer in pairs(data.layers) do
359
 
                                if self.prototype[layer.name] then
360
 
                                        error('The class you are loading layers into reserves the ' .. layer.name .. ' property for its own use; you cannot load a layer with that name')
361
 
                                end
362
 
 
363
 
                                if STRICT and self[layer.name] then
364
 
                                        local info = debug.getinfo(2, 'Sl')
365
 
                                        print('Warning: a property named ' .. layer.name .. ' already exists in this group (' ..
366
 
                                                  info.short_src .. ', line ' .. info.currentline .. ')')
367
 
                                end
368
 
 
369
 
                                if layer.type == 'tilelayer' then
370
 
                                        local map = Map:new{ spriteWidth = data.tilewidth, spriteHeight = data.tileheight }
371
 
                                        map:empty(layer.width, layer.height)
372
 
 
373
 
                                        -- load tiles
374
 
 
375
 
                                        for _, tiles in pairs(data.tilesets) do
376
 
                                                map:loadTiles(directory .. tiles.image, tileClass or Tile, tiles.firstgid)
377
 
 
378
 
                                                -- and mix in properties where applicable
379
 
 
380
 
                                                for id, tile in pairs(tileProtos) do
381
 
                                                        if map.sprites[id] then
382
 
                                                                map.sprites[id]:mixin(tile.properties)
383
 
                                                        end
384
 
                                                end
385
 
                                        end
386
 
 
387
 
                                        -- load tile data
388
 
 
389
 
                                        local x = 1
390
 
                                        local y = 1
391
 
 
392
 
                                        for _, val in ipairs(layer.data) do
393
 
                                                map.map[x][y] = val
394
 
                                                x = x + 1
395
 
 
396
 
                                                if x > layer.width then
397
 
                                                        x = 1
398
 
                                                        y = y + 1
399
 
                                                end
400
 
                                        end
401
 
 
402
 
                                        self[layer.name] = map
403
 
                                        self:add(map)
404
 
                                elseif layer.type == 'objectgroup' then
405
 
                                        local group = Group:new()
406
 
 
407
 
                                        for _, obj in pairs(layer.objects) do
408
 
                                                -- roll in tile properties if based on a tile
409
 
 
410
 
                                                if obj.gid and tileProtos[obj.gid] then
411
 
                                                        local tile = tileProtos[obj.gid]
412
 
 
413
 
                                                        obj.name = tile.properties.name
414
 
                                                        obj.width = tile.width
415
 
                                                        obj.height = tile.height
416
 
 
417
 
                                                        for key, value in pairs(tile.properties) do
418
 
                                                                obj.properties[key] = tovalue(value)
419
 
                                                        end
420
 
 
421
 
                                                        -- Tiled tile-based objects measure their y
422
 
                                                        -- position at their lower-left corner, instead
423
 
                                                        -- of their upper-left corner as usual
424
 
 
425
 
                                                        obj.y = obj.y - obj.height
426
 
                                                end
427
 
 
428
 
                                                -- create a new object if the class does exist
429
 
 
430
 
                                                local spr
431
 
 
432
 
                                                if _G[obj.name] then
433
 
                                                        obj.properties.x = obj.x
434
 
                                                        obj.properties.y = obj.y
435
 
                                                        obj.properties.width = obj.width
436
 
                                                        obj.properties.height = obj.height
437
 
 
438
 
                                                        spr = _G[obj.name]:new(obj.properties)
439
 
                                                else
440
 
                                                        spr = Fill:new{ x = obj.x, y = obj.y, width = obj.width, height = obj.height, fill = { 128, 128, 128 } }
441
 
                                                end
442
 
 
443
 
                                                if obj.properties._the then
444
 
                                                        the[obj.properties._the] = spr
445
 
                                                end
446
 
 
447
 
                                                group:add(spr)
448
 
                                        end
449
 
 
450
 
                                        self[layer.name] = group
451
 
                                        self:add(group)
452
 
                                else
453
 
                                        error("don't know how to create a " .. layer.type .. " layer from file data")
454
 
                                end
455
 
                        end
456
 
                else
457
 
                        error('could not load layers from file: ' .. data)
458
 
                end
459
 
        end,
 
366
    loadLayers = function (self, file, tileClass)
 
367
        local ok, data = pcall(loadstring(Cached:text(file)))
 
368
        local _, _, directory = string.find(file, '^(.*[/\\])')
 
369
        directory = directory or ''
 
370
 
 
371
        if ok then
 
372
            -- store tile properties by gid
 
373
 
 
374
            local tileProtos = {}
 
375
 
 
376
            for _, tileset in pairs(data.tilesets) do
 
377
                for _, tile in pairs(tileset.tiles) do
 
378
                    local id = tileset.firstgid + tile.id
 
379
 
 
380
                    for key, value in pairs(tile.properties) do
 
381
                        tile.properties[key] = tovalue(value)
 
382
                    end
 
383
 
 
384
                    tileProtos[id] = tile
 
385
                    tileProtos[id].width = tileset.tilewidth
 
386
                    tileProtos[id].height = tileset.tileheight
 
387
                end
 
388
            end
 
389
 
 
390
            for _, layer in pairs(data.layers) do
 
391
                if self.prototype[layer.name] then
 
392
                    error('The class you are loading layers into reserves the ' .. layer.name .. ' property for its own use; you cannot load a layer with that name')
 
393
                end
 
394
 
 
395
                if STRICT and self[layer.name] then
 
396
                    local info = debug.getinfo(2, 'Sl')
 
397
                    print('Warning: a property named ' .. layer.name .. ' already exists in this group (' ..
 
398
                          info.short_src .. ', line ' .. info.currentline .. ')')
 
399
                end
 
400
 
 
401
                if layer.type == 'tilelayer' then
 
402
                    local map = Map:new{ spriteWidth = data.tilewidth, spriteHeight = data.tileheight }
 
403
                    map:empty(layer.width, layer.height)
 
404
 
 
405
                    -- load tiles
 
406
 
 
407
                    for _, tiles in pairs(data.tilesets) do
 
408
                        map:loadTiles(directory .. tiles.image, tileClass or Tile, tiles.firstgid)
 
409
 
 
410
                        -- and mix in properties where applicable
 
411
 
 
412
                        for id, tile in pairs(tileProtos) do
 
413
                            if map.sprites[id] then
 
414
                                map.sprites[id]:mixin(tile.properties)
 
415
                            end
 
416
                        end
 
417
                    end
 
418
 
 
419
                    -- load tile data
 
420
 
 
421
                    local x = 1
 
422
                    local y = 1
 
423
 
 
424
                    for _, val in ipairs(layer.data) do
 
425
                        map.map[x][y] = val
 
426
                        x = x + 1
 
427
 
 
428
                        if x > layer.width then
 
429
                            x = 1
 
430
                            y = y + 1
 
431
                        end
 
432
                    end
 
433
 
 
434
                    self[layer.name] = map
 
435
                    self:add(map)
 
436
                elseif layer.type == 'objectgroup' then
 
437
                    local group = Group:new()
 
438
 
 
439
                    for _, obj in pairs(layer.objects) do
 
440
                        -- roll in tile properties if based on a tile
 
441
 
 
442
                        if obj.gid and tileProtos[obj.gid] then
 
443
                            local tile = tileProtos[obj.gid]
 
444
 
 
445
                            obj.name = tile.properties.name
 
446
                            obj.width = tile.width
 
447
                            obj.height = tile.height
 
448
 
 
449
                            for key, value in pairs(tile.properties) do
 
450
                                obj.properties[key] = tovalue(value)
 
451
                            end
 
452
 
 
453
                            -- Tiled tile-based objects measure their y
 
454
                            -- position at their lower-left corner, instead
 
455
                            -- of their upper-left corner as usual
 
456
 
 
457
                            obj.y = obj.y - obj.height
 
458
                        end
 
459
 
 
460
                        -- create a new object if the class does exist
 
461
 
 
462
                        local spr
 
463
 
 
464
                        if _G[obj.name] then
 
465
                            obj.properties.x = obj.x
 
466
                            obj.properties.y = obj.y
 
467
                            obj.properties.width = obj.width
 
468
                            obj.properties.height = obj.height
 
469
 
 
470
                            spr = _G[obj.name]:new(obj.properties)
 
471
                        else
 
472
                            spr = Fill:new{ x = obj.x, y = obj.y, width = obj.width, height = obj.height, fill = { 128, 128, 128 } }
 
473
                        end
 
474
 
 
475
                        if obj.properties._the then
 
476
                            the[obj.properties._the] = spr
 
477
                        end
 
478
 
 
479
                        group:add(spr)
 
480
                    end
 
481
 
 
482
                    self[layer.name] = group
 
483
                    self:add(group)
 
484
                else
 
485
                    error("don't know how to create a " .. layer.type .. " layer from file data")
 
486
                end
 
487
            end
 
488
        else
 
489
            error('could not load layers from file: ' .. data)
 
490
        end
 
491
    end,
 
492
 
460
493
 
461
494
        -- passes startFrame events to member sprites
462
495
 
463
496
        startFrame = function (self, elapsed)
464
497
                if not self.active then return end
465
498
                elapsed = elapsed * self.timeScale
466
 
                
467
 
                for _, spr in pairs(self.sprites) do
 
499
 
 
500
                for _, spr in self:members() do
468
501
                        if spr.active then spr:startFrame(elapsed) end
469
502
                end
470
503
 
477
510
                if not self.active then return end
478
511
                elapsed = elapsed * self.timeScale
479
512
 
480
 
                for _, spr in pairs(self.sprites) do
 
513
                for _, spr in self:members() do
481
514
                        if spr.active then spr:update(elapsed) end
482
515
                end
483
516
 
490
523
                if not self.active then return end
491
524
                elapsed = elapsed * self.timeScale
492
525
 
493
 
                for _, spr in pairs(self.sprites) do
 
526
                for _, spr in self:members() do
494
527
                        if spr.active then spr:endFrame(elapsed) end
495
528
                end
496
529
 
504
537
        --              x - x offset in pixels
505
538
        --              y - y offset in pixels
506
539
 
507
 
        draw = function (self, x, y)
 
540
        draw = function (self, x, y, yield)
508
541
                if not self.visible then return end
509
542
                x = x or self.translate.x
510
543
                y = y or self.translate.y
511
 
                
 
544
 
512
545
                local scrollX = x * self.translateScale.x
513
546
                local scrollY = y * self.translateScale.y
514
547
                local appWidth = the.app.width
515
548
                local appHeight = the.app.height
 
549
                local scaled = self.scale ~= 1 or self.distort.x ~= 1 or self.distort.y ~= 1 or self.realTranslate.x ~= 0 or self.realTranslate.y ~= 0
516
550
 
517
551
                if self.effect then
518
552
                        if self.effectType == 'screen' then
520
554
                                self._canvas:clear()
521
555
                                love.graphics.setCanvas(self._canvas)
522
556
                        elseif self.effectType == 'sprite' then
523
 
                                love.graphics.setPixelEffect(self.effect)
 
557
                                love.graphics.setShader(self.effect)
524
558
                        end
525
559
                end
526
 
                
527
 
                for _, spr in pairs(self.sprites) do    
 
560
 
 
561
                if scaled then
 
562
                        local scaleX = self.scale * self.distort.x
 
563
                        local scaleY = self.scale * self.distort.y
 
564
 
 
565
                        love.graphics.push()
 
566
 
 
567
      love.graphics.translate(self.realTranslate.x, self.realTranslate.y)
 
568
 
 
569
                        love.graphics.translate(self.origin.x, self.origin.y)
 
570
                        love.graphics.scale(scaleX, scaleY)
 
571
                        love.graphics.translate(- self.origin.x, - self.origin.y)
 
572
                end
 
573
 
 
574
                for _, spr in self:members() do
528
575
                        if spr.visible then
529
576
                                if spr.translate then
530
577
                                        spr:draw(spr.translate.x + scrollX, spr.translate.y + scrollY)
541
588
                                end
542
589
                        end
543
590
                end
544
 
                        
 
591
 
 
592
    if yield then yield(self, x, y) end
 
593
 
 
594
                if scaled then
 
595
                        love.graphics.pop()
 
596
                end
 
597
 
545
598
                if self.effect then
546
599
                        if self.effectType == 'screen' then
547
 
                                love.graphics.setPixelEffect(self.effect)
 
600
                                love.graphics.setShader(self.effect)
548
601
                                love.graphics.setCanvas()
549
602
                                love.graphics.draw(self._canvas)
550
603
                        end
551
604
 
552
 
                        love.graphics.setPixelEffect()
 
605
                        love.graphics.setShader()
553
606
                end
554
607
        end,
555
608