minigl 2.2.3 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,379 +1,379 @@
1
- require_relative 'movement'
2
-
3
- module MiniGL
4
- # This class represents an (optionally animated) image inside the game screen.
5
- class Sprite
6
- # The index of the current sprite in the spritesheet being drawn.
7
- attr_reader :img_index
8
-
9
- # The x-coordinate of the image in the screen.
10
- attr_accessor :x
11
-
12
- # The y-coordinate of the image in the screen.
13
- attr_accessor :y
14
-
15
- # Creates a new sprite.
16
- #
17
- # Parameters:
18
- # [x] The x-coordinate in the screen (or map) where the sprite will be
19
- # drawn. This can be modified later via the +x+ attribute.
20
- # [y] The y-coordinate in the screen (or map) where the sprite will be
21
- # drawn. This can be modified later via the +y+ attribute.
22
- # [img] The path to a PNG image or spritesheet, following the MiniGL
23
- # convention: images must be inside a 'data/img' directory, relative
24
- # to the code file, and you must only provide the file name, without
25
- # extension, in this case. If the image is inside a subdirectory of
26
- # 'data/img', you must prefix the file name with each subdirectory
27
- # name, followed by an underscore (so the file and directories names
28
- # must not contain underscores). For example, if your image is
29
- # 'data/img/sprite/1.png', you must provide <code>"sprite_1"</code>
30
- # or +:sprite_1+.
31
- # [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
32
- # image is not a spritesheet.
33
- # [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
34
- # image is not a spritesheet.
35
- def initialize(x, y, img, sprite_cols = nil, sprite_rows = nil, retro = nil)
36
- @x = x; @y = y
37
- retro = Res.retro_images if retro.nil?
38
- @img =
39
- if sprite_cols.nil?
40
- [Res.img(img, false, false, '.png', retro)]
41
- else
42
- Res.imgs img, sprite_cols, sprite_rows, false, '.png', retro
43
- end
44
- @anim_counter = 0
45
- @img_index = 0
46
- @index_index = 0
47
- @animate_once_control = 0
48
- end
49
-
50
- # Performs time checking to update the image index according to the
51
- # sequence of indices and the interval.
52
- #
53
- # Parameters:
54
- # [indices] The sequence of image indices used in the animation. The
55
- # indices are determined from left to right, and from top to
56
- # bottom, inside the spritesheet. All indices must be in the
57
- # interval <code>0..(sprite_cols * sprite_rows)</code>.
58
- # [interval] The amount of frames between each change in the image index.
59
- # A frame will usually represent 1/60 second (roughly 17
60
- # milliseconds).
61
- def animate(indices, interval)
62
- @animate_once_control = 0 if @animate_once_control != 0
63
-
64
- @anim_counter += 1
65
- if @anim_counter >= interval
66
- @index_index += 1
67
- @index_index = 0 if @index_index >= indices.length
68
- @img_index = indices[@index_index]
69
- @anim_counter = 0
70
- end
71
- end
72
-
73
- # Causes the sprite to animate through the +indices+ array exactly once,
74
- # so that the animation stops once it reaches the last index in the array.
75
- # Subsequent calls with the same parameters will have no effect, but if
76
- # the index or interval changes, or if +set_animation+ is called, then a
77
- # new animation cycle will begin.
78
- #
79
- # Parameters:
80
- # [indices] The sequence of image indices used in the animation. See
81
- # +animate+ for details.
82
- # [interval] The amount of frames between each change in the image index.
83
- # See +animate+ for details.
84
- def animate_once(indices, interval)
85
- if @animate_once_control == 2
86
- return if indices == @animate_once_indices && interval == @animate_once_interval
87
- @animate_once_control = 0
88
- end
89
-
90
- unless @animate_once_control == 1
91
- @anim_counter = 0
92
- @img_index = indices[0]
93
- @index_index = 0
94
- @animate_once_indices = indices
95
- @animate_once_interval = interval
96
- @animate_once_control = 1
97
- return
98
- end
99
-
100
- @anim_counter += 1
101
- return unless @anim_counter >= interval
102
-
103
- @index_index += 1
104
- @img_index = indices[@index_index]
105
- @anim_counter = 0
106
- @animate_once_control = 2 if @index_index == indices.length - 1
107
- end
108
-
109
- # Resets the animation timer and immediately changes the image index to
110
- # the specified value.
111
- #
112
- # Parameters:
113
- # [index] The image index to be set.
114
- def set_animation(index)
115
- @anim_counter = 0
116
- @img_index = index
117
- @index_index = 0
118
- @animate_once_control = 0
119
- end
120
-
121
- # Draws the sprite in the screen
122
- #
123
- # Parameters:
124
- # [map] A Map object, relative to which the sprite will be drawn (the x
125
- # and y coordinates of the sprite will be changed according to the
126
- # position of the camera).
127
- # [scale_x] A scale factor to be applied horizontally to the image.
128
- # [scale_y] A scale factor to be applied vertically to the image.
129
- # [alpha] The opacity with which the image will be drawn. Valid values
130
- # vary from 0 (fully transparent) to 255 (fully opaque).
131
- # [color] A color filter to apply to the image. A white (0xffffff) filter
132
- # will keep all colors unchanged, while a black (0x000000) filter
133
- # will turn all colors to black. A red (0xff0000) filter will keep
134
- # reddish colors with slight or no change, whereas bluish colors
135
- # will be darkened, for example.
136
- # [angle] A rotation, in degrees, to be applied to the image, relative to
137
- # its center.
138
- # [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
139
- # to draw it vertically flipped.
140
- # [z_index] The z-order to draw the object. Objects with larger z-orders
141
- # will be drawn on top of the ones with smaller z-orders.
142
- # [round] Specify whether the drawing coordinates should be rounded to an
143
- # integer before drawing, to avoid little distortions of the image.
144
- # Only applies when the image is not rotated.
145
- #
146
- # *Obs.:* This method accepts named parameters.
147
- def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
148
- if map.is_a? Hash
149
- scale_x = map.fetch(:scale_x, 1)
150
- scale_y = map.fetch(:scale_y, 1)
151
- alpha = map.fetch(:alpha, 0xff)
152
- color = map.fetch(:color, 0xffffff)
153
- angle = map.fetch(:angle, nil)
154
- flip = map.fetch(:flip, nil)
155
- z_index = map.fetch(:z_index, 0)
156
- round = map.fetch(:round, false)
157
- map = map.fetch(:map, nil)
158
- end
159
-
160
- color = (alpha << 24) | color
161
- if angle
162
- @img[@img_index].draw_rot @x - (map ? map.cam.x : 0) + @img[0].width * scale_x * 0.5,
163
- @y - (map ? map.cam.y : 0) + @img[0].height * scale_y * 0.5,
164
- z_index, angle, 0.5, 0.5, (flip == :horiz ? -scale_x : scale_x),
165
- (flip == :vert ? -scale_y : scale_y), color
166
- else
167
- x = @x - (map ? map.cam.x : 0) + (flip == :horiz ? scale_x * @img[0].width : 0)
168
- y = @y - (map ? map.cam.y : 0) + (flip == :vert ? scale_y * @img[0].height : 0)
169
- @img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
170
- z_index, (flip == :horiz ? -scale_x : scale_x),
171
- (flip == :vert ? -scale_y : scale_y), color
172
- end
173
- end
174
-
175
- # Returns whether this sprite is visible in the given map (i.e., in the
176
- # viewport determined by the camera of the given map). If no map is given,
177
- # returns whether the sprite is visible on the screen.
178
- def visible?(map = nil)
179
- r = Rectangle.new @x, @y, @img[0].width, @img[0].height
180
- return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
181
- map.cam.intersect? r
182
- end
183
- end
184
-
185
- # This class represents an object with a set of properties and methods
186
- # commonly used in games. It defines an object with a rectangular bounding
187
- # box, and having all the attributes required for using the Movement module.
188
- class GameObject < Sprite
189
- include Movement
190
-
191
- # Creates a new game object.
192
- #
193
- # Parameters:
194
- # [x] The x-coordinate of the object's bounding box. This can be modified
195
- # later via the +x+ attribute.
196
- # [y] The y-coordinate of the object's bounding box. This can be modified
197
- # later via the +y+ attribute.
198
- # [w] The width of the object's bounding box.
199
- # [h] The height of the object's bounding box.
200
- # [img] The image or spritesheet for the object.
201
- # [img_gap] A Vector object representing the difference between the top
202
- # left corner of the bounding box and the coordinates of the
203
- # image. For example, an object with <code>x = 100</code>,
204
- # <code>y = 50</code> and <code>img_gap = Vector.new(-5, -5)</code>
205
- # will be drawn at position (95, 45) of the screen.
206
- # [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
207
- # image is not a spritesheet.
208
- # [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
209
- # image is not a spritesheet.
210
- # [mass] The mass of the object. Details on how it is used can be found
211
- # in the Movement module.
212
- def initialize(x, y, w, h, img, img_gap = nil, sprite_cols = nil, sprite_rows = nil, mass = 1.0, retro = nil)
213
- super x, y, img, sprite_cols, sprite_rows, retro
214
- @w = w; @h = h
215
- @img_gap =
216
- if img_gap.nil?
217
- Vector.new 0, 0
218
- else
219
- img_gap
220
- end
221
- @mass = mass
222
- @speed = Vector.new 0, 0
223
- @max_speed = Vector.new 15, 15
224
- @stored_forces = Vector.new 0, 0
225
- end
226
-
227
- # Draws the game object in the screen.
228
- #
229
- # Parameters:
230
- # [map] A Map object, relative to which the object will be drawn (the x
231
- # and y coordinates of the image will be changed according to the
232
- # position of the camera).
233
- # [scale_x] A scale factor to be applied horizontally to the image.
234
- # [scale_y] A scale factor to be applied vertically to the image.
235
- # [alpha] The opacity with which the image will be drawn. Valid values
236
- # vary from 0 (fully transparent) to 255 (fully opaque).
237
- # [color] A color filter to apply to the image. A white (0xffffff) filter
238
- # will keep all colors unchanged, while a black (0x000000) filter
239
- # will turn all colors to black. A red (0xff0000) filter will keep
240
- # reddish colors with slight or no change, whereas bluish colors
241
- # will be darkened, for example.
242
- # [angle] A rotation, in degrees, to be applied to the image, relative to
243
- # its center.
244
- # [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
245
- # to draw it vertically flipped.
246
- # [z_index] The z-order to draw the object. Objects with larger z-orders
247
- # will be drawn on top of the ones with smaller z-orders.
248
- # [round] Specify whether the drawing coordinates should be rounded to an
249
- # integer before drawing, to avoid little distortions of the image.
250
- # Only applies when the image is not rotated.
251
- #
252
- # *Obs.:* This method accepts named parameters.
253
- def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
254
- if map.is_a? Hash
255
- scale_x = map.fetch(:scale_x, 1)
256
- scale_y = map.fetch(:scale_y, 1)
257
- alpha = map.fetch(:alpha, 0xff)
258
- color = map.fetch(:color, 0xffffff)
259
- angle = map.fetch(:angle, nil)
260
- flip = map.fetch(:flip, nil)
261
- z_index = map.fetch(:z_index, 0)
262
- round = map.fetch(:round, false)
263
- map = map.fetch(:map, nil)
264
- end
265
-
266
- color = (alpha << 24) | color
267
- if angle
268
- center_x = @x + @w * 0.5
269
- center_y = @y + @h * 0.5
270
- o_x = center_x - @x - @img_gap.x
271
- o_y = center_y - @y - @img_gap.y
272
- @img[@img_index].draw_rot @x + (flip == :horiz ? -1 : 1) * (@img_gap.x + o_x) - (map ? map.cam.x : 0),
273
- @y + (flip == :vert ? -1 : 1) * (@img_gap.y + o_y) - (map ? map.cam.y : 0),
274
- z_index, angle, o_x.to_f / (@img[0].width * scale_x), o_y.to_f / (@img[0].height * scale_y),
275
- (flip == :horiz ? -scale_x : scale_x), (flip == :vert ? -scale_y : scale_y), color
276
- else
277
- x = @x + (flip == :horiz ? -1 : 1) * @img_gap.x - (map ? map.cam.x : 0) + (flip == :horiz ? @w : 0)
278
- y = @y + (flip == :vert ? -1 : 1) * @img_gap.y - (map ? map.cam.y : 0) + (flip == :vert ? @h : 0)
279
- @img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
280
- z_index, (flip == :horiz ? -scale_x : scale_x),
281
- (flip == :vert ? -scale_y : scale_y), color
282
- end
283
- end
284
-
285
- # Returns whether this object is visible in the given map (i.e., in the
286
- # viewport determined by the camera of the given map). If no map is given,
287
- # returns whether the object is visible on the screen.
288
- def visible?(map = nil)
289
- r = Rectangle.new @x.round + @img_gap.x, @y.round + @img_gap.y, @img[0].width, @img[0].height
290
- return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
291
- map.cam.intersect? r
292
- end
293
- end
294
-
295
- # Represents a visual effect, i.e., a graphic - usually animated - that shows
296
- # up in the screen, lasts for a given time and "disappears". You should
297
- # explicitly dispose of references to effects whose attribute +dead+ is set
298
- # to +true+.
299
- class Effect < Sprite
300
- # This is +true+ when the effect's lifetime has already passed.
301
- attr_reader :dead
302
-
303
- # Creates a new Effect.
304
- #
305
- # Parameters:
306
- # [x] The x-coordinate in the screen (or map) where the effect will be
307
- # drawn. This can be modified later via the +x+ attribute.
308
- # [y] The y-coordinate in the screen (or map) where the effect will be
309
- # drawn. This can be modified later via the +y+ attribute.
310
- # [img] The image or spritesheet to use for this effect (see Sprite for
311
- # details on spritesheets).
312
- # [sprite_cols] (see Sprite)
313
- # [sprite_rows] (see Sprite)
314
- # [interval] The interval between steps of the animation, in updates.
315
- # [indices] The indices to use in the animation. See Sprite#animate for
316
- # details. If +nil+, it will be the sequence from 0 to
317
- # <code>sprite_cols * sprite_rows - 1</code>.
318
- # [lifetime] The lifetime of the effect, in updates. After +update+ is
319
- # called this number of times, the effect will no longer
320
- # be visible, even when +draw+ is called, and the +dead+ flag
321
- # will be set to +true+, so you get to know when to dispose
322
- # of the Effect object. If +nil+, it will be set to
323
- # <code>@indices.length * interval</code>, i.e., the exact time
324
- # needed for one animation cycle to complete.
325
- # [sound] The id of a sound to be played when the effect is created (id must
326
- # be given in the format specified for the +Res.sound+ method).
327
- # [sound_ext] Extension of the sound file, if a sound is given. Default is
328
- # '.wav'.
329
- # [sound_volume] The volume (from 0 to 1) to play the sound, if any. Default
330
- # is 1.
331
- #
332
- # *Obs.:* This method accepts named parameters, but +x+, +y+ and +img+ are
333
- # mandatory.
334
- def initialize(x, y = nil, img = nil, sprite_cols = nil, sprite_rows = nil, interval = 10, indices = nil, lifetime = nil,
335
- sound = nil, sound_ext = '.wav', sound_volume = 1)
336
- if x.is_a? Hash
337
- y = x[:y]
338
- img = x[:img]
339
- sprite_cols = x.fetch(:sprite_cols, nil)
340
- sprite_rows = x.fetch(:sprite_rows, nil)
341
- interval = x.fetch(:interval, 10)
342
- indices = x.fetch(:indices, nil)
343
- lifetime = x.fetch(:lifetime, nil)
344
- sound = x.fetch(:sound, nil)
345
- sound_ext = x.fetch(:sound_ext, '.wav')
346
- sound_volume = x.fetch(:sound_volume, 1)
347
- x = x[:x]
348
- end
349
-
350
- super x, y, img, sprite_cols, sprite_rows
351
- @timer = 0
352
- if indices
353
- @indices = indices
354
- else
355
- @indices = *(0..(@img.length - 1))
356
- end
357
- @interval = interval
358
- if lifetime
359
- @lifetime = lifetime
360
- else
361
- @lifetime = @indices.length * interval
362
- end
363
- Res.sound(sound, false, sound_ext).play(sound_volume) if sound
364
- end
365
-
366
- # Updates the effect, animating and counting its remaining lifetime.
367
- def update
368
- unless @dead
369
- animate @indices, @interval
370
- @timer += 1
371
- @dead = true if @timer == @lifetime
372
- end
373
- end
374
-
375
- def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, z_index = 0)
376
- super unless @dead
377
- end
378
- end
379
- end
1
+ require_relative 'movement'
2
+
3
+ module MiniGL
4
+ # This class represents an (optionally animated) image inside the game screen.
5
+ class Sprite
6
+ # The index of the current sprite in the spritesheet being drawn.
7
+ attr_reader :img_index
8
+
9
+ # The x-coordinate of the image in the screen.
10
+ attr_accessor :x
11
+
12
+ # The y-coordinate of the image in the screen.
13
+ attr_accessor :y
14
+
15
+ # Creates a new sprite.
16
+ #
17
+ # Parameters:
18
+ # [x] The x-coordinate in the screen (or map) where the sprite will be
19
+ # drawn. This can be modified later via the +x+ attribute.
20
+ # [y] The y-coordinate in the screen (or map) where the sprite will be
21
+ # drawn. This can be modified later via the +y+ attribute.
22
+ # [img] The path to a PNG image or spritesheet, following the MiniGL
23
+ # convention: images must be inside a 'data/img' directory, relative
24
+ # to the code file, and you must only provide the file name, without
25
+ # extension, in this case. If the image is inside a subdirectory of
26
+ # 'data/img', you must prefix the file name with each subdirectory
27
+ # name, followed by an underscore (so the file and directories names
28
+ # must not contain underscores). For example, if your image is
29
+ # 'data/img/sprite/1.png', you must provide <code>"sprite_1"</code>
30
+ # or +:sprite_1+.
31
+ # [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
32
+ # image is not a spritesheet.
33
+ # [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
34
+ # image is not a spritesheet.
35
+ def initialize(x, y, img, sprite_cols = nil, sprite_rows = nil, retro = nil)
36
+ @x = x; @y = y
37
+ retro = Res.retro_images if retro.nil?
38
+ @img =
39
+ if sprite_cols.nil?
40
+ [Res.img(img, false, false, '.png', retro)]
41
+ else
42
+ Res.imgs img, sprite_cols, sprite_rows, false, '.png', retro
43
+ end
44
+ @anim_counter = 0
45
+ @img_index = 0
46
+ @index_index = 0
47
+ @animate_once_control = 0
48
+ end
49
+
50
+ # Performs time checking to update the image index according to the
51
+ # sequence of indices and the interval.
52
+ #
53
+ # Parameters:
54
+ # [indices] The sequence of image indices used in the animation. The
55
+ # indices are determined from left to right, and from top to
56
+ # bottom, inside the spritesheet. All indices must be in the
57
+ # interval <code>0..(sprite_cols * sprite_rows)</code>.
58
+ # [interval] The amount of frames between each change in the image index.
59
+ # A frame will usually represent 1/60 second (roughly 17
60
+ # milliseconds).
61
+ def animate(indices, interval)
62
+ @animate_once_control = 0 if @animate_once_control != 0
63
+
64
+ @anim_counter += 1
65
+ if @anim_counter >= interval
66
+ @index_index += 1
67
+ @index_index = 0 if @index_index >= indices.length
68
+ @img_index = indices[@index_index]
69
+ @anim_counter = 0
70
+ end
71
+ end
72
+
73
+ # Causes the sprite to animate through the +indices+ array exactly once,
74
+ # so that the animation stops once it reaches the last index in the array.
75
+ # Subsequent calls with the same parameters will have no effect, but if
76
+ # the index or interval changes, or if +set_animation+ is called, then a
77
+ # new animation cycle will begin.
78
+ #
79
+ # Parameters:
80
+ # [indices] The sequence of image indices used in the animation. See
81
+ # +animate+ for details.
82
+ # [interval] The amount of frames between each change in the image index.
83
+ # See +animate+ for details.
84
+ def animate_once(indices, interval)
85
+ if @animate_once_control == 2
86
+ return if indices == @animate_once_indices && interval == @animate_once_interval
87
+ @animate_once_control = 0
88
+ end
89
+
90
+ unless @animate_once_control == 1
91
+ @anim_counter = 0
92
+ @img_index = indices[0]
93
+ @index_index = 0
94
+ @animate_once_indices = indices
95
+ @animate_once_interval = interval
96
+ @animate_once_control = 1
97
+ return
98
+ end
99
+
100
+ @anim_counter += 1
101
+ return unless @anim_counter >= interval
102
+
103
+ @index_index += 1
104
+ @img_index = indices[@index_index]
105
+ @anim_counter = 0
106
+ @animate_once_control = 2 if @index_index == indices.length - 1
107
+ end
108
+
109
+ # Resets the animation timer and immediately changes the image index to
110
+ # the specified value.
111
+ #
112
+ # Parameters:
113
+ # [index] The image index to be set.
114
+ def set_animation(index)
115
+ @anim_counter = 0
116
+ @img_index = index
117
+ @index_index = 0
118
+ @animate_once_control = 0
119
+ end
120
+
121
+ # Draws the sprite in the screen
122
+ #
123
+ # Parameters:
124
+ # [map] A Map object, relative to which the sprite will be drawn (the x
125
+ # and y coordinates of the sprite will be changed according to the
126
+ # position of the camera).
127
+ # [scale_x] A scale factor to be applied horizontally to the image.
128
+ # [scale_y] A scale factor to be applied vertically to the image.
129
+ # [alpha] The opacity with which the image will be drawn. Valid values
130
+ # vary from 0 (fully transparent) to 255 (fully opaque).
131
+ # [color] A color filter to apply to the image. A white (0xffffff) filter
132
+ # will keep all colors unchanged, while a black (0x000000) filter
133
+ # will turn all colors to black. A red (0xff0000) filter will keep
134
+ # reddish colors with slight or no change, whereas bluish colors
135
+ # will be darkened, for example.
136
+ # [angle] A rotation, in degrees, to be applied to the image, relative to
137
+ # its center.
138
+ # [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
139
+ # to draw it vertically flipped.
140
+ # [z_index] The z-order to draw the object. Objects with larger z-orders
141
+ # will be drawn on top of the ones with smaller z-orders.
142
+ # [round] Specify whether the drawing coordinates should be rounded to an
143
+ # integer before drawing, to avoid little distortions of the image.
144
+ # Only applies when the image is not rotated.
145
+ #
146
+ # *Obs.:* This method accepts named parameters.
147
+ def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
148
+ if map.is_a? Hash
149
+ scale_x = map.fetch(:scale_x, 1)
150
+ scale_y = map.fetch(:scale_y, 1)
151
+ alpha = map.fetch(:alpha, 0xff)
152
+ color = map.fetch(:color, 0xffffff)
153
+ angle = map.fetch(:angle, nil)
154
+ flip = map.fetch(:flip, nil)
155
+ z_index = map.fetch(:z_index, 0)
156
+ round = map.fetch(:round, false)
157
+ map = map.fetch(:map, nil)
158
+ end
159
+
160
+ color = (alpha << 24) | color
161
+ if angle
162
+ @img[@img_index].draw_rot @x - (map ? map.cam.x : 0) + @img[0].width * scale_x * 0.5,
163
+ @y - (map ? map.cam.y : 0) + @img[0].height * scale_y * 0.5,
164
+ z_index, angle, 0.5, 0.5, (flip == :horiz ? -scale_x : scale_x),
165
+ (flip == :vert ? -scale_y : scale_y), color
166
+ else
167
+ x = @x - (map ? map.cam.x : 0) + (flip == :horiz ? scale_x * @img[0].width : 0)
168
+ y = @y - (map ? map.cam.y : 0) + (flip == :vert ? scale_y * @img[0].height : 0)
169
+ @img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
170
+ z_index, (flip == :horiz ? -scale_x : scale_x),
171
+ (flip == :vert ? -scale_y : scale_y), color
172
+ end
173
+ end
174
+
175
+ # Returns whether this sprite is visible in the given map (i.e., in the
176
+ # viewport determined by the camera of the given map). If no map is given,
177
+ # returns whether the sprite is visible on the screen.
178
+ def visible?(map = nil)
179
+ r = Rectangle.new @x, @y, @img[0].width, @img[0].height
180
+ return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
181
+ map.cam.intersect? r
182
+ end
183
+ end
184
+
185
+ # This class represents an object with a set of properties and methods
186
+ # commonly used in games. It defines an object with a rectangular bounding
187
+ # box, and having all the attributes required for using the Movement module.
188
+ class GameObject < Sprite
189
+ include Movement
190
+
191
+ # Creates a new game object.
192
+ #
193
+ # Parameters:
194
+ # [x] The x-coordinate of the object's bounding box. This can be modified
195
+ # later via the +x+ attribute.
196
+ # [y] The y-coordinate of the object's bounding box. This can be modified
197
+ # later via the +y+ attribute.
198
+ # [w] The width of the object's bounding box.
199
+ # [h] The height of the object's bounding box.
200
+ # [img] The image or spritesheet for the object.
201
+ # [img_gap] A Vector object representing the difference between the top
202
+ # left corner of the bounding box and the coordinates of the
203
+ # image. For example, an object with <code>x = 100</code>,
204
+ # <code>y = 50</code> and <code>img_gap = Vector.new(-5, -5)</code>
205
+ # will be drawn at position (95, 45) of the screen.
206
+ # [sprite_cols] The number of columns in the spritesheet. Use +nil+ if the
207
+ # image is not a spritesheet.
208
+ # [sprite_rows] The number of rows in the spritesheet. Use +nil+ if the
209
+ # image is not a spritesheet.
210
+ # [mass] The mass of the object. Details on how it is used can be found
211
+ # in the Movement module.
212
+ def initialize(x, y, w, h, img, img_gap = nil, sprite_cols = nil, sprite_rows = nil, mass = 1.0, retro = nil)
213
+ super x, y, img, sprite_cols, sprite_rows, retro
214
+ @w = w; @h = h
215
+ @img_gap =
216
+ if img_gap.nil?
217
+ Vector.new 0, 0
218
+ else
219
+ img_gap
220
+ end
221
+ @mass = mass
222
+ @speed = Vector.new 0, 0
223
+ @max_speed = Vector.new 15, 15
224
+ @stored_forces = Vector.new 0, 0
225
+ end
226
+
227
+ # Draws the game object in the screen.
228
+ #
229
+ # Parameters:
230
+ # [map] A Map object, relative to which the object will be drawn (the x
231
+ # and y coordinates of the image will be changed according to the
232
+ # position of the camera).
233
+ # [scale_x] A scale factor to be applied horizontally to the image.
234
+ # [scale_y] A scale factor to be applied vertically to the image.
235
+ # [alpha] The opacity with which the image will be drawn. Valid values
236
+ # vary from 0 (fully transparent) to 255 (fully opaque).
237
+ # [color] A color filter to apply to the image. A white (0xffffff) filter
238
+ # will keep all colors unchanged, while a black (0x000000) filter
239
+ # will turn all colors to black. A red (0xff0000) filter will keep
240
+ # reddish colors with slight or no change, whereas bluish colors
241
+ # will be darkened, for example.
242
+ # [angle] A rotation, in degrees, to be applied to the image, relative to
243
+ # its center.
244
+ # [flip] Specify +:horiz+ to draw the image horizontally flipped or +:vert+
245
+ # to draw it vertically flipped.
246
+ # [z_index] The z-order to draw the object. Objects with larger z-orders
247
+ # will be drawn on top of the ones with smaller z-orders.
248
+ # [round] Specify whether the drawing coordinates should be rounded to an
249
+ # integer before drawing, to avoid little distortions of the image.
250
+ # Only applies when the image is not rotated.
251
+ #
252
+ # *Obs.:* This method accepts named parameters.
253
+ def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, flip = nil, z_index = 0, round = false)
254
+ if map.is_a? Hash
255
+ scale_x = map.fetch(:scale_x, 1)
256
+ scale_y = map.fetch(:scale_y, 1)
257
+ alpha = map.fetch(:alpha, 0xff)
258
+ color = map.fetch(:color, 0xffffff)
259
+ angle = map.fetch(:angle, nil)
260
+ flip = map.fetch(:flip, nil)
261
+ z_index = map.fetch(:z_index, 0)
262
+ round = map.fetch(:round, false)
263
+ map = map.fetch(:map, nil)
264
+ end
265
+
266
+ color = (alpha << 24) | color
267
+ if angle
268
+ center_x = @x + @w * 0.5
269
+ center_y = @y + @h * 0.5
270
+ o_x = center_x - @x - @img_gap.x
271
+ o_y = center_y - @y - @img_gap.y
272
+ @img[@img_index].draw_rot @x + (flip == :horiz ? -1 : 1) * (@img_gap.x + o_x) - (map ? map.cam.x : 0),
273
+ @y + (flip == :vert ? -1 : 1) * (@img_gap.y + o_y) - (map ? map.cam.y : 0),
274
+ z_index, angle, o_x.to_f / (@img[0].width * scale_x), o_y.to_f / (@img[0].height * scale_y),
275
+ (flip == :horiz ? -scale_x : scale_x), (flip == :vert ? -scale_y : scale_y), color
276
+ else
277
+ x = @x + (flip == :horiz ? -1 : 1) * @img_gap.x - (map ? map.cam.x : 0) + (flip == :horiz ? @w : 0)
278
+ y = @y + (flip == :vert ? -1 : 1) * @img_gap.y - (map ? map.cam.y : 0) + (flip == :vert ? @h : 0)
279
+ @img[@img_index].draw (round ? x.round : x), (round ? y.round : y),
280
+ z_index, (flip == :horiz ? -scale_x : scale_x),
281
+ (flip == :vert ? -scale_y : scale_y), color
282
+ end
283
+ end
284
+
285
+ # Returns whether this object is visible in the given map (i.e., in the
286
+ # viewport determined by the camera of the given map). If no map is given,
287
+ # returns whether the object is visible on the screen.
288
+ def visible?(map = nil)
289
+ r = Rectangle.new @x.round + @img_gap.x, @y.round + @img_gap.y, @img[0].width, @img[0].height
290
+ return Rectangle.new(0, 0, G.window.width, G.window.height).intersect? r if map.nil?
291
+ map.cam.intersect? r
292
+ end
293
+ end
294
+
295
+ # Represents a visual effect, i.e., a graphic - usually animated - that shows
296
+ # up in the screen, lasts for a given time and "disappears". You should
297
+ # explicitly dispose of references to effects whose attribute +dead+ is set
298
+ # to +true+.
299
+ class Effect < Sprite
300
+ # This is +true+ when the effect's lifetime has already passed.
301
+ attr_reader :dead
302
+
303
+ # Creates a new Effect.
304
+ #
305
+ # Parameters:
306
+ # [x] The x-coordinate in the screen (or map) where the effect will be
307
+ # drawn. This can be modified later via the +x+ attribute.
308
+ # [y] The y-coordinate in the screen (or map) where the effect will be
309
+ # drawn. This can be modified later via the +y+ attribute.
310
+ # [img] The image or spritesheet to use for this effect (see Sprite for
311
+ # details on spritesheets).
312
+ # [sprite_cols] (see Sprite)
313
+ # [sprite_rows] (see Sprite)
314
+ # [interval] The interval between steps of the animation, in updates.
315
+ # [indices] The indices to use in the animation. See Sprite#animate for
316
+ # details. If +nil+, it will be the sequence from 0 to
317
+ # <code>sprite_cols * sprite_rows - 1</code>.
318
+ # [lifetime] The lifetime of the effect, in updates. After +update+ is
319
+ # called this number of times, the effect will no longer
320
+ # be visible, even when +draw+ is called, and the +dead+ flag
321
+ # will be set to +true+, so you get to know when to dispose
322
+ # of the Effect object. If +nil+, it will be set to
323
+ # <code>@indices.length * interval</code>, i.e., the exact time
324
+ # needed for one animation cycle to complete.
325
+ # [sound] The id of a sound to be played when the effect is created (id must
326
+ # be given in the format specified for the +Res.sound+ method).
327
+ # [sound_ext] Extension of the sound file, if a sound is given. Default is
328
+ # '.wav'.
329
+ # [sound_volume] The volume (from 0 to 1) to play the sound, if any. Default
330
+ # is 1.
331
+ #
332
+ # *Obs.:* This method accepts named parameters, but +x+, +y+ and +img+ are
333
+ # mandatory.
334
+ def initialize(x, y = nil, img = nil, sprite_cols = nil, sprite_rows = nil, interval = 10, indices = nil, lifetime = nil,
335
+ sound = nil, sound_ext = '.wav', sound_volume = 1)
336
+ if x.is_a? Hash
337
+ y = x[:y]
338
+ img = x[:img]
339
+ sprite_cols = x.fetch(:sprite_cols, nil)
340
+ sprite_rows = x.fetch(:sprite_rows, nil)
341
+ interval = x.fetch(:interval, 10)
342
+ indices = x.fetch(:indices, nil)
343
+ lifetime = x.fetch(:lifetime, nil)
344
+ sound = x.fetch(:sound, nil)
345
+ sound_ext = x.fetch(:sound_ext, '.wav')
346
+ sound_volume = x.fetch(:sound_volume, 1)
347
+ x = x[:x]
348
+ end
349
+
350
+ super x, y, img, sprite_cols, sprite_rows
351
+ @timer = 0
352
+ if indices
353
+ @indices = indices
354
+ else
355
+ @indices = *(0..(@img.length - 1))
356
+ end
357
+ @interval = interval
358
+ if lifetime
359
+ @lifetime = lifetime
360
+ else
361
+ @lifetime = @indices.length * interval
362
+ end
363
+ Res.sound(sound, false, sound_ext).play(sound_volume) if sound
364
+ end
365
+
366
+ # Updates the effect, animating and counting its remaining lifetime.
367
+ def update
368
+ unless @dead
369
+ animate @indices, @interval
370
+ @timer += 1
371
+ @dead = true if @timer == @lifetime
372
+ end
373
+ end
374
+
375
+ def draw(map = nil, scale_x = 1, scale_y = 1, alpha = 0xff, color = 0xffffff, angle = nil, z_index = 0)
376
+ super unless @dead
377
+ end
378
+ end
379
+ end