chingu 0.7.6.5 → 0.7.6.6

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.
@@ -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