rubygame 2.2.0-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CREDITS +60 -0
  2. data/LICENSE +504 -0
  3. data/NEWS +201 -0
  4. data/README +139 -0
  5. data/ROADMAP +43 -0
  6. data/Rakefile +409 -0
  7. data/doc/extended_readme.rdoc +49 -0
  8. data/doc/getting_started.rdoc +47 -0
  9. data/doc/macosx_install.rdoc +70 -0
  10. data/doc/windows_install.rdoc +127 -0
  11. data/ext/rubygame/MANIFEST +25 -0
  12. data/ext/rubygame/rubygame_core.so +0 -0
  13. data/ext/rubygame/rubygame_event.c +644 -0
  14. data/ext/rubygame/rubygame_event.h +48 -0
  15. data/ext/rubygame/rubygame_event.obj +0 -0
  16. data/ext/rubygame/rubygame_gfx.c +942 -0
  17. data/ext/rubygame/rubygame_gfx.h +101 -0
  18. data/ext/rubygame/rubygame_gfx.obj +0 -0
  19. data/ext/rubygame/rubygame_gfx.so +0 -0
  20. data/ext/rubygame/rubygame_gl.c +154 -0
  21. data/ext/rubygame/rubygame_gl.h +32 -0
  22. data/ext/rubygame/rubygame_gl.obj +0 -0
  23. data/ext/rubygame/rubygame_image.c +108 -0
  24. data/ext/rubygame/rubygame_image.h +41 -0
  25. data/ext/rubygame/rubygame_image.obj +0 -0
  26. data/ext/rubygame/rubygame_image.so +0 -0
  27. data/ext/rubygame/rubygame_joystick.c +247 -0
  28. data/ext/rubygame/rubygame_joystick.h +41 -0
  29. data/ext/rubygame/rubygame_joystick.obj +0 -0
  30. data/ext/rubygame/rubygame_main.c +155 -0
  31. data/ext/rubygame/rubygame_main.h +33 -0
  32. data/ext/rubygame/rubygame_main.obj +0 -0
  33. data/ext/rubygame/rubygame_mixer.c +764 -0
  34. data/ext/rubygame/rubygame_mixer.h +62 -0
  35. data/ext/rubygame/rubygame_mixer.obj +0 -0
  36. data/ext/rubygame/rubygame_mixer.so +0 -0
  37. data/ext/rubygame/rubygame_screen.c +448 -0
  38. data/ext/rubygame/rubygame_screen.h +43 -0
  39. data/ext/rubygame/rubygame_screen.obj +0 -0
  40. data/ext/rubygame/rubygame_shared.c +209 -0
  41. data/ext/rubygame/rubygame_shared.h +60 -0
  42. data/ext/rubygame/rubygame_shared.obj +0 -0
  43. data/ext/rubygame/rubygame_surface.c +1147 -0
  44. data/ext/rubygame/rubygame_surface.h +62 -0
  45. data/ext/rubygame/rubygame_surface.obj +0 -0
  46. data/ext/rubygame/rubygame_time.c +183 -0
  47. data/ext/rubygame/rubygame_time.h +32 -0
  48. data/ext/rubygame/rubygame_time.obj +0 -0
  49. data/ext/rubygame/rubygame_ttf.c +599 -0
  50. data/ext/rubygame/rubygame_ttf.h +69 -0
  51. data/ext/rubygame/rubygame_ttf.obj +0 -0
  52. data/ext/rubygame/rubygame_ttf.so +0 -0
  53. data/lib/rubygame.rb +41 -0
  54. data/lib/rubygame/MANIFEST +12 -0
  55. data/lib/rubygame/clock.rb +128 -0
  56. data/lib/rubygame/color.rb +79 -0
  57. data/lib/rubygame/color/models/base.rb +106 -0
  58. data/lib/rubygame/color/models/hsl.rb +153 -0
  59. data/lib/rubygame/color/models/hsv.rb +149 -0
  60. data/lib/rubygame/color/models/rgb.rb +78 -0
  61. data/lib/rubygame/color/palettes/css.rb +49 -0
  62. data/lib/rubygame/color/palettes/palette.rb +100 -0
  63. data/lib/rubygame/color/palettes/x11.rb +177 -0
  64. data/lib/rubygame/constants.rb +238 -0
  65. data/lib/rubygame/event.rb +313 -0
  66. data/lib/rubygame/ftor.rb +370 -0
  67. data/lib/rubygame/hotspot.rb +265 -0
  68. data/lib/rubygame/keyconstants.rb +237 -0
  69. data/lib/rubygame/mediabag.rb +94 -0
  70. data/lib/rubygame/queue.rb +288 -0
  71. data/lib/rubygame/rect.rb +612 -0
  72. data/lib/rubygame/sfont.rb +223 -0
  73. data/lib/rubygame/sprite.rb +511 -0
  74. data/samples/FreeSans.ttf +0 -0
  75. data/samples/GPL.txt +340 -0
  76. data/samples/README +40 -0
  77. data/samples/chimp.bmp +0 -0
  78. data/samples/chimp.rb +313 -0
  79. data/samples/demo_gl.rb +151 -0
  80. data/samples/demo_gl_tex.rb +197 -0
  81. data/samples/demo_music.rb +75 -0
  82. data/samples/demo_rubygame.rb +284 -0
  83. data/samples/demo_sfont.rb +52 -0
  84. data/samples/demo_ttf.rb +193 -0
  85. data/samples/demo_utf8.rb +53 -0
  86. data/samples/fist.bmp +0 -0
  87. data/samples/load_and_blit.rb +22 -0
  88. data/samples/panda.png +0 -0
  89. data/samples/punch.wav +0 -0
  90. data/samples/ruby.png +0 -0
  91. data/samples/song.ogg +0 -0
  92. data/samples/term16.png +0 -0
  93. data/samples/whiff.wav +0 -0
  94. metadata +152 -0
