rubygame 2.2.0-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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