/ld27

To get this branch, use:
bzr branch /bzr/ld27

« back to all changes in this revision

Viewing changes to zoetrope/core/group.lua

  • Committer: Josh C
  • Date: 2014-07-03 14:41:57 UTC
  • Revision ID: josh@9ix.org-20140703144157-xydxt62xzcdq6bdk
cluke009 zoetrope + my spritebatch changes

Show diffs side-by-side

added added

removed removed

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