/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: 2013-08-25 15:24:12 UTC
  • Revision ID: josh@9ix.org-20130825152412-t0uzxw6fd92i81rj
pause/quit screen

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: onBeginFrame
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. You can iterate over this
35
 
        -- table with <members()>.
 
34
        -- A table of member sprites, in drawing order.
36
35
        sprites = {},
37
36
 
38
37
        -- Property: timeScale
43
42
        -- This table's x and y properties shift member sprites' positions when drawn.
44
43
        -- To draw sprites at their normal position, set both x and y to 0.
45
44
        translate = { x = 0, y = 0 },
46
 
 
 
45
        
47
46
        -- Property: translateScale
48
47
        -- This table's x and y properties multiply member sprites'
49
48
        -- positions, which you can use to simulate parallax scrolling. To draw
50
49
        -- sprites at their normal position, set both x and y to 1.
51
50
        translateScale = { x = 1, y = 1 },
52
51
 
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
 
 
66
52
        -- Method: add
67
53
        -- Adds a sprite to the group.
68
54
        --
70
56
        --              sprite - <Sprite> to add
71
57
        --
72
58
        -- Returns:
73
 
        --              the sprite added, so you can write things like
74
 
        --              self.player = self:add(Player:new())
 
59
        --              nothing
75
60
 
76
61
        add = function (self, sprite)
77
62
                assert(sprite, 'asked to add nil to a group')
78
63
                assert(sprite ~= self, "can't add a group to itself")
79
 
 
 
64
        
80
65
                if STRICT and self:contains(sprite) then
81
66
                        local info = debug.getinfo(2, 'Sl')
