/ld26

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

« back to all changes in this revision

Viewing changes to zoetrope/core/view.lua

  • Committer: Josh C
  • Date: 2013-04-27 16:36:06 UTC
  • Revision ID: josh@9ix.org-20130427163606-0eef0f5v9wbzoi0j
zoetrope 1.4

Show diffs side-by-side

added added

removed removed

 
1
-- Class: View
 
2
-- A view is a group that packages several useful objects with it.
 
3
-- It's helpful to use, but not required. When a view is created, it
 
4
-- automatically sets the.view for itself. the.view should be considered
 
5
-- a read-only reference. If you want to switch views, you *must* set
 
6
-- the app's view property instead.
 
7
--
 
8
-- Extends:
 
9
--              <Group>
 
10
 
 
11
View = Group:extend
 
12
{
 
13
        -- Property: timer
 
14
        -- A built-in <Timer> object for use as needed.
 
15
 
 
16
        -- Property: tween
 
17
        -- A built-in <Tween> object for use as needed.
 
18
 
 
19
        -- Property: factory
 
20
        -- A built-in <Factory> object for use as needed.
 
21
 
 
22
        -- Property: focus
 
23
        -- A <Sprite> to keep centered onscreen.
 
24
 
 
25
        -- Property: focusOffset
 
26
        -- This shifts the view of the focus, if one is set. If both
 
27
        -- x and y properties are set to 0, then the view keeps the focus
 
28
        -- centered onscreen.
 
29
        focusOffset = { x = 0, y = 0 },
 
30
 
 
31
        -- Property: minVisible
 
32
        -- The view clamps its scrolling so that nothing above or to the left
 
33
        -- of these x and y coordinates is visible.
 
34
        minVisible = { x = -math.huge, y = -math.huge },
 
35
 
 
36
        -- Property: maxVisible
 
37
        -- This view clamps its scrolling so that nothing below or to the right
 
38
        -- of these x and y coordinates is visible.
 
39
        maxVisible = { x = math.huge, y = math.huge },
 
40
 
 
41
        -- private property: _tint
 
42
        -- used to implement tints.
 
43
 
 
44
        -- private property: _fx
 
45
        -- used to perform fades and flashes.
 
46
 
 
47
        new = function (self, obj)
 
48
                obj = self:extend(obj)
 
49
 
 
50
                obj.timer = Timer:new()
 
51
                obj:add(obj.timer)
 
52
                obj.tween = Tween:new()
 
53
                obj:add(obj.tween)
 
54
                obj.factory = Factory:new()
 
55
 
 
56
                -- set the.view briefly, so that during the onNew() handler
 
57
                -- we appear to be the current view
 
58
        
 
59
                local oldView = the.view
 
60
 
 
61
                the.view = obj
 
62
                if obj.onNew then obj:onNew() end
 
63
 
 
64
                -- then reset it so that nothing breaks for the remainder
 
65
                -- of the frame for the old, outgoing view members.
 
66
                -- our parent app will restore us into the.view at the top of the next frame
 
67
                -- exception: there was no old view.
 
68
 
 
69
                if oldView then the.view = oldView end
 
70
                return obj
 
71
        end,
 
72
 
 
73
        -- Method: clampTo
 
74
        -- Clamps the view so that it never scrolls past a sprite's boundaries.
 
75
        -- This only looks at the sprite's position at this instant in time,
 
76
        -- not afterwards.
 
77
        --
 
78
        -- Arguments:
 
79
        --              sprite - sprite to clamp to
 
80
        --
 
81
        -- Returns:
 
82
        --              nothing
 
83
 
 
84
        clampTo = function (self, sprite)
 
85
                self.minVisible.x = sprite.x
 
86
                
 
87
                if sprite.x + sprite.width > the.app.width then
 
88
                        self.maxVisible.x = sprite.x + sprite.width
 
89
                else
 
90
                        self.maxVisible.x = the.app.width
 
91
                end
 
92
                
 
93
                self.minVisible.y = sprite.y
 
94
                
 
95
                if sprite.y + sprite.height > the.app.height then
 
96
                        self.maxVisible.y = sprite.y + sprite.height
 
97
                else
 
98
                        self.maxVisible.y = the.app.height
 
99
                end
 
100
        end,
 
101
 
 
102
        -- Method: panTo
 
103
        -- Pans the view so that the target sprite or position is centered
 
104
        -- onscreen. This sets the view's focus to nil.
 
105
        --
 
106
        -- Arguments:
 
107
        --              target - sprite or coordinate pair to pan to
 
108
        --              duration - how long the pan will take, in seconds
 
109
        --              ease - what easing to apply, see <Tween> for details, defaults to 'quadInOut'
 
110
        --
 
111
        -- Returns:
 
112
        --              A <Promise> that is fulfilled when the pan completes.
 
113
 
 
114
        panTo = function (self, target, duration, ease)
 
115
                ease = ease or 'quadInOut'
 
116
                local targetX, targetY
 
117
 
 
118
                if STRICT then
 
119
                        assert((target.x and target.y and target.width and target.height) or (#target == 2),
 
120
                                   'pan target does not appear to be a sprite or coordinate pair')
 
121
                        assert(type(duration) == 'number', 'pan duration is not a number')
 
122
                        assert(self.tween.easers[ease], 'pan easing method ' .. ease .. ' is not defined')
 
123
                end
 
124
 
 
125
                if target.x and target.y and target.width and target.height then
 
126
                        targetX = target.x + target.width / 2
 
127
                        targetY = target.y + target.height / 2
 
128
                else
 
129
                        targetX = target[1]
 
130
                        targetY = target[2]
 
131
                end
 
132
 
 
133
                -- calculate translation to center these coordinates
 
134
 
 
135
                local tranX = math.floor(-targetX + the.app.width / 2)
 
136
                local tranY = math.floor(-targetY + the.app.height / 2)
 
137
                
 
138
                -- clamp translation to min and max visible
 
139
                
 
140
                if tranX > - self.minVisible.x then tranX = - self.minVisible.x end
 
141
                if tranY > - self.minVisible.y then tranY = - self.minVisible.y end
 
142
                
 
143
                if tranX < the.app.width - self.maxVisible.x then
 
144
                        tranX = the.app.width - self.maxVisible.x
 
145
                end
 
146
                
 
147
                if tranY < the.app.height - self.maxVisible.y then
 
148
                        tranY = the.app.height - self.maxVisible.y
 
149
                end
 
150
 
 
151
                -- tween the appropriate properties
 
152
                -- some care has to be taken to avoid fulfilling the promise twice
 
153
 
 
154
                self.focus = nil
 
155
                local promise = Promise:new()
 
156
 
 
157
                if tranX ~= self.translate.x then
 
158
                        self.tween:start(self.translate, 'x', tranX, duration, ease)
 
159
                                :andThen(function() promise:fulfill() end)
 
160
 
 
161
                        if tranY ~= self.translate.y then
 
162
                                self.tween:start(self.translate, 'y', tranY, duration, ease)
 
163
                        end
 
164
                elseif tranY ~= self.translate.y then
 
165
                        self.tween:start(self.translate, 'y', tranY, duration, ease)
 
166
                                :andThen(function() promise:fulfill() end)
 
167
                else
 
168
                        promise:fulfill()
 
169
                end
 
170
 
 
171
                return promise
 
172
        end,
 
173
 
 
174
        -- Method: fade
 
175
        -- Fades out to a specified color over a period of time.
 
176
        --
 
177
        -- Arguments:
 
178
        --              color - color table to fade to, e.g. { 0, 0, 0 }
 
179
        --              duration - how long to fade out in seconds, default 1
 
180
        --
 
181
        -- Returns:
 
182
        --              A <Promise> that is fulfilled when the effect completes.
 
183
 
 
184
        fade = function (self, color, duration)
 
185
                assert(type(color) == 'table', 'color to fade to is ' .. type(color) .. ', not a table')
 
186
                local alpha = color[4] or 255
 
187
                self._fx = color
 
188
                self._fx[4] = 0
 
189
                return self.tween:start(self._fx, 4, alpha, duration or 1, 'quadOut')
 
190
        end,
 
191
 
 
192
        -- Method: flash
 
193
        -- Immediately flashes the screen to a specific color, then fades out.
 
194
        --
 
195
        -- Arguments:
 
196
        --              color - color table to flash, e.g. { 0, 0, 0 }
 
197
        --              duration - how long to restore normal view in seconds, default 1
 
198
        --
 
199
        -- Returns:
 
200
        --              A <Promise> that is fulfilled when the effect completes.
 
201
 
 
202
        flash = function (self, color, duration)
 
203
                assert(type(color) == 'table', 'color to flash is ' .. type(color) .. ', not a table')
 
204
                color[4] = color[4] or 255
 
205
                self._fx = color
 
206
                return self.tween:start(self._fx, 4, 0, duration or 1, 'quadOut')
 
207
        end,
 
208
 
 
209
        -- Method: tint
 
210
        -- Immediately tints the screen a color. To restore normal viewing,
 
211
        -- call this method again with no arguments.
 
212
        --
 
213
        -- Arguments:
 
214
        --              red - red component, 0-255
 
215
        --              green - green component, 0-255
 
216
        --              blue - blue component, 0-255
 
217
        --              alpha - alpha, 0-255, default 255
 
218
        --
 
219
        -- Returns:
 
220
        --              nothing
 
221
 
 
222
        tint = function (self, red, green, blue, alpha)
 
223
                alpha = alpha or 255
 
224
 
 
225
                if red and green and blue and alpha > 0 then
 
226
                        self._tint = { red, green, blue, alpha }
 
227
                else
 
228
                        self._tint = nil
 
229
                end
 
230
        end,
 
231
 
 
232
        update = function (self, elapsed)
 
233
                Group.update(self, elapsed)
 
234
 
 
235
                -- follow the focused sprite
 
236
 
 
237
                local screenWidth = the.app.width
 
238
                local screenHeight = the.app.height
 
239
                
 
240
                if self.focus and self.focus.width < screenWidth
 
241
                   and self.focus.height < screenHeight then
 
242
                        self.translate.x = math.floor(- (self.focus.x + self.focusOffset.x) +
 
243
                                                           (screenWidth - self.focus.width) / 2)
 
244
                        self.translate.y = math.floor(- (self.focus.y + self.focusOffset.y) +
 
245
                                                           (screenHeight - self.focus.height) / 2)
 
246
                end
 
247
                
 
248
                -- clamp translation to min and max visible
 
249
                
 
250
                if self.translate.x > - self.minVisible.x then
 
251
                        self.translate.x = - self.minVisible.x
 
252
                end
 
253
 
 
254
                if self.translate.y > - self.minVisible.y then
 
255
                        self.translate.y = - self.minVisible.y
 
256
                end
 
257
                
 
258
                if self.translate.x < screenWidth - self.maxVisible.x then
 
259
                        self.translate.x = screenWidth - self.maxVisible.x
 
260
                end
 
261
                
 
262
                if self.translate.y < screenHeight - self.maxVisible.y then
 
263
                        self.translate.y = screenHeight - self.maxVisible.y
 
264
                end
 
265
        end,
 
266
 
 
267
        draw = function (self, x, y)
 
268
                Group.draw(self, x, y)
 
269
 
 
270
                -- draw our fx and tint on top of everything
 
271
 
 
272
                if self._tint then
 
273
                        love.graphics.setColor(self._tint)
 
274
                        love.graphics.rectangle('fill', 0, 0, the.app.width, the.app.height)
 
275
                        love.graphics.setColor(255, 255, 255, 255)
 
276
                end
 
277
 
 
278
                if self._fx then
 
279
                        love.graphics.setColor(self._fx)
 
280
                        love.graphics.rectangle('fill', 0, 0, the.app.width, the.app.height)
 
281
                        love.graphics.setColor(255, 255, 255, 255)
 
282
                end
 
283
        end,
 
284
 
 
285
        __tostring = function (self)
 
286
                local result = 'View ('
 
287
 
 
288
                if self.active then
 
289
                        result = result .. 'active'
 
290
                else
 
291
                        result = result .. 'inactive'
 
292
                end
 
293
 
 
294
                if self.visible then
 
295
                        result = result .. ', visible'
 
296
                else
 
297
                        result = result .. ', invisible'
 
298
                end
 
299
 
 
300
                if self.solid then
 
301
                        result = result .. ', solid'
 
302
                else
 
303
                        result = result .. ', not solid'
 
304
                end
 
305
 
 
306
                return result .. ', ' .. self:count(true) .. ' sprites)'
 
307
        end
 
308
}