chingu 0.7.6.5 → 0.7.6.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{chingu}
8
- s.version = "0.7.6.5"
8
+ s.version = "0.7.6.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["ippa"]
12
- s.date = %q{2010-06-22}
12
+ s.date = %q{2010-06-24}
13
13
  s.description = %q{OpenGL accelerated 2D game framework for Ruby. Builds on Gosu (Ruby/C++) which provides all the core functionality. Chingu adds simple yet powerful game states, prettier input handling, deployment safe asset-handling, a basic re-usable game object and stackable game logic.}
14
14
  s.email = %q{ippa@rubylicio.us}
15
15
  s.extra_rdoc_files = [
@@ -33,7 +33,7 @@ require_all "#{CHINGU_ROOT}/chingu/traits"
33
33
  require_all "#{CHINGU_ROOT}/chingu"
34
34
 
35
35
  module Chingu
36
- VERSION = "0.7.6.5"
36
+ VERSION = "0.7.6.6"
37
37
 
38
38
  DEBUG_COLOR = Gosu::Color.new(0xFFFF0000)
39
39
  DEBUG_ZORDER = 9999
@@ -100,7 +100,6 @@ module Chingu
100
100
  # .. and finalize() is called on the game state we're switching _from_.
101
101
  #
102
102
  def switch_game_state(state, options = {})
103
- puts state.class
104
103
  options = {:setup => true, :finalize => true, :transitional => true}.merge(options)
105
104
 
106
105
  @previous_game_state = current_game_state
@@ -195,8 +194,8 @@ module Chingu
195
194
  #
196
195
  # Give the soon-to-be-disabled state a chance to clean up by calling finalize() on it.
197
196
  #
198
- @previous_game_state = current_game_state
199
197
  current_game_state.finalize if current_game_state.respond_to?(:finalize) && options[:finalize]
198
+ @previous_game_state = current_game_state
200
199
 
201
200
  #
202
201
  # Activate the game state "bellow" current one with a simple Array.pop
@@ -46,29 +46,38 @@ module Chingu
46
46
  class Edit < Chingu::GameState
47
47
  attr_accessor :grid, :debug, :file, :hud_color
48
48
  attr_reader :classes, :exclude
49
-
49
+
50
50
  def initialize(options = {})
51
51
  super
52
+
53
+ options = {:draw_grid => true, :snap_to_grid => true, :resize_to_grid => true}.merge(options)
54
+
52
55
  @grid = options[:grid] || [8,8]
56
+ @grid_color = options[:grid_color] || Color.new(0xaa222222)
57
+ @draw_grid = options[:draw_grid]
58
+ @snap_to_grid = options[:snap_to_grid] # todo
59
+ @resize_to_grid = options[:resize_to_grid] # todo
60
+
53
61
  @classes = Array(options[:classes] || game_object_classes)
54
62
  @except = options[:except] || []
55
- @classes -= Array(@except)
63
+ @classes -= Array(@except)
56
64
  @debug = options[:debug]
57
65
  @zorder = 10000
58
-
66
+
59
67
  p @classes if @debug
60
-
61
- @hud_color = Gosu::Color.new(180,70,70,70)
62
- @selected_game_object = nil
63
- self.input = {
64
- :left_mouse_button => :left_mouse_button,
68
+
69
+ @hud_color = Gosu::Color.new(200,70,70,70)
70
+ @selected_game_object = nil
71
+ self.input = {
72
+ :left_mouse_button => :left_mouse_button,
65
73
  :released_left_mouse_button => :released_left_mouse_button,
66
74
  :right_mouse_button => :right_mouse_button,
67
- :released_right_mouse_button => :released_right_mouse_button,
68
-
69
- :delete => :destroy_selected_game_objects,
75
+ :released_right_mouse_button => :released_right_mouse_button,
76
+
77
+ :delete => :destroy_selected_game_objects,
78
+ :d => :destroy_selected_game_objects,
70
79
  :backspace => :reset_selected_game_objects,
71
-
80
+
72
81
  :holding_numpad_7 => :scale_down,
73
82
  :holding_numpad_9 => :scale_up,
74
83
  :holding_numpad_4 => :tilt_left,
@@ -87,42 +96,43 @@ module Chingu
87
96
 
88
97
  :page_up => :inc_zorder,
89
98
  :page_down => :dec_zorder,
90
-
99
+
91
100
  :s => :try_save,
92
101
  :a => :try_select_all,
93
-
102
+
94
103
  :e => :save_and_quit,
95
-
104
+
96
105
  :esc => :esc,
97
106
  :q => :quit,
98
-
107
+
99
108
  :up_arrow => :move_up,
100
109
  :down_arrow => :move_down,
101
110
  :left_arrow => :move_left,
102
111
  :right_arrow => :move_right,
103
-
112
+
104
113
  :holding_up_arrow => :try_scroll_up,
105
114
  :holding_down_arrow => :try_scroll_down,
106
115
  :holding_left_arrow => :try_scroll_left,
107
116
  :holding_right_arrow => :try_scroll_right,
108
-
117
+
109
118
  :plus => :scale_up,
110
119
  :minus => :scale_down,
111
120
  :mouse_wheel_up => :mouse_wheel_up,
112
121
  :mouse_wheel_down => :mouse_wheel_down,
113
-
122
+
114
123
  :"1" => :create_object_1,
115
124
  :"2" => :create_object_2,
116
125
  :"3" => :create_object_3,
117
126
  :"4" => :create_object_4,
118
127
  :"5" => :create_object_5,
119
128
  }
120
-
129
+
130
+ @hud_height = 140
121
131
  x = 20
122
132
  y = 60
123
133
  @classes.each do |klass|
124
134
  puts "Creating a #{klass}" if @debug
125
-
135
+
126
136
  # We initialize x,y,zorder,rotation_center after creation
127
137
  # so they're not overwritten by the class initialize/setup or simular
128
138
  begin
@@ -131,12 +141,13 @@ module Chingu
131
141
  game_object.y = y
132
142
  game_object.zorder = @zorder
133
143
  game_object.options[:toolbar] = true
134
-
144
+
135
145
  # Scale down object to fit our toolbar
136
146
  if game_object.image
147
+ Text.create("#{klass}\n#{game_object.width.to_i}x#{game_object.height.to_i}", :size => 13, :x=>x-16, :y=>y+18, :zorder => @zorder)
137
148
  game_object.size = [32,32]
138
149
  game_object.cache_bounding_box if game_object.respond_to?(:cache_bounding_box)
139
- x += 40
150
+ x += 60
140
151
  else
141
152
  puts "Skipping #{klass} - no image" if @debug
142
153
  game_object.destroy
@@ -147,6 +158,23 @@ module Chingu
147
158
  end
148
159
  end
149
160
 
161
+ def draw_grid
162
+ return unless @grid
163
+
164
+ start_x = start_y = 0,0
165
+ if defined?(previous_game_state.viewport)
166
+ start_x = -previous_game_state.viewport.x % @grid.first
167
+ start_y = -previous_game_state.viewport.y % @grid.last
168
+ end
169
+ (start_x .. $window.width).step(@grid.first).each do |x|
170
+ $window.draw_line(x, 1, @grid_color, x, $window.height, @grid_color, @zorder-10, :additive)
171
+ end
172
+ (start_y .. $window.height).step(@grid.last).each do |y|
173
+ $window.draw_line(1, y, @grid_color, $window.width, y, @grid_color, @zorder-10, :additive)
174
+ end
175
+
176
+ end
177
+
150
178
  #
151
179
  # SETUP
152
180
  #
@@ -157,6 +185,12 @@ module Chingu
157
185
  @title.text += " - Grid: #{@grid}" if @grid
158
186
  @text = Text.create("", :x => 300, :y => 20, :factor => 1, :size => 16, :zorder => @zorder)
159
187
  @status_text = Text.create("-", :x => 5, :y => 20, :factor => 1, :size => 16, :zorder => @zorder)
188
+
189
+ if defined?(previous_game_state.viewport)
190
+ @game_area_backup = previous_game_state.viewport.game_area.dup
191
+ previous_game_state.viewport.game_area.x -= @hud_height
192
+ previous_game_state.viewport.game_area.y -= @hud_height
193
+ end
160
194
  end
161
195
 
162
196
  #
@@ -184,8 +218,11 @@ module Chingu
184
218
  selected_game_objects.each do |selected_game_object|
185
219
  selected_game_object.x = self.mouse_x + selected_game_object.options[:mouse_x_offset]
186
220
  selected_game_object.y = self.mouse_y + selected_game_object.options[:mouse_y_offset]
187
- selected_game_object.x -= selected_game_object.x % @grid[0]
188
- selected_game_object.y -= selected_game_object.y % @grid[1]
221
+
222
+ if @snap_to_grid
223
+ selected_game_object.x -= selected_game_object.x % @grid[0]
224
+ selected_game_object.y -= selected_game_object.y % @grid[1]
225
+ end
189
226
  end
190
227
 
191
228
  # TODO: better cleaner sollution
@@ -218,13 +255,15 @@ module Chingu
218
255
 
219
256
  super
220
257
 
258
+ draw_grid if @draw_grid
259
+
221
260
  #
222
261
  # Draw an edit HUD
223
262
  #
224
263
  $window.draw_quad( 0,0,@hud_color,
225
264
  $window.width,0,@hud_color,
226
- $window.width,100,@hud_color,
227
- 0,100,@hud_color, @zorder-1)
265
+ $window.width,@hud_height,@hud_color,
266
+ 0,@hud_height,@hud_color, @zorder-1)
228
267
 
229
268
  #
230
269
  # Draw red rectangles/circles around all selected game objects
@@ -423,6 +462,12 @@ module Chingu
423
462
  quit
424
463
  end
425
464
 
465
+ def finalize
466
+ if defined?(previous_game_state.viewport)
467
+ previous_game_state.viewport.game_area = @game_area_backup
468
+ end
469
+ end
470
+
426
471
  def move_left
427
472
  scroll_left && return if selected_game_objects.empty?
428
473
  selected_game_objects.each { |game_object| game_object.x -= 1 }
@@ -36,7 +36,7 @@ module Chingu
36
36
  class FadeTo < Chingu::GameState
37
37
 
38
38
  def initialize(new_game_state, options = {})
39
- @options = {:speed => 3}.merge(options)
39
+ @options = {:speed => 3, :zorder => 999999}.merge(options)
40
40
 
41
41
  @new_game_state = new_game_state
42
42
  @new_game_state = new_game_state.new if new_game_state.is_a? Class
@@ -81,7 +81,7 @@ module Chingu
81
81
  $window.draw_quad( 0,0,@color,
82
82
  $window.width,0,@color,
83
83
  $window.width,$window.height,@color,
84
- 0,$window.height,@color,999)
84
+ 0,$window.height,@color,@options[:zorder])
85
85
  end