82
67
                        print('Warning: adding a sprite to a group it already belongs to (' ..
84
69
                end
85
70
 
86
71
                table.insert(self.sprites, sprite)
87
 
                return sprite
88
72
        end,
89
73
 
90
74
        -- Method: remove
91
75
        -- Removes a sprite from the group. If the sprite is
92
76
        -- not in the group, this does nothing.
93
 
        --
 
77
        -- 
94
78
        -- Arguments:
95
79
        --              sprite - <Sprite> to remove
96
 
        --
 
80
        -- 
97
81
        -- Returns:
98
82
        --              nothing
99
83
 
100
84
        remove = function (self, sprite)
101
 
                for i, spr in self:members() do
 
85
                for i, spr in ipairs(self.sprites) do
102
86
                        if spr == sprite then
103
87
                                table.remove(self.sprites, i)
104
88
                                return
105
89
                        end
106
90
                end
107
 
 
 
91
                
108
92
                if STRICT then
109
93
                        local info = debug.getinfo(2, 'Sl')
110
94
                        print('Warning: asked to remove a sprite from a group it was not a member of (' ..
123
107
        --              nothing
124
108
 
125
109
        moveToFront = function (self, sprite)
126
 
                for i, spr in self:members() do
 
110
                for i, spr in ipairs(self.sprites) do
127
111
                        if spr == sprite then
128
112
                                table.remove(self.sprites, i)
129
113
                                table.insert(self.sprites, sprite)
145
129
        --
146
130
        -- Returns:
147
131
        --              nothing
148
 
 
 
132
        
149
133
        moveToBack = function (self, sprite)
150
 
                for i, spr in self:members() do
 
134
                for i, spr in ipairs(self.sprites) do
151
135
                        if spr == sprite then
152
136
                                table.remove(self.sprites, i)
153
137
                                table.insert(self.sprites, 1, sprite)
174
158
                table.sort(self.sprites, func)
175
159
        end,
176
160
 
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
 
 
190
161
        -- Method: collide
191
162
        -- Collides all solid sprites in the group with another sprite or group.
192
163
        -- This calls the <Sprite.onCollide> event handlers on all sprites that
198
169
        -- Arguments:
199
170
        --              ... - any number of <Sprite>s or <Group>s to collide with. If none
200
171
        --                        are specified, the group collides with itself.
201
 
        --
 
172
        -- 
202
173
        -- Returns:
203
174
        --              nothing
204
175
        --
244
215
        setEffect = function (self, filename, effectType)
245
216
                effectType = effectType or 'screen'
246
217
 
247
 
                if love.graphics.isSupported('shader') and
 
218
                if love.graphics.isSupported('pixeleffect') and
248
219
                   (effectType == 'sprite' or love.graphics.isSupported('canvas'))then
249
220
                        if filename then
250
 
                                self.effect = love.graphics.newShader(Cached:text(filename))
 
221
                                self.effect = love.graphics.newPixelEffect(Cached:text(filename))
251
222
                                self.effectType = effectType
252
223
                        else
253
224
                                self.effect = nil
261
232
 
262
233
        -- Method: count
263
234
        -- Counts how many sprites are in this group.
264
 
        --
 
235
        -- 
265
236
        -- Arguments:
266
237
        --              subgroups - include subgroups?
267
 
        --
 
238
        -- 
268
239
        -- Returns:
269
240
        --              integer count
270
241
 
272
243
                if subgroups then
273
244
                        local count = 0
274
245
 
275
 
                        for _, spr in self:members() do
 
246
                        for _, spr in pairs(self.sprites) do
276
247
                                if spr:instanceOf(Group) then
277
248
                                        count = count + spr:count(true)
278
249
                                else
331
302
        contains = function (self, sprite, recurse)
332
303
                if recurse ~= false then recurse = true end
333
304
 
334
 
                for _, spr in self:members() do
 
305
                for _, spr in pairs(self.sprites) do
335
306
                        if spr == sprite then return true end
336
307
 
337
308
                        if recurse and spr:instanceOf(Group) and spr:contains(sprite) then
360
331
        -- Returns:
361
332
        --              nothing
362
333
 
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
 
 
 
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,
490
460
 
491
461
        -- passes startFrame events to member sprites
492
462
 
493
463
        startFrame = function (self, elapsed)
494
464
                if not self.active then return end
495
465
                elapsed = elapsed * self.timeScale
496
 
 
497
 
                for _, spr in self:members() do
 
466
                
 
467
                for _, spr in pairs(self.sprites) do
498
468
                        if spr.active then spr:startFrame(elapsed) end
499
469
                end
500
470
 
507
477
                if not self.active then return end
508
478
                elapsed = elapsed * self.timeScale
509
479
 
510
 
                for _, spr in self:members() do
 
480
                for _, spr in pairs(self.sprites) do
511
481
                        if spr.active then spr:update(elapsed) end
512
482
                end
513
483
 
520
490
                if not self.active then return end
521
491
                elapsed = elapsed * self.timeScale
522
492
 
523
 
                for _, spr in self:members() do
 
493
                for _, spr in pairs(self.sprites) do
524
494
                        if spr.active then spr:endFrame(elapsed) end
525
495
                end
526
496
 
538
508
                if not self.visible then return end
539
509
                x = x or self.translate.x
540
510
                y = y or self.translate.y
541
 
 
 
511
                
542
512
                local scrollX = x * self.translateScale.x
543
513
                local scrollY = y * self.translateScale.y
544
514
                local appWidth = the.app.width
545
515
                local appHeight = the.app.height
546
 
                local scaled = self.scale ~= 1 or self.distort.x ~= 1 or self.distort.y ~= 1
547
516
 
548
517
                if self.effect then
549
518
                        if self.effectType == 'screen' then
551
520
                                self._canvas:clear()
552
521
                                love.graphics.setCanvas(self._canvas)
553
522
                        elseif self.effectType == 'sprite' then
554
 
                                love.graphics.setShader(self.effect)
 
523
                                love.graphics.setPixelEffect(self.effect)
555
524
                        end
556
525
                end
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
 
526
                
 
527
                for _, spr in pairs(self.sprites) do    
569
528
                        if spr.visible then
570
529
                                if spr.translate then
571
530
                                        spr:draw(spr.translate.x + scrollX, spr.translate.y + scrollY)
582
541
                                end
583
542
                        end
584
543
                end
585
 
 
586
 
                if scaled then
587
 
                        love.graphics.pop()
588
 
                end
589
 
 
 
544
                        
590
545
                if self.effect then
591
546
                        if self.effectType == 'screen' then
592
 
                                love.graphics.setShader(self.effect)
 
547
                                love.graphics.setPixelEffect(self.effect)
593
548
                                love.graphics.setCanvas()
594
549
                                love.graphics.draw(self._canvas)
595
550
                        end
596
551
 
597
 
                        love.graphics.setShader()
 
552
                        love.graphics.setPixelEffect()
598
553
                end
599
554
        end,
600
555