rubygame 2.3.0 → 2.4.0

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.
@@ -0,0 +1,454 @@
1
+ #--
2
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
+ # Copyright (C) 2004-2008 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
+ require 'rubygame/event_hook'
21
+
22
+
23
+ # EventHandler provides a simple, extensible system for
24
+ # hook-based event handling.
25
+ #
26
+ # An EventHandler holds a list of EventHook objects. When
27
+ # the EventHandler receives a new event (passed to #handle),
28
+ # it tests the event against each EventHook. If the event
29
+ # matches the EventHook, the EventHandler passes the event to
30
+ # the EventHook to perform an action (such as calling a
31
+ # method or executing a block).
32
+ #
33
+ # Although the EventHandler and EventHook classes are very
34
+ # simple in themselves, they can be used as building blocks
35
+ # to create flexible and complex systems, whatever is needed
36
+ # by your application.
37
+ #
38
+ # Here are a few ways you could use EventHandler:
39
+ #
40
+ # * One central EventHandler with EventHooks to perform
41
+ # all types of actions. This is good for simple apps.
42
+ #
43
+ # * Multiple EventHandlers, one for each category of
44
+ # event. For example, one for keyboard input, one for
45
+ # mouse input, one for game logic events, etc.
46
+ #
47
+ # * An EventHandler in every game object (using the
48
+ # HasEventHandler mixin module), being fed events from
49
+ # a central EventHandler.
50
+ #
51
+ # You can also extend the possibilities of EventHandler and
52
+ # EventHook by creating your own event trigger and action
53
+ # classes. See EventHook for more information.
54
+ #
55
+ class Rubygame::EventHandler
56
+
57
+ # call-seq:
58
+ # EventHandler.new { |handler| ... } -> new_handler
59
+ #
60
+ # Create a new EventHandler. The optional block can be used
61
+ # for further initializing the EventHandler.
62
+ #
63
+ def initialize(&block)
64
+ @hooks = []
65
+ yield self if block_given?
66
+ end
67
+
68
+ attr_accessor :hooks
69
+
70
+ # call-seq:
71
+ # append_hook( hook ) -> hook
72
+ # append_hook( description ) -> hook
73
+ #
74
+ # Puts an EventHook at the bottom of the stack (it will be handled last).
75
+ # If the EventHandler already has that hook, it is moved to the bottom.
76
+ # See EventHook and #handle.
77
+ #
78
+ # This method has two distinct forms. The first form adds an existing Hook
79
+ # instance; the second form constructs a new EventHook instance and adds it.
80
+ #
81
+ # hook:: the hook to add. (EventHook, for first form only)
82
+ #
83
+ # description:: a Hash to initialize a new EventHook.
84
+ # (Hash, for second form only)
85
+ #
86
+ # Returns:: the hook that was added. (EventHook)
87
+ #
88
+ # Contrast this method with #prepend, which puts the EventHook at
89
+ # the top of the stack.
90
+ #
91
+ def append_hook( hook )
92
+ hook = EventHook.new( hook ) if hook.kind_of?( Hash )
93
+ @hooks = (@hooks - [hook]) | [hook]
94
+ return hook
95
+ end
96
+
97
+ # call-seq:
98
+ # prepend_hook( hook ) -> hook
99
+ # prepend_hook( description ) -> hook
100
+ #
101
+ # Exactly like #append_hook, except that the EventHook is put at the
102
+ # top of the stack (it will be handled first).
103
+ # If the EventHandler already has that hook, it is moved to the top.
104
+ #
105
+ def prepend_hook( hook )
106
+ hook = EventHook.new( hook ) if hook.kind_of?( Hash )
107
+ @hooks = [hook] | @hooks
108
+ return hook
109
+ end
110
+
111
+ # call-seq:
112
+ # handle( event ) -> nil
113
+ #
114
+ # Triggers every hook in the stack which matches the given event.
115
+ # See EventHook.
116
+ #
117
+ # If one of the matching hooks has @consumes enabled, no hooks
118
+ # after it will receive that event. (Example use: a mouse click that
119
+ # only affects the top-most object it hits, not any below it.)
120
+ #
121
+ # event: the event to handle. (Object, required)
122
+ #
123
+ # Returns:: nil.
124
+ #
125
+ def handle( event )
126
+ matching_hooks = @hooks.select { |hook| hook.match?( event ) }
127
+
128
+ catch :event_consumed do
129
+ matching_hooks.each do |hook|
130
+ hook.perform( event )
131
+ throw :event_consumed if hook.consumes
132
+ end
133
+ end
134
+
135
+ return nil
136
+ end
137
+
138
+ # Returns true if the given EventHook instance is on the stack.
139
+ def has_hook?( hook )
140
+ @hooks.include?( hook )
141
+ end
142
+
143
+ # Removes the given EventHook instance from the stack, if it exists
144
+ # on the stack.
145
+ #
146
+ # Returns:: the hook that was removed, or nil if the hook did not
147
+ # exist on the stack.
148
+ #
149
+ # NOTE: You must pass the exact EventHook instance to remove it!
150
+ # Passing another EventHook that is "similar" will not work.
151
+ # So, you should store a reference to the hook when it is returned
152
+ # by #append_hook or #prepend_hook.
153
+ #
154
+ def remove_hook( hook )
155
+ @hooks.delete( hook )
156
+ end
157
+
158
+ end
159
+
160
+
161
+
162
+ # HasEventHandler is a mixin module to conveniently integrate
163
+ # EventHandler into any class, allowing instances of the class
164
+ # to hold event hooks and handle incoming events.
165
+ #
166
+ # To use HasEventHandler, you simply 'include' it in your class:
167
+ #
168
+ # class Player
169
+ # include Rubygame::EventHandler::HasEventHandler
170
+ #
171
+ # # ... the rest of your class ...
172
+ #
173
+ # end
174
+ #
175
+ # You can then use all of the functionality of HasEventHandler.
176
+ #
177
+ # HasEventHandler provides several methods for adding new
178
+ # event hooks to the object. The two basic methods for that are
179
+ # #append_hook and #prepend_hook. The #make_magic_hooks method can
180
+ # create multiple hooks very simply and conveniently.
181
+ #
182
+ # HasEventHandler also defines the #handle method, which accepts
183
+ # an event and gives it to the object's event handler. This is
184
+ # the recommended way to make the object process an event.
185
+ #
186
+ module Rubygame::EventHandler::HasEventHandler
187
+
188
+ # Appends a new hook to the end of the list. If the hook does
189
+ # not have an owner, the owner is set to this object before
190
+ # appending.
191
+ #
192
+ # hook:: the hook to append.
193
+ # (EventHook or Hash description, required)
194
+ #
195
+ # See also EventHandler#append_hook.
196
+ #
197
+ # Example:
198
+ #
199
+ # # Create and append new hook from a description:
200
+ # trigger = KeyPressedTrigger.new(:space)
201
+ # action = MethodAction.new(:jump)
202
+ # player.append_hook( :trigger => trigger, :action => action )
203
+ #
204
+ # # You can also give it an EventHook instance, if you want.
205
+ # hook = EventHook.new( :trigger => trigger, :action => action )
206
+ # player.append_hook( hook )
207
+ #
208
+ def append_hook( hook )
209
+ hook = _prepare_hook( hook )
210
+ @event_handler.append_hook( hook )
211
+ rescue NoMethodError
212
+ _make_event_handler
213
+ retry
214
+ end
215
+
216
+ # Passes the given event to the object's event handler.
217
+ def handle( event )
218
+ @event_handler.handle( event )
219
+ rescue NoMethodError
220
+ _make_event_handler
221
+ retry
222
+ end
223
+
224
+ # Returns true if the object's event handler includes the given
225
+ # EventHook instance.
226
+ def has_hook?( hook )
227
+ @event_handler.has_hook?( hook )
228
+ rescue NoMethodError
229
+ _make_event_handler
230
+ retry
231
+ end
232
+
233
+
234
+ # Convenience method for creating and appending hooks easily.
235
+ # It takes a Hash of {trigger_seed => action_seed} pairs, and
236
+ # creates and appends a new EventHook for each pair.
237
+ #
238
+ # Returns:: an Array of the EventHook instances that were
239
+ # created and appended.
240
+ #
241
+ # May raise:: ArgumentError, if an object doesn't match any
242
+ # conversion rules.
243
+ #
244
+ # Trigger and action can be symbols, classes, or other types of
245
+ # object. The method uses simple rules to convert the "seed"
246
+ # objects into appropriate event triggers or event actions.
247
+ #
248
+ # By default, triggers are converted according to these rules:
249
+ #
250
+ # * Symbols starting with "mouse" become a MouseClickTrigger.
251
+ # * Keyboard symbols become a KeyPressTrigger.
252
+ # * Classes become an InstanceOfTrigger.
253
+ # * Objects with a #match? method are duplicated and used
254
+ # as the trigger without being converted.
255
+ #
256
+ # By default, actions are converted according to these rules:
257
+ #
258
+ # * Symbols become a MethodAction.
259
+ # * Proc and Method instances become a BlockAction.
260
+ # * Objects with a #perform method are duplicated and used
261
+ # as the action without being converted.
262
+ #
263
+ # This method raises ArgumentError if an object doesn't match
264
+ # any of the conversion rules.
265
+ #
266
+ # You can define your own custom conversion rules by overriding
267
+ # the private methods #_make_magic_trigger and #make_magic_action
268
+ # in your class.
269
+ #
270
+ # NOTE: Additional default rules may be added in the future, but
271
+ # objects which match the existing rules will continue to match
272
+ # them. However, objects which are invalid in one version might
273
+ # become valid in future versions, if a new rule is added. So, you
274
+ # should never depend on ArgumentError being raised for a paricular
275
+ # object!
276
+ #
277
+ # Example:
278
+ #
279
+ # died_action = proc { |owner, event|
280
+ # owner.say "Blargh, I'm dead!" if event.who_died == owner
281
+ # }
282
+ #
283
+ # player.make_magic_hooks( :space => :jump,
284
+ # :left => :move_left,
285
+ # :right => :move_right,
286
+ # :mouse_left => :shoot,
287
+ # DiedEvent => died_action )
288
+ #
289
+ def make_magic_hooks( hash )
290
+ hash.collect do |trigger, action|
291
+ append_hook( :trigger => _make_magic_trigger( trigger ),
292
+ :action => _make_magic_action( action ))
293
+ end
294
+ end
295
+
296
+ # Exactly like #append_hook, except that the hook is put at the
297
+ # top of the stack (it will be handled first).
298
+ #
299
+ # See also EventHandler#prepend_hook.
300
+ #
301
+ def prepend_hook( hook )
302
+ hook = _prepare_hook( hook )
303
+ @event_handler.prepend_hook( hook )
304
+ rescue NoMethodError
305
+ _make_event_handler
306
+ retry
307
+ end
308
+
309
+ # Remove the given EventHook instance from the stack, if it
310
+ # exists on the stack.
311
+ # See EventHandler#remove_hook for details and restrictions.
312
+ #
313
+ # Returns:: the hook that was removed, or nil if the hook did not
314
+ # exist on the stack.
315
+ #
316
+ def remove_hook( hook )
317
+ @event_handler.remove_hook( hook )
318
+ rescue NoMethodError
319
+ _make_event_handler
320
+ retry
321
+ end
322
+
323
+ private
324
+
325
+ # Sets @event_handler to a new EventHandler if needed.
326
+ def _make_event_handler
327
+ @event_handler = EventHandler.new() if @event_handler.nil?
328
+ end
329
+
330
+ # This method is called by #make_magic_hooks to convert an
331
+ # object into an event action instance. For example, when
332
+ # this method is given a Proc, it creates and returns a
333
+ # BlockAction using that Proc. See #make_magic_hooks for
334
+ # information about how other objects are converted.
335
+ #
336
+ # You can override this method in your own classes to
337
+ # define your own custom conversion rules. Example:
338
+ #
339
+ # class Player
340
+ # include Rubygame::EventHandler::HasEventHandler
341
+ #
342
+ # private
343
+ #
344
+ # def _make_magic_action( action )
345
+ # if( action == :move_left )
346
+ # return BlockAction.new { |owner, event|
347
+ # owner.move_by( [-1, 0] )
348
+ # }
349
+ # else
350
+ # super
351
+ # end
352
+ # end
353
+ #
354
+ # end
355
+ #
356
+ #
357
+ # Returns:: an event action instance
358
+ #
359
+ # May raise:: ArgumentError, if the given object does not
360
+ # match any of the conversion rules.
361
+ #
362
+ def _make_magic_action( action )
363
+ case action
364
+
365
+ when Symbol
366
+ EventActions::MethodAction.new(action)
367
+
368
+ when Proc, Method
369
+ EventActions::BlockAction.new(&action)
370
+
371
+ else
372
+ if action.respond_to? :perform
373
+ action.dup
374
+ else
375
+ raise( ArgumentError,
376
+ "invalid action '#{action.inspect}'. " +\
377
+ "See HasEventHandler#make_magic_hooks docs for " +\
378
+ "allowed action types." )
379
+ end
380
+ end
381
+ end
382
+
383
+
384
+ # This method is called by #make_magic_hooks to convert an
385
+ # object into an event trigger instance. For example, when
386
+ # this method is given the symbol :mouse_left, it creates
387
+ # and returns a MousePressTrigger that matches :mouse_left.
388
+ # See #make_magic_hooks for information about how other objects
389
+ # are converted.
390
+ #
391
+ # You can override this method in your own classes to
392
+ # define your own custom conversion rules. Example:
393
+ #
394
+ # class Player
395
+ # include Rubygame::EventHandler::HasEventHandler
396
+ #
397
+ # private
398
+ #
399
+ # def _make_magic_trigger( trigger )
400
+ # if( trigger == :game_over )
401
+ # return GameOverTrigger.new()
402
+ # else
403
+ # super
404
+ # end
405
+ # end
406
+ #
407
+ # end
408
+ #
409
+ #
410
+ # Returns:: an event trigger instance
411
+ #
412
+ # May raise:: ArgumentError, if the given object does not
413
+ # match any of the conversion rules.
414
+ #
415
+ def _make_magic_trigger( trigger )
416
+ case trigger
417
+
418
+ when Symbol
419
+ case(trigger.to_s)
420
+ when /mouse/
421
+ EventTriggers::MousePressTrigger.new(trigger)
422
+ else
423
+ EventTriggers::KeyPressTrigger.new(trigger)
424
+ end
425
+
426
+ when Class
427
+ EventTriggers::InstanceOfTrigger.new(trigger)
428
+
429
+ else
430
+ if trigger.respond_to? :match?
431
+ trigger.dup
432
+ else
433
+ raise( ArgumentError,
434
+ "invalid trigger '#{trigger.inspect}'. " +\
435
+ "See HasEventHandler#make_magic_hooks docs for " +\
436
+ "allowed trigger types." )
437
+ end
438
+ end
439
+ end
440
+
441
+
442
+ def _prepare_hook( hook )
443
+ if( hook.kind_of? Hash )
444
+ hook = EventHook.new( {:owner => self}.merge(hook) )
445
+ end
446
+
447
+ if( hook.owner == nil )
448
+ hook = hook.dup
449
+ hook.owner = self
450
+ end
451
+
452
+ return hook
453
+ end
454
+ end