86
86
 
87
87
  if @fading_in && @alpha == 0
@@ -1,612 +1,629 @@
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
- #--
21
- # Table of Contents:
22
- #
23
- # class Rect
24
- # GENERAL:
25
- # initialize
26
- # new_from_object
27
- # to_s
28
- # to_a, to_ary
29
- # []
30
- # ATTRIBUTES:
31
- # x, y, w, h [<- accessors]
32
- # width, height, size
33
- # left, top, right, bottom
34
- # center, centerx, centery
35
- # topleft, topright
36
- # bottomleft, bottomright
37
- # midleft, midtop, midright, midbottom
38
- # UTILITY METHODS:
39
- # clamp, clamp!
40
- # clip, clip!
41
- # collide_hash, collide_hash_all
42
- # collide_array, collide_array_all
43
- # collide_point?
44
- # collide_rect?
45
- # contain?
46
- # inflate, inflate!
47
- # move, move!
48
- # normalize, normalize!
49
- # union, union!
50
- # union_all, union_all!
51
- #
52
- # class Surface
53
- # make_rect
54
- #
55
- #++
56
-
57
- module Chingu
58
-
59
- # A Rect is a representation of a rectangle, with four core attributes
60
- # (x offset, y offset, width, and height) and a variety of functions
61
- # for manipulating and accessing these attributes.
62
- #
63
- # Like all coordinates in Rubygame (and its base library, SDL), x and y
64
- # offsets are measured from the top-left corner of the screen, with greater
65
- # y offsets being lower. Thus, specifying the x and y offsets of the Rect
66
- # is equivalent to setting the location of its top-left corner.
67
- #
68
- # In Rubygame, Rects are used for collision detection and describing
69
- # the area of a Surface to operate on.
70
- class Rect < Array
71
-
72
- #--
73
- # GENERAL
74
- #++
75
-
76
- # Create a new Rect, attempting to extract its own information from
77
- # the given arguments. The arguments must fall into one of these cases:
78
- #
79
- # - 4 integers +(x, y, w, h)+.
80
- # - 1 Rect or Array containing 4 integers +([x, y, w, h])+.
81
- # - 2 Arrays containing 2 integers each +([x,y], [w,h])+.
82
- # - 1 object with a +rect+ attribute which is a valid Rect object.
83
- #
84
- # All rect core attributes (x,y,w,h) must be integers.
85
- #
86
- def initialize(*argv)
87
- case argv.length
88
- when 1
89
- if argv[0].kind_of? Array; super(argv[0])
90
- elsif argv[0].respond_to? :rect; super(argv[0])
91
- end
92
- when 2
93
- super(argv[0].concat(argv[1]))
94
- when 4
95
- super(argv)
96
- end
97
- return self
98
- end
99
-
100
- # Extract or generate a Rect from the given object, if possible, using the
101
- # following process:
102
- #
103
- # 1. If it's a Rect already, return a duplicate Rect.
104
- # 2. Elsif it's an Array with at least 4 values, make a Rect from it.
105
- # 3. Elsif it has a +rect+ attribute., perform (1) and (2) on that.
106
- # 4. Otherwise, raise TypeError.
107
- #
108
- # See also Surface#make_rect()
109
- def Rect.new_from_object(object)
110
- case(object)
111
- when Rect
112
- return object.dup
113
- when Array
114
- if object.length >= 4
115
- return Rect.new(object)
116
- else
117
- raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.length )
118
- end
119
- else
120
- begin
121
- case(object.rect)
122
- when Rect
123
- return object.rect.dup
124
- when Array
125
- if object.rect.length >= 4
126
- return Rect.new(object.rect)
127
- else
128
- raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.rect.length )
129
- end
130
- end # case object.rect
131
- rescue NoMethodError # if no rect.rect
132
- raise(TypeError,"Object must be a Rect or Array [x,y,w,h], or have an attribute called 'rect'. (Got %s instance.)"%object.class)
133
- end
134
- end # case object
135
- end
136
-
137
-
138
- # Print the Rect in the form "+#<Rect [x,y,w,h]>+"
139
- def to_s; "#<Rect [%s,%s,%s,%s]>"%self; end
140
-
141
- # Print the Rect in the form "+#<Rect:id [x,y,w,h]>+"
142
- def inspect; "#<Rect:#{self.object_id} [%s,%s,%s,%s]>"%self; end
143
-
144
- #--
145
- # ATTRIBUTES
146
- #++
147
-
148
- # Returns self.at(0)
149
- def x; return self.at(0); end
150
- # Sets self[0] to +val+
151
- def x=(val); self[0] = val; end
152
-
153
- alias left x
154
- alias left= x=;
155
- alias l x
156
- alias l= x=;
157
-
158
- # Returns self.at(1)
159
- def y; return self.at(1); end
160
- # Sets self[1] to +val+
161
- def y=(val); self[1] = val; end
162
-
163
- alias top y
164
- alias top= y=;
165
- alias t y
166
- alias t= y=;
167
-
168
- # Returns self.at(2)
169
- def w; return self.at(2); end
170
- # Sets self[2] to +val+
171
- def w=(val); self[2] = val; end
172
-
173
- alias width w
174
- alias width= w=;
175
-
176
- # Returns self.at(3)
177
- def h; return self.at(3); end
178
- # Sets self[3] to +val+
179
- def h=(val); self[3] = val; end
180
-
181
- alias height h
182
- alias height= h=;
183
-
184
- # Return the width and height of the Rect.
185
- def size; return self[2,2]; end
186
-
187
- # Set the width and height of the Rect.
188
- def size=(size)
189
- raise ArgumentError, "Rect#size= takes an Array of form [width, height]." if size.size != 2
190
- self[2,2] = size
191
- size
192
- end
193
-
194
- # Return the x coordinate of the right side of the Rect.
195
- def right; return self.at(0)+self.at(2); end
196
-
197
- # Set the x coordinate of the right side of the Rect by translating the
198
- # Rect (adjusting the x offset).
199
- def right=(r); self[0] = r - self.at(2); return r; end
200
-
201
- alias r right
202
- alias r= right=;
203
-
204
- # Return the y coordinate of the bottom side of the Rect.
205
- def bottom; return self.at(1)+self.at(3); end
206
-
207
- # Set the y coordinate of the bottom side of the Rect by translating the
208
- # Rect (adjusting the y offset).
209
- def bottom=(b); self[1] = b - self.at(3); return b; end
210
-
211
- alias b bottom
212
- alias b= bottom=;
213
-
214
- # Return the x and y coordinates of the center of the Rect.
215
- def center; return self.centerx, self.centery; end
216
-
217
- # Set the x and y coordinates of the center of the Rect by translating the
218
- # Rect (adjusting the x and y offsets).
219
- def center=(center)
220
- raise ArgumentError, "Rect#center= takes an Array of the form [x,y]." if center.size != 2
221
- self.centerx, self.centery = center
222
- center
223
- end
224
- alias c center
225
- alias c= center=;
226
-
227
- # Return the x coordinate of the center of the Rect
228
- def centerx; return self.at(0)+(self.at(2).div(2)); end
229
-
230
- # Set the x coordinate of the center of the Rect by translating the
231
- # Rect (adjusting the x offset).
232
- def centerx=(x); self[0] = x - (self.at(2).div(2)); return x; end
233
-
234
- alias cx centerx
235
- alias cx= centerx=;
236
-
237
- # Return the y coordinate of the center of the Rect
238
- def centery; return self.at(1)+(self.at(3).div(2)); end
239
-
240
- # Set the y coordinate of the center of the Rect by translating the
241
- # Rect (adjusting the y offset).
242
- def centery=(y); self[1] = y- (self.at(3).div(2)); return y; end
243
-
244
- alias cy centery
245
- alias cy= centery=;
246
-
247
- # Return the x and y coordinates of the top-left corner of the Rect
248
- def topleft; return self[0,2].to_a; end
249
-
250
- # Set the x and y coordinates of the top-left corner of the Rect by
251
- # translating the Rect (adjusting the x and y offsets).
252
- def topleft=(topleft)
253
- raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topleft.size != 2
254
- self[0,2] = topleft
255
- return topleft
256
- end
257
-
258
- alias tl topleft
259
- alias tl= topleft=;
260
-
261
- # Return the x and y coordinates of the top-right corner of the Rect
262
- def topright; return self.right, self.at(1); end
263
-
264
- # Set the x and y coordinates of the top-right corner of the Rect by
265
- # translating the Rect (adjusting the x and y offsets).
266
- def topright=(topright)
267
- raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topright.size != 2
268
- self.right, self[1] = topright
269
- return topright
270
- end
271
-
272
- alias tr topright
273
- alias tr= topright=;
274
-
275
- # Return the x and y coordinates of the bottom-left corner of the Rect
276
- def bottomleft; return self.at(0), self.bottom; end
277
-
278
- # Set the x and y coordinates of the bottom-left corner of the Rect by
279
- # translating the Rect (adjusting the x and y offsets).
280
- def bottomleft=(bottomleft)
281
- raise ArgumentError, "Rect#bottomleft= takes an Array of form [x, y]." if bottomleft.size != 2
282
- self[0], self.bottom = bottomleft
283
- return bottomleft
284
- end
285
-
286
- alias bl bottomleft
287
- alias bl= bottomleft=;
288
-
289
- # Return the x and y coordinates of the bottom-right corner of the Rect
290
- def bottomright; return self.right, self.bottom; end
291
-
292
- # Set the x and y coordinates of the bottom-right corner of the Rect by
293
- # translating the Rect (adjusting the x and y offsets).
294
- def bottomright=(bottomright)
295
- raise ArgumentError, "Rect#bottomright= takes an Array of form [x, y]." if bottomright.size != 2
296
- self.right, self.bottom = bottomright
297
- return bottomright
298
- end
299
-
300
- alias br bottomright
301
- alias br= bottomright=;
302
-
303
- # Return the x and y coordinates of the midpoint on the left side of the
304
- # Rect.
305
- def midleft; return self.at(0), self.centery; end
306
-
307
- # Set the x and y coordinates of the midpoint on the left side of the Rect
308
- # by translating the Rect (adjusting the x and y offsets).
309
- def midleft=(midleft)
310
- raise ArgumentError, "Rect#midleft= takes an Array of form [x, y]." if midleft.size != 2
311
- self[0], self.centery = midleft
312
- return midleft
313
- end
314
-
315
- alias ml midleft
316
- alias ml= midleft=;
317
-
318
- # Return the x and y coordinates of the midpoint on the left side of the
319
- # Rect.
320
- def midtop; return self.centerx, self.at(1); end
321
-
322
- # Set the x and y coordinates of the midpoint on the top side of the Rect
323
- # by translating the Rect (adjusting the x and y offsets).
324
- def midtop=(midtop)
325
- raise ArgumentError, "Rect#midtop= takes an Array of form [x, y]." if midtop.size != 2
326
- self.centerx, self[1] = midtop
327
- return midtop
328
- end
329
-
330
- alias mt midtop
331
- alias mt= midtop=;
332
-
333
- # Return the x and y coordinates of the midpoint on the left side of the
334
- # Rect.
335
- def midright; return self.right, self.centery; end
336
-
337
- # Set the x and y coordinates of the midpoint on the right side of the Rect
338
- # by translating the Rect (adjusting the x and y offsets).
339
- def midright=(midright)
340
- raise ArgumentError, "Rect#midright= takes an Array of form [x, y]." if midright.size != 2
341
- self.right, self.centery = midright
342
- return midright
343
- end
344
-
345
- alias mr midright
346
- alias mr= midright=;
347
-
348
- # Return the x and y coordinates of the midpoint on the left side of the
349
- # Rect.
350
- def midbottom; return self.centerx, self.bottom; end
351
-
352
- # Set the x and y coordinates of the midpoint on the bottom side of the
353
- # Rect by translating the Rect (adjusting the x and y offsets).
354
- def midbottom=(midbottom)
355
- raise ArgumentError, "Rect#midbottom= takes an Array of form [x, y]." if midbottom.size != 2
356
- self.centerx, self.bottom = midbottom
357
- return midbottom
358
- end
359
-
360
- alias mb midbottom
361
- alias mb= midbottom=;
362
-
363
- #--
364
- # UTILITY METHODS
365
- #++
366
-
367
-
368
- # As #clamp!, but the original caller is not changed.
369
- def clamp(rect)
370
- self.dup.clamp!(rect)
371
- end
372
-
373
- # Translate the calling Rect to be entirely inside the given Rect. If
374
- # the caller is too large along either axis to fit in the given rect,
375
- # it is centered with respect to the given rect, along that axis.
376
- def clamp!(rect)
377
- nself = self.normalize
378
- rect = Rect.new_from_object(rect)
379
- #If self is inside given, there is no need to move self
380
- unless rect.contain?(nself)
381
-
382
- #If self is too wide:
383
- if nself.at(2) >= rect.at(2)
384
- self[0] = rect.centerx - nself.at(2).div(2)
385
- #Else self is not too wide
386
- else
387
- #If self is to the left of arg
388
- if nself.at(0) < rect.at(0)
389
- self[0] = rect.at(0)
390
- #If self is to the right of arg
391
- elsif nself.right > rect.right
392
- self[0] = rect.right - nself.at(2)
393
- #Otherwise, leave x alone
394
- end
395
- end
396
-
397
- #If self is too tall:
398
- if nself.at(3) >= rect.at(3)
399
- self[1] = rect.centery - nself.at(3).div(2)
400
- #Else self is not too tall
401
- else
402
- #If self is above arg
403
- if nself.at(1) < rect.at(1)
404
- self[1] = rect.at(1)
405
- #If self below arg
406
- elsif nself.bottom > rect.bottom
407
- self[1] = rect.bottom - nself.at(3)
408
- #Otherwise, leave y alone
409
- end
410
- end
411
- end
412
- return self
413
- end
414
-
415
- # As #clip!, but the original caller is not changed.
416
- def clip(rect)
417
- self.dup.clip!(rect)
418
- end
419
-
420
- # Crop the calling Rect to be entirely inside the given Rect. If the
421
- # caller does not intersect the given Rect at all, its width and height
422
- # are set to zero, but its x and y offsets are not changed.
423
- #
424
- # As a side effect, the Rect is normalized.
425
- def clip!(rect)
426
- nself = self.normalize
427
- other = Rect.new_from_object(rect).normalize!
428
- if self.collide_rect?(other)
429
- self[0] = [nself.at(0), other.at(0)].max
430
- self[1] = [nself.at(1), other.at(1)].max
431
- self[2] = [nself.right, other.right].min - self.at(0)
432
- self[3] = [nself.bottom, other.bottom].min - self.at(1)
433
- else #if they do not intersect at all:
434
- self[0], self[1] = nself.topleft
435
- self[2], self[3] = 0, 0
436
- end
437
- return self
438
- end
439
-
440
- # Iterate through all key/value pairs in the given hash table, and
441
- # return the first pair whose value is a Rect that collides with the
442
- # caller.
443
- #
444
- # Because a hash table is unordered, you should not expect any
445
- # particular Rect to be returned first.
446
- def collide_hash(hash_rects)
447
- hash_rects.each { |key,value|
448
- if value.collide_rect?+(self); return [key,value]; end
449
- }
450
- return nil
451
- end
452
-
453
- # Iterate through all key/value pairs in the given hash table, and
454
- # return an Array of every pair whose value is a Rect that collides
455
- # the caller.
456
- #
457
- # Because a hash table is unordered, you should not expect the returned
458
- # pairs to be in any particular order.
459
- def collide_hash_all(hash_rects)
460
- hash_rects.select { |key,value|
461
- value.collide_rect?+(self)
462
- }
463
- end
464
-
465
- # Iterate through all elements in the given Array, and return
466
- # the *index* of the first element which is a Rect that collides with
467
- # the caller.
468
- def collide_array(array_rects)
469
- for i in (0...(array_rects.length))
470
- if array_rects[i].collide_rect?(self)
471
- return i
472
- end
473
- end
474
- return nil
475
- end
476
-
477
- # Iterate through all elements in the given Array, and return
478
- # an Array containing the *indices* of every element that is a Rect
479
- # that collides with the caller.
480
- def collide_array_all(array_rects)
481
- indexes = []
482
- for i in (0...(array_rects.length))
483
- if array_rects[i].collide_rect?(self)
484
- indexes += [i]
485
- end
486
- end
487
- return indexes
488
- end
489
-
490
- # True if the point is inside (including on the border) of the caller.
491
- # If you have Array of coordinates, you can use collide_point?(*coords).
492
- def collide_point?(x,y)
493
- nself = normalize()
494
- x.between?(nself.left,nself.right) && y.between?(nself.top,nself.bottom)
495
- end
496
-
497
- # True if the caller and the given Rect overlap (or touch) at all.
498
- def collide_rect?(rect)
499
- nself = self.normalize
500
- rect = Rect.new_from_object(rect).normalize!
501
- return ((nself.l >= rect.l && nself.l <= rect.r) or (rect.l >= nself.l && rect.l <= nself.r)) &&
502
- ((nself.t >= rect.t && nself.t <= rect.b) or (rect.t >= nself.t && rect.t <= nself.b))
503
- end
504
-
505
- # True if the given Rect is totally within the caller. Borders may
506
- # overlap.
507
- def contain?(rect)
508
- nself = self.normalize
509
- rect = Rect.new_from_object(rect).normalize!
510
- return (nself.left <= rect.left and rect.right <= nself.right and
511
- nself.top <= rect.top and rect.bottom <= nself.bottom)
512
- end
513
-
514
- # As #inflate!, but the original caller is not changed.
515
- def inflate(x,y)
516
- return self.class.new(self.at(0) - x.div(2),
517
- self.at(1) - y.div(2),
518
- self.at(2) + x,
519
- self.at(3) + y)
520
- end
521
-
522
- # Increase the Rect's size is the x and y directions, while keeping the
523
- # same center point. For best results, expand by an even number.
524
- # X and y inflation can be given as an Array or as separate values.
525
- def inflate!(x,y)
526
- self[0] -= x.div(2)
527
- self[1] -= y.div(2)
528
- self[2] += x
529
- self[3] += y
530
- return self
531
- end
532
-
533
- # As #move!, but the original caller is not changed.
534
- def move(x,y)
535
- self.dup.move!(x,y)
536
- end
537
-
538
- # Translate the Rect by the given amounts in the x and y directions.
539
- # Positive values are rightward for x and downward for y.
540
- # X and y movement can be given as an Array or as separate values.
541
- def move!(x,y)
542
- self[0]+=x; self[1]+=y
543
- return self
544
- end
545
-
546
- # As #normalize!, but the original caller is not changed.
547
- def normalize
548
- self.dup.normalize!()
549
- end
550
-
551
- # Fix Rects that have negative width or height, without changing the
552
- # area it represents. Has no effect on Rects with non-negative width
553
- # and height. Some Rect methods will automatically normalize the Rect.
554
- def normalize!
555
- if self.at(2) < 0
556
- self[0], self[2] = self.at(0)+self.at(2), -self.at(2)
557
- end
558
- if self.at(3) < 0
559
- self[1], self[3] = self.at(1)+self.at(3), -self.at(3)
560
- end
561
- self
562
- end
563
-
564
- # As #union!, but the original caller is not changed.
565
- def union(rect)
566
- self.dup.union!(rect)
567
- end
568
-
569
- # Expand the caller to also cover the given Rect. The Rect is still a
570
- # rectangle, so it may also cover areas that neither of the original
571
- # Rects did, for example areas between the two Rects.
572
- def union!(rect)
573
- self.normalize!
574
- rleft, rtop = self.topleft
575
- rright, rbottom = self.bottomright
576
- r2 = Rect.new_from_object(rect).normalize!
577
-
578
- rleft = [rleft, r2.left].min
579
- rtop = [rtop, r2.top].min
580
- rright = [rright, r2.right].max
581
- rbottom = [rbottom, r2.bottom].max
582
-
583
- self[0,4] = rleft, rtop, rright - rleft, rbottom - rtop
584
- return self
585
- end
586
-
587
- # As #union_all!, but the original caller is not changed.
588
- def union_all(array_rects)
589
- self.dup.union_all!(array_rects)
590
- end
591
-
592
- # Expand the caller to cover all of the given Rects. See also #union!
593
- def union_all!(array_rects)
594
- array_rects.each do |r|
595
- self.union!(r)
596
- end
597
- return self
598
- end
599
-
600
-
601
- end # class Rect
602
-
603
-
604
- class Surface
605
- # Return a Rect with the same width and height as the Surface, positioned
606
- # at (0,0).
607
- def make_rect()
608
- return Rect.new(0,0,self.width,self.height)
609
- end
610
- end
611
-
612
- end # module Rubygame
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
+ #--
21
+ # Table of Contents:
22
+ #
23
+ # class Rect
24
+ # GENERAL:
25
+ # initialize
26
+ # new_from_object
27
+ # to_s
28
+ # to_a, to_ary
29
+ # []
30
+ # ATTRIBUTES:
31
+ # x, y, w, h [<- accessors]
32
+ # width, height, size
33
+ # left, top, right, bottom
34
+ # center, centerx, centery
35
+ # topleft, topright
36
+ # bottomleft, bottomright
37
+ # midleft, midtop, midright, midbottom
38
+ # UTILITY METHODS:
39
+ # clamp, clamp!
40
+ # clip, clip!
41
+ # collide_hash, collide_hash_all
42
+ # collide_array, collide_array_all
43
+ # collide_point?
44
+ # collide_rect?
45
+ # contain?
46
+ # inflate, inflate!
47
+ # move, move!
48
+ # normalize, normalize!
49
+ # union, union!
50
+ # union_all, union_all!
51
+ #
52
+ # class Surface
53
+ # make_rect
54
+ #
55
+ #++
56
+
57
+ module Chingu
58
+
59
+ # A Rect is a representation of a rectangle, with four core attributes
60
+ # (x offset, y offset, width, and height) and a variety of functions
61
+ # for manipulating and accessing these attributes.
62
+ #
63
+ # Like all coordinates in Rubygame (and its base library, SDL), x and y
64
+ # offsets are measured from the top-left corner of the screen, with greater
65
+ # y offsets being lower. Thus, specifying the x and y offsets of the Rect
66
+ # is equivalent to setting the location of its top-left corner.
67
+ #
68
+ # In Rubygame, Rects are used for collision detection and describing
69
+ # the area of a Surface to operate on.
70
+ class Rect < Array
71
+
72
+ #--
73
+ # GENERAL
74
+ #++
75
+
76
+ # Create a new Rect, attempting to extract its own information from
77
+ # the given arguments. The arguments must fall into one of these cases:
78
+ #
79
+ # - 4 integers +(x, y, w, h)+.
80
+ # - 1 Rect or Array containing 4 integers +([x, y, w, h])+.
81
+ # - 2 Arrays containing 2 integers each +([x,y], [w,h])+.
82
+ # - 1 object with a +rect+ attribute which is a valid Rect object.
83
+ #
84
+ # All rect core attributes (x,y,w,h) must be integers.
85
+ #
86
+ def initialize(*argv)
87
+ case argv.length
88
+ when 1
89
+ if argv[0].kind_of? Array; super(argv[0])
90
+ elsif argv[0].respond_to? :rect; super(argv[0])
91
+ end
92
+ when 2
93
+ super(argv[0].concat(argv[1]))
94
+ when 4
95
+ super(argv)
96
+ end
97
+ return self
98
+ end
99
+
100
+ # Extract or generate a Rect from the given object, if possible, using the
101
+ # following process:
102
+ #
103
+ # 1. If it's a Rect already, return a duplicate Rect.
104
+ # 2. Elsif it's an Array with at least 4 values, make a Rect from it.
105
+ # 3. Elsif it has a +rect+ attribute., perform (1) and (2) on that.
106
+ # 4. Otherwise, raise TypeError.
107
+ #
108
+ # See also Surface#make_rect()
109
+ def Rect.new_from_object(object)
110
+ case(object)
111
+ when Rect
112
+ return object.dup
113
+ when Array
114
+ if object.length >= 4
115
+ return Rect.new(object)
116
+ else
117
+ raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.length )
118
+ end
119
+ else
120
+ begin
121
+ case(object.rect)
122
+ when Rect
123
+ return object.rect.dup
124
+ when Array
125
+ if object.rect.length >= 4
126
+ return Rect.new(object.rect)
127
+ else
128
+ raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.rect.length )
129
+ end
130
+ end # case object.rect
131
+ rescue NoMethodError # if no rect.rect
132
+ raise(TypeError,"Object must be a Rect or Array [x,y,w,h], or have an attribute called 'rect'. (Got %s instance.)"%object.class)
133
+ end
134
+ end # case object
135
+ end
136
+
137
+
138
+ # Print the Rect in the form "+#<Rect [x,y,w,h]>+"
139
+ def to_s; "#<Rect [%s,%s,%s,%s]>"%self; end
140
+
141
+ # Print the Rect in the form "+#<Rect:id [x,y,w,h]>+"
142
+ def inspect; "#<Rect:#{self.object_id} [%s,%s,%s,%s]>"%self; end
143
+
144
+ #--
145
+ # ATTRIBUTES
146
+ #++
147
+
148
+ # Returns self.at(0)
149
+ def x; return self.at(0); end
150
+ # Sets self[0] to +val+
151
+ def x=(val); self[0] = val; end
152
+
153
+ alias left x
154
+ alias left= x=;
155
+ alias l x
156
+ alias l= x=;
157
+
158
+ # Returns self.at(1)
159
+ def y; return self.at(1); end
160
+ # Sets self[1] to +val+
161
+ def y=(val); self[1] = val; end
162
+
163
+ alias top y
164
+ alias top= y=;
165
+ alias t y
166
+ alias t= y=;
167
+
168
+ # Returns self.at(2)
169
+ def w; return self.at(2); end
170
+ # Sets self[2] to +val+
171
+ def w=(val); self[2] = val; end
172
+
173
+ alias width w
174
+ alias width= w=;
175
+
176
+ # Returns self.at(3)
177
+ def h; return self.at(3); end
178
+ # Sets self[3] to +val+
179
+ def h=(val); self[3] = val; end
180
+
181
+ alias height h
182
+ alias height= h=;
183
+
184
+ # Return the width and height of the Rect.
185
+ def size; return self[2,2]; end
186
+
187
+ # Set the width and height of the Rect.
188
+ def size=(size)
189
+ raise ArgumentError, "Rect#size= takes an Array of form [width, height]." if size.size != 2
190
+ self[2,2] = size
191
+ size
192
+ end
193
+
194
+ # Return the x coordinate of the right side of the Rect.
195
+ def right; return self.at(0)+self.at(2); end
196
+
197
+ # Set the x coordinate of the right side of the Rect by translating the
198
+ # Rect (adjusting the x offset).
199
+ def right=(r); self[0] = r - self.at(2); return r; end
200
+
201
+ alias r right
202
+ alias r= right=;
203
+
204
+ # Return the y coordinate of the bottom side of the Rect.
205
+ def bottom; return self.at(1)+self.at(3); end
206
+
207
+ # Set the y coordinate of the bottom side of the Rect by translating the
208
+ # Rect (adjusting the y offset).
209
+ def bottom=(b); self[1] = b - self.at(3); return b; end
210
+
211
+ alias b bottom
212
+ alias b= bottom=;
213
+
214
+ # Return the x and y coordinates of the center of the Rect.
215
+ def center; return self.centerx, self.centery; end
216
+
217
+ # Set the x and y coordinates of the center of the Rect by translating the
218
+ # Rect (adjusting the x and y offsets).
219
+ def center=(center)
220
+ raise ArgumentError, "Rect#center= takes an Array of the form [x,y]." if center.size != 2
221
+ self.centerx, self.centery = center
222
+ center
223
+ end
224
+ alias c center
225
+ alias c= center=;
226
+
227
+ # Return the x coordinate of the center of the Rect
228
+ def centerx; return self.at(0)+(self.at(2).div(2)); end
229
+
230
+ # Set the x coordinate of the center of the Rect by translating the
231
+ # Rect (adjusting the x offset).
232
+ def centerx=(x); self[0] = x - (self.at(2).div(2)); return x; end
233
+
234
+ alias cx centerx
235
+ alias cx= centerx=;
236
+
237
+ # Return the y coordinate of the center of the Rect
238
+ def centery; return self.at(1)+(self.at(3).div(2)); end
239
+
240
+ # Set the y coordinate of the center of the Rect by translating the
241
+ # Rect (adjusting the y offset).
242
+ def centery=(y); self[1] = y- (self.at(3).div(2)); return y; end
243
+
244
+ alias cy centery
245
+ alias cy= centery=;
246
+
247
+ # Return the x and y coordinates of the top-left corner of the Rect
248
+ def topleft; return self[0,2].to_a; end
249
+
250
+ # Set the x and y coordinates of the top-left corner of the Rect by
251
+ # translating the Rect (adjusting the x and y offsets).
252
+ def topleft=(topleft)
253
+ raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topleft.size != 2
254
+ self[0,2] = topleft
255
+ return topleft
256
+ end
257
+
258
+ alias tl topleft
259
+ alias tl= topleft=;
260
+
261
+ # Return the x and y coordinates of the top-right corner of the Rect
262
+ def topright; return self.right, self.at(1); end
263
+
264
+ # Set the x and y coordinates of the top-right corner of the Rect by
265
+ # translating the Rect (adjusting the x and y offsets).
266
+ def topright=(topright)
267
+ raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topright.size != 2
268
+ self.right, self[1] = topright
269
+ return topright
270
+ end
271
+
272
+ alias tr topright
273
+ alias tr= topright=;
274
+
275
+ # Return the x and y coordinates of the bottom-left corner of the Rect
276
+ def bottomleft; return self.at(0), self.bottom; end
277
+
278
+ # Set the x and y coordinates of the bottom-left corner of the Rect by
279
+ # translating the Rect (adjusting the x and y offsets).
280
+ def bottomleft=(bottomleft)
281
+ raise ArgumentError, "Rect#bottomleft= takes an Array of form [x, y]." if bottomleft.size != 2
282
+ self[0], self.bottom = bottomleft
283
+ return bottomleft
284
+ end
285
+
286
+ alias bl bottomleft
287
+ alias bl= bottomleft=;
288
+
289
+ # Return the x and y coordinates of the bottom-right corner of the Rect
290
+ def bottomright; return self.right, self.bottom; end
291
+
292
+ # Set the x and y coordinates of the bottom-right corner of the Rect by
293
+ # translating the Rect (adjusting the x and y offsets).
294
+ def bottomright=(bottomright)
295
+ raise ArgumentError, "Rect#bottomright= takes an Array of form [x, y]." if bottomright.size != 2
296
+ self.right, self.bottom = bottomright
297
+ return bottomright
298
+ end
299
+
300
+ alias br bottomright
301
+ alias br= bottomright=;
302
+
303
+ # Return the x and y coordinates of the midpoint on the left side of the
304
+ # Rect.
305
+ def midleft; return self.at(0), self.centery; end
306
+
307
+ # Set the x and y coordinates of the midpoint on the left side of the Rect
308
+ # by translating the Rect (adjusting the x and y offsets).
309
+ def midleft=(midleft)
310
+ raise ArgumentError, "Rect#midleft= takes an Array of form [x, y]." if midleft.size != 2
311
+ self[0], self.centery = midleft
312
+ return midleft
313
+ end
314
+
315
+ alias ml midleft
316
+ alias ml= midleft=;
317
+
318
+ # Return the x and y coordinates of the midpoint on the left side of the
319
+ # Rect.
320
+ def midtop; return self.centerx, self.at(1); end
321
+
322
+ # Set the x and y coordinates of the midpoint on the top side of the Rect
323
+ # by translating the Rect (adjusting the x and y offsets).
324
+ def midtop=(midtop)
325
+ raise ArgumentError, "Rect#midtop= takes an Array of form [x, y]." if midtop.size != 2
326
+ self.centerx, self[1] = midtop
327
+ return midtop
328
+ end
329
+
330
+ alias mt midtop
331
+ alias mt= midtop=;
332
+
333
+ # Return the x and y coordinates of the midpoint on the left side of the
334
+ # Rect.
335
+ def midright; return self.right, self.centery; end
336
+
337
+ # Set the x and y coordinates of the midpoint on the right side of the Rect
338
+ # by translating the Rect (adjusting the x and y offsets).
339
+ def midright=(midright)
340
+ raise ArgumentError, "Rect#midright= takes an Array of form [x, y]." if midright.size != 2
341
+ self.right, self.centery = midright
342
+ return midright
343
+ end
344
+
345
+ alias mr midright
346
+ alias mr= midright=;
347
+
348
+ # Return the x and y coordinates of the midpoint on the left side of the
349
+ # Rect.
350
+ def midbottom; return self.centerx, self.bottom; end
351
+
352
+ # Set the x and y coordinates of the midpoint on the bottom side of the
353
+ # Rect by translating the Rect (adjusting the x and y offsets).
354
+ def midbottom=(midbottom)
355
+ raise ArgumentError, "Rect#midbottom= takes an Array of form [x, y]." if midbottom.size != 2
356
+ self.centerx, self.bottom = midbottom
357
+ return midbottom
358
+ end
359
+
360
+ alias mb midbottom
361
+ alias mb= midbottom=;
362
+
363
+
364
+ # returns rects for side collision
365
+ def left_side
366
+ Rect.new([ x, y, width * 0.25, height ])
367
+ end
368
+
369
+ def right_side
370
+ Rect.new([ x + (width * 0.75), y, width * 0.25, height ])
371
+ end
372
+
373
+ def top_side
374
+ Rect.new([ x, y, width, height * 0.25 ])
375
+ end
376
+
377
+ def bottom_side
378
+ Rect.new([ x, y + (height * 0.75), width, height * 0.25 ])
379
+ end
380
+
381
+ #--
382
+ # UTILITY METHODS
383
+ #++
384
+
385
+ # As #clamp!, but the original caller is not changed.
386
+ def clamp(rect)
387
+ self.dup.clamp!(rect)
388
+ end
389
+
390
+ # Translate the calling Rect to be entirely inside the given Rect. If
391
+ # the caller is too large along either axis to fit in the given rect,
392
+ # it is centered with respect to the given rect, along that axis.
393
+ def clamp!(rect)
394
+ nself = self.normalize
395
+ rect = Rect.new_from_object(rect)
396
+ #If self is inside given, there is no need to move self
397
+ unless rect.contain?(nself)
398
+
399
+ #If self is too wide:
400
+ if nself.at(2) >= rect.at(2)
401
+ self[0] = rect.centerx - nself.at(2).div(2)
402
+ #Else self is not too wide
403
+ else
404
+ #If self is to the left of arg
405
+ if nself.at(0) < rect.at(0)
406
+ self[0] = rect.at(0)
407
+ #If self is to the right of arg
408
+ elsif nself.right > rect.right
409
+ self[0] = rect.right - nself.at(2)
410
+ #Otherwise, leave x alone
411
+ end
412
+ end
413
+
414
+ #If self is too tall:
415
+ if nself.at(3) >= rect.at(3)
416
+ self[1] = rect.centery - nself.at(3).div(2)
417
+ #Else self is not too tall
418
+ else
419
+ #If self is above arg
420
+ if nself.at(1) < rect.at(1)
421
+ self[1] = rect.at(1)
422
+ #If self below arg
423
+ elsif nself.bottom > rect.bottom
424
+ self[1] = rect.bottom - nself.at(3)
425
+ #Otherwise, leave y alone
426
+ end
427
+ end
428
+ end
429
+ return self
430
+ end
431
+
432
+ # As #clip!, but the original caller is not changed.
433
+ def clip(rect)
434
+ self.dup.clip!(rect)
435
+ end
436
+
437
+ # Crop the calling Rect to be entirely inside the given Rect. If the
438
+ # caller does not intersect the given Rect at all, its width and height
439
+ # are set to zero, but its x and y offsets are not changed.
440
+ #
441
+ # As a side effect, the Rect is normalized.
442
+ def clip!(rect)
443
+ nself = self.normalize
444
+ other = Rect.new_from_object(rect).normalize!
445
+ if self.collide_rect?(other)
446
+ self[0] = [nself.at(0), other.at(0)].max
447
+ self[1] = [nself.at(1), other.at(1)].max
448
+ self[2] = [nself.right, other.right].min - self.at(0)
449
+ self[3] = [nself.bottom, other.bottom].min - self.at(1)
450
+ else #if they do not intersect at all:
451
+ self[0], self[1] = nself.topleft
452
+ self[2], self[3] = 0, 0
453
+ end
454
+ return self
455
+ end
456
+
457
+ # Iterate through all key/value pairs in the given hash table, and
458
+ # return the first pair whose value is a Rect that collides with the
459
+ # caller.
460
+ #
461
+ # Because a hash table is unordered, you should not expect any
462
+ # particular Rect to be returned first.
463
+ def collide_hash(hash_rects)
464
+ hash_rects.each { |key,value|
465
+ if value.collide_rect?+(self); return [key,value]; end
466
+ }
467
+ return nil
468
+ end
469
+
470
+ # Iterate through all key/value pairs in the given hash table, and
471
+ # return an Array of every pair whose value is a Rect that collides
472
+ # the caller.
473
+ #
474
+ # Because a hash table is unordered, you should not expect the returned
475
+ # pairs to be in any particular order.
476
+ def collide_hash_all(hash_rects)
477
+ hash_rects.select { |key,value|
478
+ value.collide_rect?+(self)
479
+ }
480
+ end
481
+
482
+ # Iterate through all elements in the given Array, and return
483
+ # the *index* of the first element which is a Rect that collides with
484
+ # the caller.
485
+ def collide_array(array_rects)
486
+ for i in (0...(array_rects.length))
487
+ if array_rects[i].collide_rect?(self)
488
+ return i
489
+ end
490
+ end
491
+ return nil
492
+ end
493
+
494
+ # Iterate through all elements in the given Array, and return
495
+ # an Array containing the *indices* of every element that is a Rect
496
+ # that collides with the caller.
497
+ def collide_array_all(array_rects)
498
+ indexes = []
499
+ for i in (0...(array_rects.length))
500
+ if array_rects[i].collide_rect?(self)
501
+ indexes += [i]
502
+ end
503
+ end
504
+ return indexes
505
+ end
506
+
507
+ # True if the point is inside (including on the border) of the caller.
508
+ # If you have Array of coordinates, you can use collide_point?(*coords).
509
+ def collide_point?(x,y)
510
+ nself = normalize()
511
+ x.between?(nself.left,nself.right) && y.between?(nself.top,nself.bottom)
512
+ end
513
+
514
+ # True if the caller and the given Rect overlap (or touch) at all.
515
+ def collide_rect?(rect)
516
+ nself = self.normalize
517
+ rect = Rect.new_from_object(rect).normalize!
518
+ return ((nself.l >= rect.l && nself.l <= rect.r) or (rect.l >= nself.l && rect.l <= nself.r)) &&
519
+ ((nself.t >= rect.t && nself.t <= rect.b) or (rect.t >= nself.t && rect.t <= nself.b))
520
+ end
521
+
522
+ # True if the given Rect is totally within the caller. Borders may
523
+ # overlap.
524
+ def contain?(rect)
525
+ nself = self.normalize
526
+ rect = Rect.new_from_object(rect).normalize!
527
+ return (nself.left <= rect.left and rect.right <= nself.right and
528
+ nself.top <= rect.top and rect.bottom <= nself.bottom)
529
+ end
530
+
531
+ # As #inflate!, but the original caller is not changed.
532
+ def inflate(x,y)
533
+ return self.class.new(self.at(0) - x.div(2),
534
+ self.at(1) - y.div(2),
535
+ self.at(2) + x,
536
+ self.at(3) + y)
537
+ end
538
+
539
+ # Increase the Rect's size is the x and y directions, while keeping the
540
+ # same center point. For best results, expand by an even number.
541
+ # X and y inflation can be given as an Array or as separate values.
542
+ def inflate!(x,y)
543
+ self[0] -= x.div(2)
544
+ self[1] -= y.div(2)
545
+ self[2] += x
546
+ self[3] += y
547
+ return self
548
+ end
549
+
550
+ # As #move!, but the original caller is not changed.
551
+ def move(x,y)
552
+ self.dup.move!(x,y)
553
+ end
554
+
555
+ # Translate the Rect by the given amounts in the x and y directions.
556
+ # Positive values are rightward for x and downward for y.
557
+ # X and y movement can be given as an Array or as separate values.
558
+ def move!(x,y)
559
+ self[0]+=x; self[1]+=y
560
+ return self
561
+ end
562
+
563
+ # As #normalize!, but the original caller is not changed.
564
+ def normalize
565
+ self.dup.normalize!()
566
+ end
567
+
568
+ # Fix Rects that have negative width or height, without changing the
569
+ # area it represents. Has no effect on Rects with non-negative width
570
+ # and height. Some Rect methods will automatically normalize the Rect.
571
+ def normalize!
572
+ if self.at(2) < 0
573
+ self[0], self[2] = self.at(0)+self.at(2), -self.at(2)
574
+ end
575
+ if self.at(3) < 0
576
+ self[1], self[3] = self.at(1)+self.at(3), -self.at(3)
577
+ end
578
+ self
579
+ end
580
+
581
+ # As #union!, but the original caller is not changed.
582
+ def union(rect)
583
+ self.dup.union!(rect)
584
+ end
585
+
586
+ # Expand the caller to also cover the given Rect. The Rect is still a
587
+ # rectangle, so it may also cover areas that neither of the original
588
+ # Rects did, for example areas between the two Rects.
589
+ def union!(rect)
590
+ self.normalize!
591
+ rleft, rtop = self.topleft
592
+ rright, rbottom = self.bottomright
593
+ r2 = Rect.new_from_object(rect).normalize!
594
+
595
+ rleft = [rleft, r2.left].min
596
+ rtop = [rtop, r2.top].min
597
+ rright = [rright, r2.right].max
598
+ rbottom = [rbottom, r2.bottom].max
599
+
600
+ self[0,4] = rleft, rtop, rright - rleft, rbottom - rtop
601
+ return self
602
+ end
603
+
604
+ # As #union_all!, but the original caller is not changed.
605
+ def union_all(array_rects)
606
+ self.dup.union_all!(array_rects)
607
+ end
608
+
609
+ # Expand the caller to cover all of the given Rects. See also #union!
610
+ def union_all!(array_rects)
611
+ array_rects.each do |r|
612
+ self.union!(r)
613
+ end
614
+ return self
615
+ end
616
+
617
+
618
+ end # class Rect
619
+
620
+
621
+ class Surface
622
+ # Return a Rect with the same width and height as the Surface, positioned
623
+ # at (0,0).
624
+ def make_rect()
625
+ return Rect.new(0,0,self.width,self.height)
626
+ end
627
+ end
628
+
629
+ end # module Rubygame