@@ -0,0 +1,223 @@
1
+ #--
2
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
+ # Copyright (C) 2004-2007 John Croisant
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #++
19
+
20
+ module Rubygame
21
+
22
+ # *NOTE*: you must require 'rubygame/sfont' manually to gain access to
23
+ # Rubygame::SFont. It is not imported with Rubygame by default!
24
+ #
25
+ # SFont is a type of bitmapped font, which is loaded from an image file
26
+ # with a meaningful top row of pixels, and the font itself below that. The
27
+ # top row provides information about what parts of of the lower area
28
+ # contains font data, and which parts are empty.
29
+ #
30
+ # The image file should contain all of the glyphs on one row, with the
31
+ # colorkey color at the bottom-left pixel and the "skip" color at the
32
+ # top-left pixel.
33
+ #
34
+ # The colorkey color is applied to the image file when it is loaded, so
35
+ # that all pixels of that color appear transparent. Alternatively, if the
36
+ # alpha value of pixel [0,0] is 0 (that is, if it is fully transparent),
37
+ # the image file is assumed to have a proper alpha channel, and no colorkey
38
+ # is applied. The skip color is used in the top row of pixels to indicate
39
+ # that all the pixels below it contain empty space, and that the next
40
+ # pixel that is not the skip color marks the beginning of the next glyph.
41
+ #
42
+ # The official SFont homepage, with information on SFont and sample font
43
+ # files, can be found at http://www.linux-games.com/sfont/. More
44
+ # information on SFont, and a useful utility for automatically generating
45
+ # the top row for a font, can be found at: http://www.nostatic.org/sfont/
46
+
47
+ class SFont
48
+ @@default_glyphs = [\
49
+ "!",'"',"#","$","%","&","'","(",")","*","+",",","-",".","/","0",
50
+ "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@",
51
+ "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
52
+ "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_","`",
53
+ "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
54
+ "q","r","s","t","u","v","w","x","y","z","{","|","}","~"]
55
+
56
+ # Returns an array of strings, each string a single ASCII character
57
+ # from ! (33) to ~ (126). This is the default set of characters in a
58
+ # SFont. The full set is as follows:
59
+ #
60
+ # ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @
61
+ # A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ `
62
+ # a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
63
+ def SFont.default_glyphs
64
+ @@default_glyphs
65
+ end
66
+
67
+ # Create a now SFont instance.
68
+ #
69
+ # This function takes these arguments:
70
+ # filename:: the name of the image file from which the font should be
71
+ # loaded. Or, a Surface with a font image on it. The
72
+ # Surface will be copied onto separate Surfaces for each
73
+ # glyph, so the original Surface can be recycled.
74
+ # glyphs:: array of strings, one for each glyph, in the order they
75
+ # are found in the image file. If glyphs is not provided,
76
+ # or is nil, it is assumed to be the normal SFont order;
77
+ # that is, ASCII characters ! (33) to ~ (126). See
78
+ # SFont.default_glyphs for a full list.
79
+ # spacew:: represents the width of a space character ( ). You can
80
+ # either specify the width in pixels, or specify a
81
+ # character whose width, as found in the image, should be
82
+ # used. Alternatively, you could add a space character to
83
+ # the list of glyphs, and to the image file. If +spacew+
84
+ # is not given or is nil, and the space character is not
85
+ # in the list of glyphs, it will have the same width as
86
+ # the double-quote character (").
87
+ def initialize(filename,glyphs=nil,spacew=nil)
88
+ # load the surface containing all the glyphs
89
+ surface = nil
90
+ if filename.is_a? String
91
+ surface = Surface.load_image(filename)
92
+ elsif filename.is_a? Surface
93
+ surface = filename
94
+ end
95
+ @height = surface.height
96
+ colorkey = surface.get_at(0,@height-1)
97
+
98
+ # set colorkey if "transparent" color is not actually transparent
99
+ if colorkey[3] != 0
100
+ surface.set_colorkey(colorkey[0..2])
101
+ end
102
+
103
+ @glyphs = {}
104
+ @skip = surface.get_at(0,0)[0..2]
105
+
106
+ # split the glyphs into separate surfaces
107
+ glyphs = (glyphs or @@default_glyphs)
108
+ start_x = 2
109
+ glyphs.each{ |glyph| start_x = load_glyph(surface,glyph,start_x) }
110
+
111
+ if not glyphs.include?(" ")
112
+ if spacew == nil
113
+ spacew = @glyphs['"'].width
114
+ elsif spacew.kind_of? Numeric
115
+ spacew = spacew.to_i
116
+ elsif spacew.kind_of? String
117
+ if glyphs.include? spacew
118
+ spacew = @glyphs[spacew].width
119
+ else
120
+ spacew = @glyphs['"'].width
121
+ end
122
+ else
123
+ raise(ArgumentError,"spacew must be Numeric, String, \
124
+ or nil (got %s)"%[spacew.class])
125
+ end
126
+ @glyphs[" "] = Surface.new([spacew,@height])
127
+ end
128
+ end
129
+
130
+ # Return the height of the font, in pixels. This is the same as the
131
+ # height of the image file (all glyphs are the same height).
132
+ attr_reader :height
133
+
134
+ # This is a private function which is used to parse the font image.
135
+ #
136
+ # Create a Surface for a single glyph, and store it as a value in the
137
+ # +@glyphs+ hash, indexed by the glyph (string) it represents.
138
+ #
139
+ # Starting at a pixel in the "skip" region to the left of the glyph.
140
+ # Scans to the right along the top row until it finds a non-skip pixel
141
+ # (this is where the glyph starts) and then scans until it finds a skip
142
+ # pixel (this is where the glyph ends.
143
+ #
144
+ # Returns the x value it stops at, plus 1. This should be fed back in
145
+ # for the +start_x+ of the next glyph.
146
+ #
147
+ # This _private_ method takes these arguments:
148
+ # surface:: the Surface containing all glyph image data.
149
+ # glyph:: a string containing the current glyph
150
+ # start_x:: the x position to start scanning at.
151
+ def load_glyph(surface,glyph,start_x) # :doc:
152
+ # find where this glyph starts
153
+ begin
154
+ while(surface.get_at(start_x,0)[0..2] == @skip)
155
+ start_x += 1
156
+ end
157
+ rescue IndexError
158
+ return -1
159
+ end
160
+
161
+ end_x = start_x
162
+
163
+ # find how wide this glyph is
164
+ begin
165
+ while(surface.get_at(end_x,0)[0..2] != @skip)
166
+ end_x += 1
167
+ end
168
+ rescue IndexError
169
+ return -1
170
+ end
171
+
172
+ # make a new surface for the glyph and blit the image onto it
173
+ rect = Rect.new(start_x, 0, end_x-start_x, surface.h)
174
+ @glyphs[glyph] = Surface.new(rect.size)
175
+ surface.blit(@glyphs[glyph],[0,0],rect)
176
+
177
+ return end_x+1
178
+ end
179
+ private :load_glyph
180
+
181
+ # This is a private function which is used to render a string.
182
+ #
183
+ # Blit a single glyph to a Surface at the given position.
184
+ #
185
+ # This _private_ method takes these arguments:
186
+ # glyph:: a string containing the glyph to blit.
187
+ # surface:: the target surface to blit onto.
188
+ # pos:: an Array of the x and y values to blit the glyph to.
189
+ def blit_glyph(glyph,surface,pos) # :doc:
190
+ @glyphs[glyph].blit(surface,pos)
191
+ end
192
+ private :blit_glyph
193
+
194
+ # Pretends to render the given string, and returns the width in pixels
195
+ # of the surface that would be created if it were rendered using
196
+ # SFont#render. If you want the height too, you can get it with
197
+ # SFont#height (the height is contant).
198
+ #
199
+ # This method takes this argument:
200
+ # string:: the string to pretend to render.
201
+ def string_width(string)
202
+ w = 0
203
+ string.each_byte { |glyph| w += @glyphs["%c"%[glyph]].width }
204
+ return w
205
+ end
206
+
207
+ # Renders the given string to a Surface, and returns that surface.
208
+ #
209
+ # This method takes this argument:
210
+ # string:: the text string to render.
211
+ def render(string)
212
+ size = [self.string_width(string),self.height]
213
+ render = Surface.new(size)
214
+ x = 0
215
+ string.each_byte { |glyph|
216
+ blit_glyph("%c"%[glyph],render,[x,0])
217
+ x += @glyphs["%c"%[glyph]].width
218
+ }
219
+ return render
220
+ end
221
+ end # class SFont
222
+
223
+ end # module Rubygame
@@ -0,0 +1,511 @@
1
+ #--
2
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
+ # Copyright (C) 2004-2007 John Croisant
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #++
19
+
20
+ module Rubygame
21
+
22
+ # The Sprites module provides classes and mix-in modules for managing
23
+ # sprites. A Sprite is, fundamentally, a Surface with an associated
24
+ # Rect which defines the position of that Surface on the Screen.
25
+ # Additionally, sprites can have some behavior associated with them
26
+ # via the Sprite#update method, including movement and collision detection;
27
+ # because of this, sprites can be the foundation of most on-screen objects,
28
+ # such as characters, items, missiles, and even user-interface elements.
29
+ #
30
+ # There are several classes/modules under the Sprites module:
31
+ # Sprite:: mix-in module to turn an individual object or class into
32
+ # a Sprite.
33
+ # Group:: class for containing and manipulating many Sprite objects.
34
+ # UpdateGroup:: mix-in module for Group to allow undrawing/redrawing of
35
+ # member Sprite objects.
36
+ # LimitGroup:: mix-in module for Group to limit the number of Sprite objects
37
+ # the Group can hold at once.
38
+ #
39
+ # More mix-in modules to extend the functionality of Group or Sprite are
40
+ # planned for the future. Do not hesitate to change either (or both) Group
41
+ # or Sprite within your application to fit your needs! That's what they are
42
+ # here for!
43
+ #
44
+ module Sprites
45
+
46
+ # The Sprite mix-in module (not to be confused with its parent module,
47
+ # Sprites) can be used to extend a class or object to behave as a
48
+ # sprite. Specifically, a sprite can:
49
+ # - #draw (blit) itself onto a Surface in its proper position
50
+ # - detect bounding-box collision with Groups (#collide_group) and/or other
51
+ # sprites (#collide_sprite).
52
+ # - #update its own state based on arbitrary rules.
53
+ #
54
+ # A Sprite is, fundamentally, a Surface with an associated
55
+ # Rect which defines the position of that Surface on the Screen.
56
+ # Additionally, sprites can have some behavior associated with them
57
+ # via the #update method, including movement and collision detection;
58
+ # because of this, sprites can be the foundation of most on-screen objects,
59
+ # such as characters, items, missiles, and even user-interface elements.
60
+ #
61
+ # In order to work properly as a Sprite, the extended object or class must
62
+ # have two methods defined (by default, these are defined as accessors to
63
+ # attributes of the same names):
64
+ # image:: return a Surface with the sprite's image.
65
+ # rect:: returns a Rect with the position and dimensions of the sprite.
66
+ #
67
+ # Normally, the value returned by rect is used to draw the sprite
68
+ # onto a Surface as well as to detect collision between sprites. However,
69
+ # if @col_rect will be used for collision detection instead, if it is
70
+ # defined. See also #col_rect.
71
+ #
72
+ # Additionally, if you are extending an already-existing instance (rather
73
+ # than a class), that instance must have an attribute @groups, which is
74
+ # an Array containing all Groups to which the sprite belongs.
75
+ #
76
+ #
77
+ module Sprite
78
+ attr_reader :groups
79
+ attr_accessor :image, :rect, :depth
80
+
81
+ # Initialize the Sprite, defining @groups and @depth.
82
+ def initialize
83
+ @groups = []
84
+ @depth = 0
85
+ end
86
+
87
+ # Add the Sprite to each given Group.
88
+ def add(*groups)
89
+ groups.each { |group|
90
+ unless @groups.include? group
91
+ @groups.push(group)
92
+ group.push(self)
93
+ end
94
+ }
95
+ end
96
+
97
+ # call-seq: alive? -> true or false
98
+ #
99
+ # True if the Sprite belongs to at least one Group.
100
+ def alive?
101
+ return @groups.length > 0
102
+ end
103
+
104
+ # Set an alternative Rect to use for collision detection. If undefined or
105
+ # set to nil, the Sprite's #rect is used instead.
106
+ attr_writer :col_rect
107
+
108
+ # call-seq: col_rect -> Rect
109
+ #
110
+ # Returns @col_rect if it is defined, otherwise calls #rect.
111
+ # This method is used by #collide, #collide_group, and #collide_sprite
112
+ # to get the bounding box for collision detection.
113
+ def col_rect
114
+ if defined? @col_rect
115
+ return (@col_rect or rect)
116
+ else
117
+ return rect
118
+ end
119
+ end
120
+
121
+ # call-seq: collide(other) -> Array
122
+ #
123
+ # Check collision between the caller and a Group or another Sprite. See
124
+ # also #collide_group and #collide_sprite. This method uses the value
125
+ # of #col_rect as the bounding box when detecting collision.
126
+ #
127
+ # If +other+ is a Group, returns an Array of every Sprite in that
128
+ # Group that collides with the caller. If +other+ is a Sprite, returns
129
+ # an Array containing +other+ if it collides with the caller. Otherwise,
130
+ # returns an empty Array.
131
+ def collide(other)
132
+ if other.class.is_a? Group
133
+ return collide_group(other)
134
+ elsif collide_sprite?(other)
135
+ return [other]
136
+ else
137
+ return []
138
+ end
139
+ end
140
+
141
+ # call-seq: collide_group(group) -> Array
142
+ #
143
+ # Check collision between the caller and all members of a Group. This
144
+ # method uses the value of #col_rect as the bounding box when detecting
145
+ # collision.
146
+ #
147
+ # Returns an Array of every Sprite in +group+ that collides with the
148
+ # caller. If none collide, returns an empty Array.
149
+ def collide_group(group)
150
+ sprites = []
151
+ group.each { |sprite|
152
+ if self.collide_sprite?(sprite) and (not sprites.include?(sprite))
153
+ sprites.push(sprite)
154
+ end
155
+ }
156
+ return sprites
157
+ end
158
+
159
+ # call-seq: collide_sprite?(sprite) -> true or false or nil
160
+ #
161
+ # Check collision between the caller and another Sprite. This method uses
162
+ # the value of #col_rect as the bounding box when detecting collision.
163
+ # Returns true if +sprite+ collides with the caller, false if they do
164
+ # not, or nil if +sprite+ does not respond to either :col_rect or :rect
165
+ # (meaning it was not possible to detect collision).
166
+ def collide_sprite?(sprite)
167
+ if sprite.respond_to?(:col_rect)
168
+ return self.col_rect.collide_rect?(sprite.col_rect)
169
+ elsif sprite.respond_to?(:rect)
170
+ return self.col_rect.collide_rect?(sprite.rect)
171
+ else
172
+ return nil
173
+ end
174
+ end
175
+
176
+ # call-seq: draw(surface) -> Rect
177
+ #
178
+ # Blit the Surface returned by #image onto +surface+ at the position
179
+ # given by the Rect returned by #rect. Returns a Rect representing
180
+ # the area of +surface+ which was affected by the draw.
181
+ def draw(destination)
182
+ self.image.blit(destination, self.rect)
183
+ end
184
+
185
+ # Remove the caller from every Group that it belongs to.
186
+ def kill
187
+ @groups.each { |group| group.delete(self) }
188
+ @groups = []
189
+ end
190
+
191
+ # Remove the caller from each given Group.
192
+ def remove(*groups)
193
+ groups.each { |group|
194
+ if @groups.include? group
195
+ @groups.delete(group)
196
+ group.delete(self)
197
+ end
198
+ }
199
+ end
200
+
201
+
202
+ # call-seq: undraw(surface, background) -> Rect
203
+ #
204
+ # 'Erase' the sprite from +surface+ by drawing over it with part of
205
+ # +background+. For best results, +background+ should be the same size
206
+ # as +surface+.
207
+ #
208
+ # Returns a Rect representing the area of +surface+ which was affected.
209
+ #
210
+ def undraw(dest, background)
211
+ background.blit(dest, @rect, @rect)
212
+ end
213
+
214
+ # This method is meant to be overwritten by Sprite-based classes to
215
+ # define meaningful behavior. It can take any number of arguments you
216
+ # want, be called however often you want, and do whatever you want.
217
+ # It may return something, but Group#update will not (by default) use,
218
+ # or even collect, return values from this method.
219
+ #
220
+ # An example definition might take the amount of time that has passed
221
+ # since the last update; the Sprite could then update its position
222
+ # accordingly. Game logic, collision detection, and animation would also
223
+ # fit in here, if appropriate to your class.
224
+ def update( *args )
225
+ super
226
+ end
227
+
228
+ end # module Sprite
229
+
230
+ # The Group class is a special container, based on Array, with supplemental
231
+ # methods for handling multiple Sprite objects. Group can draw, update,
232
+ # and check collision for all its member sprites with one call.
233
+ #
234
+ # All members of a Group must be unique (duplicates will be refused), and
235
+ # should be a Sprite (or functionally equivalent).
236
+ class Group < Array
237
+
238
+ # Add +sprite+ to the Group. +sprite+ is notified so that it can
239
+ # add this Group to its list of parent Groups. See also #push.
240
+ def <<(sprite)
241
+ unless self.include? sprite
242
+ super(sprite)
243
+ sprite.add(self)
244
+ end
245
+ return self
246
+ end
247
+
248
+ # Call the method represented by +symbol+ for every member sprite,
249
+ # giving +args+ as the arguments to the method. This method uses
250
+ # Object#send, which does not hesitate to call private methods, so
251
+ # use this wisely! See also #draw and #update.
252
+ def call(symbol,*args)
253
+ self.each { |sprite|
254
+ sprite.send(symbol,*args)
255
+ }
256
+ end
257
+
258
+ # Remove every member sprite from the Group. Each sprite is notified
259
+ # so that it can remove this Group from its list of parent Groups.
260
+ # See also #delete.
261
+ def clear
262
+ self.dup.each { |sprite| sprite.remove(self) }
263
+ end
264
+
265
+ # call-seq: collide_sprite(sprite) -> Array
266
+ #
267
+ # Check collision between each member of the Group and +sprite+. Returns
268
+ # an Array of all member sprites that collided with +sprite+. If none
269
+ # collided, returns an empty Array.
270
+ def collide_sprite(sprite)
271
+ sprite.collide_group(self)
272
+ end
273
+
274
+ # call-seq:
275
+ # collide_group(group, &block) -> Hash
276
+ # collide_group(group, killa=false, killb=false) -> Hash # deprecated
277
+ #
278
+ # Check collision between each member of the calling Group and each
279
+ # member of +group+. Returns a Hash table with each member of the calling
280
+ # Group as a key, and as a value an Array of all members of +group+ that
281
+ # it collided with.
282
+ #
283
+ # If a block is given, that block is executed for every pair of colliding
284
+ # sprites. For example, if a1 collides with b1 and b2, the block will
285
+ # be called twice: once with [ a1, b1 ] and once with [ a1, b2 ].
286
+ #
287
+ # Example:
288
+ #
289
+ # # 'kills' both sprites when they collide
290
+ #
291
+ # groupA,collide_group(groupB) do |a, b|
292
+ # a.kill
293
+ # b.kill
294
+ # end
295
+ #
296
+ # *NOTE*: +killa+ and +killb+ are deprecated and will be removed in the future.
297
+ # It is highly recommended that you use the block argument instead.
298
+ #
299
+ # *IMPORTANT*: +killa+ and +killb+ will be ignored if a block is given!
300
+ #
301
+ # If +killa+ is true and a sprite in group A collides with a sprite in group B,
302
+ # the sprite in group A will have its #kill method called; the same goes for
303
+ # +killb+ and group B.
304
+ #
305
+ def collide_group(group, killa=false, killb=false, &block)
306
+ sprites = {}
307
+ self.each { |sprite|
308
+ col = sprite.collide_group(group)
309
+ sprites[sprite] = col if col.length > 0
310
+ }
311
+
312
+ if block_given?
313
+ sprites.each_pair do |a, bs|
314
+ bs.each { |b| yield(a, b) }
315
+ end
316
+ else
317
+ # killa and killb only work if no block is given
318
+ if killa
319
+ sprites.each_key { |sprite| sprite.kill }
320
+ end
321
+ if killb
322
+ sprites.each_value do |array|
323
+ array.each { |sprite| sprite.kill }
324
+ end
325
+ end
326
+ end
327
+
328
+ return sprites
329
+ end
330
+
331
+ # Remove each sprite in +sprites+ from the Group. Each sprite is notified
332
+ # so that it can remove this Group from its list of parent Groups.
333
+ # Note that this will not work correctly if fed a list of its
334
+ # own sprites (use Array.dup if you want correct behavior)
335
+ def delete(*sprites)
336
+ sprites.each { |sprite|
337
+ if self.include? sprite
338
+ super(sprite)
339
+ sprite.remove(self)
340
+ end
341
+ }
342
+ return self
343
+ end
344
+
345
+ # Draw every sprite on Surface +dest+. Calls Sprite#draw for every member
346
+ # sprite, passing +dest+ as the argument. See also #call and #update.
347
+ def draw(dest)
348
+ self.each { |sprite| sprite.draw(dest) }
349
+ end
350
+
351
+ # Add each sprite in +sprites+ to the Group. Each sprite is notified so
352
+ # that it can add this Group to its list of parent Groups. See also #<<.
353
+ def push(*sprites)
354
+ sprites.each { |sprite|
355
+ self << sprite
356
+ }
357
+ return self
358
+ end
359
+
360
+ # Update every member sprite. Calls Sprite#update for every member
361
+ # sprite, passing on all arguments. See also #call and #draw.
362
+ def update(*args)
363
+ self.each { |sprite|
364
+ sprite.update(*args)
365
+ }
366
+ end
367
+
368
+ end #class Group
369
+
370
+ # UpdateGroup is a mix-in module that extends Group to allow it to "erase"
371
+ # (i.e. draw over with a background Surface) and re-draw each sprite in its
372
+ # new position. This eliminates the "trail" of images that sprites would
373
+ # otherwise leave as they move around.
374
+ #
375
+ # UpdateGroup adds a new attribute, @dirty_rects, which is an Array storing
376
+ # all Rects which need to be updated, including the old positions of the
377
+ # sprites. This attribute is returned when UpdateGroup#draw is called,
378
+ # so that it can be used to update the parts of the Screen that have
379
+ # changed.
380
+ #
381
+ # The general order of calls each frame should be:
382
+ # 1. #undraw; clear the old positions of the sprites.
383
+ # 2. #update; update the sprites to their new positions.
384
+ # 3. #draw; draw the sprites in their new positions.
385
+ #
386
+ # This module can extend either a class or an already-existing Group
387
+ # instance (either empty or with members) without any special preparation.
388
+ module UpdateGroup
389
+ attr_accessor :dirty_rects
390
+
391
+ # Defines @dirty_rects when an existing object is extended.
392
+ def UpdateGroup.extend_object(obj)
393
+ super
394
+ obj.dirty_rects = []
395
+ end
396
+
397
+ # Initialize the Group, calling +super+ and defining @dirty_rects.
398
+ def initialize
399
+ super
400
+ @dirty_rects = []
401
+ end
402
+
403
+ # call-seq: draw(dest) -> Array
404
+ #
405
+ # Draw every sprite on Surface +dest+. See Group#draw.
406
+ # Returns an Array of Rects representing the portions of +dest+ which
407
+ # were affected by the last #undraw and this #draw.
408
+ def draw(dest)
409
+ self.each { |sprite|
410
+ @dirty_rects.push( sprite.draw(dest) )
411
+ }
412
+ rects = @dirty_rects
413
+ @dirty_rects = []
414
+ return rects
415
+ end
416
+
417
+ # Draw over part of +dest+ with image data from the corresponding part
418
+ # of +background+. For best results, +background+ should be at least as
419
+ # big as +dest+ (or, rather, the part of +dest+ that will ever be drawn
420
+ # over).
421
+ def undraw(dest,background)
422
+ self.each { |sprite|
423
+ @dirty_rects.push( sprite.undraw(dest, background) )
424
+ }
425
+ end
426
+ end # module UpdateGroup
427
+
428
+
429
+ # LimitGroup is a mix-in module that extends Group to limit the number
430
+ # of sprites it can contain. If the limit has been reached, each new sprite
431
+ # will "push out" the oldest sprite, on a First-In-First-Out basis.
432
+ #
433
+ # The limit can be set either when the LimitGroup is created, or
434
+ # at any time during execution. However, if you reduce the limit,
435
+ # excess sprites will not be removed until the next time a sprite is
436
+ # added. (You can work around this by pushing the last sprite in the
437
+ # group again; the duplicate will be removed.)
438
+ #
439
+ # Please note that, because Group#push is defined in terms of Group#<<,
440
+ # both LimitGroup#<< and LimitGroup#push work properly, even though only
441
+ # LimitGroup#<< is defined.
442
+ module LimitGroup
443
+ attr_accessor :limit
444
+
445
+ # Defines and sets @limit = 1 when an existing object is extended.
446
+ def LimitGroup.extend_object(obj)
447
+ super
448
+ obj.limit = 1
449
+ end
450
+
451
+ # Initialize the LimitGroup and define @limit (as 1, by default).
452
+ def initialize(limit=1)
453
+ @limit = limit
454
+ end
455
+
456
+ # Add +sprite+ to the LimitGroup, removing the oldest member if
457
+ # necessary to keep under the limit. If +sprite+ is already in
458
+ # the LimitGroup, it will be moved to the top of the queue.
459
+ def <<(sprite)
460
+ if not include? sprite
461
+ super(sprite)
462
+ while length > @limit
463
+ self.slice!(0)
464
+ end
465
+ else # move sprite to the back of the queue
466
+ self.delete(sprite)
467
+ super(sprite)
468
+ end
469
+ end
470
+ end # module LimitGroup
471
+
472
+ # DepthSortGroup is a mix-in module that extends Group to sort its
473
+ # sprites by their @depth attribute, so that sprites with low depths
474
+ # will appear on top of sprites with higher depths. A sprite's depth
475
+ # can be any Numeric, or nil (which will be counted as 0).
476
+ #
477
+ # If two sprites have exactly the same depth, there is no guarantee about
478
+ # which one will be drawn on top of the other. (But, whichever one is on
479
+ # top will stay on top, at least until the group is re-sorted.)
480
+ #
481
+ # If a sprite's depth changes after it has been added to the group, you
482
+ # must use the #sort_sprites method for the change to have any effect.
483
+ #
484
+ module DepthSortGroup
485
+ # Add a single sprite to the group. For efficiency reasons, this
486
+ # method doesn't re-sort sprites afterwards. You should use #sort_sprites
487
+ # after you're done adding sprites. Or, better yet, just use #push.
488
+ def <<(sprite)
489
+ super
490
+ end
491
+
492
+ # Add multiple sprites to the group, then re-sort all sprites.
493
+ def push(*sprites)
494
+ sprites.each { |sprite|
495
+ self << sprite
496
+ }
497
+ sort_sprites()
498
+ return self
499
+ end
500
+
501
+ # Sort sprites by depth, in descending order, so that sprites with low depths
502
+ # will be drawn on top of sprites with high depths.
503
+ #
504
+ # If a sprite has a depth of nil, it is sorted as if its depth were 0 (zero).
505
+ def sort_sprites
506
+ self.sort! { |a,b| (b.depth or 0) <=> (a.depth or 0) }
507
+ end
508
+ end
509
+
510
+ end #module Sprite
511
+ end # module Rubygame