ray 0.0.0.pre2 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +3 -0
  3. data/README.md +62 -0
  4. data/Rakefile +33 -23
  5. data/VERSION +1 -1
  6. data/ext/audio.c +473 -0
  7. data/ext/color.c +4 -4
  8. data/ext/event.c +25 -3
  9. data/ext/extconf.rb +35 -22
  10. data/ext/font.c +287 -0
  11. data/ext/image.c +682 -33
  12. data/ext/joystick.c +9 -9
  13. data/ext/ray.c +166 -55
  14. data/ext/ray.h +120 -9
  15. data/ext/ray_osx.m +161 -0
  16. data/ext/rect.c +31 -4
  17. data/lib/ray/audio.rb +52 -0
  18. data/lib/ray/color.rb +16 -0
  19. data/lib/ray/dsl.rb +1 -3
  20. data/lib/ray/dsl/event.rb +1 -39
  21. data/lib/ray/dsl/event_listener.rb +38 -0
  22. data/lib/ray/dsl/event_runner.rb +3 -1
  23. data/lib/ray/dsl/event_translator.rb +74 -8
  24. data/lib/ray/dsl/handler.rb +3 -33
  25. data/lib/ray/dsl/matcher.rb +129 -23
  26. data/lib/ray/font.rb +108 -0
  27. data/lib/ray/font_set.rb +37 -0
  28. data/lib/ray/game.rb +171 -34
  29. data/lib/ray/helper.rb +43 -5
  30. data/lib/ray/image.rb +90 -3
  31. data/lib/ray/image_set.rb +35 -0
  32. data/lib/ray/joystick.rb +30 -0
  33. data/lib/ray/music_set.rb +35 -0
  34. data/lib/ray/ray.rb +17 -9
  35. data/lib/ray/rect.rb +51 -0
  36. data/lib/ray/resource_set.rb +92 -0
  37. data/lib/ray/scene.rb +220 -51
  38. data/lib/ray/sound_set.rb +35 -0
  39. data/lib/ray/sprite.rb +184 -0
  40. data/psp/ext.c +4 -0
  41. data/samples/hello_world/hello.rb +35 -0
  42. data/samples/hello_world/hello_dsl.rb +24 -0
  43. data/samples/pong/pong.rb +128 -0
  44. data/samples/sokoban/level_1 +7 -0
  45. data/samples/sokoban/sokoban.rb +370 -0
  46. data/spec/ray/audio_spec.rb +146 -0
  47. data/spec/ray/color_spec.rb +13 -0
  48. data/spec/ray/event_spec.rb +57 -168
  49. data/spec/ray/font_spec.rb +93 -0
  50. data/spec/ray/image_set_spec.rb +48 -0
  51. data/spec/ray/image_spec.rb +130 -44
  52. data/spec/ray/joystick_spec.rb +13 -9
  53. data/spec/ray/matcher_spec.rb +32 -55
  54. data/spec/ray/ray_spec.rb +33 -31
  55. data/spec/ray/rect_spec.rb +80 -0
  56. data/spec/ray/resource_set_spec.rb +105 -0
  57. data/spec/ray/sprite_spec.rb +163 -0
  58. data/spec/res/VeraMono.ttf +0 -0
  59. data/spec/res/aqua2.bmp +0 -0
  60. data/spec/res/pop.wav +0 -0
  61. data/spec/spec.opts +4 -0
  62. data/spec/spec_helper.rb +8 -0
  63. data/yard_ext.rb +91 -0
  64. metadata +104 -38
  65. data/bin/ray +0 -5
  66. data/bin/ray_irb +0 -4
  67. data/ext/SDLMain.h +0 -17
  68. data/ext/SDLMain.m +0 -381
  69. data/lib/ray/config.rb +0 -84
  70. data/lib/ray/dsl/converter.rb +0 -65
  71. data/lib/ray/dsl/listener.rb +0 -30
  72. data/lib/ray/dsl/type.rb +0 -58
  73. data/spec/ray/config_spec.rb +0 -90
  74. data/spec/ray/conversion_spec.rb +0 -43
  75. data/spec/ray/type_spec.rb +0 -17
  76. data/spec_runner.rb +0 -27
data/psp/ext.c CHANGED
@@ -208,6 +208,8 @@ VALUE Wlan_ip(VALUE self) {
208
208
  Inits a builtin module. You should never call it by yourself.
209
209
  This avoids to load unneeded module from the stdlib (Ray's are all loaded
210
210
  anyway). For instance, the zlib library needs to be enabled by this function.
211
+
212
+ @note This method is only available on the PSP.
211
213
  */
212
214
  VALUE ray_init_internal(VALUE self, VALUE module_name) {
213
215
  VALUE rb_str = rb_obj_as_string(module_name);
@@ -265,6 +267,8 @@ VALUE ray_init_internal(VALUE self, VALUE module_name) {
265
267
 
266
268
  Once you're connected, everything is transparent: you can use sockets as
267
269
  usual.
270
+
271
+ @note This module is only available on the PSP.
268
272
  */
269
273
  void Init_ray_psp() {
270
274
  rb_define_module_function(ray_mRay, "init_internal", ray_init_internal, 1);
@@ -0,0 +1,35 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../ext")
3
+
4
+ def path_of(res)
5
+ File.expand_path(File.dirname(__FILE__) + "/../../spec/res/#{res}")
6
+ end
7
+
8
+ require 'ray'
9
+
10
+ class HelloScene < Ray::Scene
11
+ scene_name :hello
12
+
13
+ def setup
14
+ @font = font(path_of("VeraMono.ttf"), 12)
15
+ end
16
+
17
+ def render(win)
18
+ @font.draw("Hello world!", :on => win, :at => [0, 0])
19
+ end
20
+ end
21
+
22
+ class HelloWorld < Ray::Game
23
+ def initialize
24
+ super("Hello world!")
25
+
26
+ HelloScene.bind(self)
27
+ push_scene :hello
28
+ end
29
+
30
+ def register
31
+ add_hook :quit, method(:exit!)
32
+ end
33
+ end
34
+
35
+ HelloWorld.new.run
@@ -0,0 +1,24 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../ext")
3
+
4
+ def path_of(res)
5
+ File.expand_path(File.dirname(__FILE__) + "/../../spec/res/#{res}")
6
+ end
7
+
8
+ require 'ray'
9
+
10
+ Ray::Game.new("Hello world!") do
11
+ register do
12
+ add_hook :quit, method(:exit!)
13
+ end
14
+
15
+ scene :hello do
16
+ @font = font(path_of("VeraMono.ttf"), 12)
17
+
18
+ render do |win|
19
+ @font.draw("Hello world", :on => win, :at => [0, 0])
20
+ end
21
+ end
22
+
23
+ push_scene :hello
24
+ end
@@ -0,0 +1,128 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../ext")
3
+
4
+ def path_of(res)
5
+ File.expand_path(File.dirname(__FILE__) + "/../../spec/res/#{res}")
6
+ end
7
+
8
+ require 'ray'
9
+
10
+ module Pong
11
+ class Scene < Ray::Scene
12
+ scene_name :pong_scene
13
+
14
+ def setup
15
+ @font = font(path_of("VeraMono.ttf"), 12)
16
+
17
+ player_img = Ray::Image.new(:w => 20, :h => 50)
18
+ player_img.draw_filled_rect(Ray::Rect.new(0, 0, 20, 50), Ray::Color.blue)
19
+
20
+ adv_img = Ray::Image.new(:w => 20, :h => 50)
21
+ adv_img.draw_filled_rect(Ray::Rect.new(0, 0, 20, 50), Ray::Color.red)
22
+
23
+ ball_img = Ray::Image.new(:w => 10, :h => 10)
24
+ ball_img.draw_filled_circle([5, 5], 5, Ray::Color.white)
25
+
26
+ @player = sprite(player_img)
27
+ @adv = sprite(adv_img)
28
+ @ball = sprite(ball_img)
29
+
30
+ screen = Ray.screen
31
+ width, height = screen.w, screen.h
32
+
33
+ @screen_rect = Ray::Rect.new(0, 0, width, height)
34
+
35
+ @player.y = (height / 2) - 25
36
+ @adv.y = (height / 2) - 25
37
+
38
+ @player.x = 30
39
+ @adv.x = width - 30
40
+
41
+ reset_ball
42
+
43
+ @scores = {:player => 0, :adv => 0}
44
+ end
45
+
46
+ def reset_ball
47
+ @ball.y = (@screen_rect.h / 2) - 5
48
+ @ball.x = (@screen_rect.w / 2) - 5
49
+
50
+ @ball_movement = [6, 6]
51
+ end
52
+
53
+ def register
54
+ self.loops_per_second = 60
55
+
56
+ on :point_gained do |by|
57
+ @scores[by] += 1
58
+ reset_ball
59
+ end
60
+
61
+ always do
62
+ if holding? key(:down)
63
+ @player.y += 4
64
+ @player.y -= 4 unless @player.inside? @screen_rect
65
+ elsif holding? key(:up)
66
+ @player.y -= 4
67
+ @player.y += 4 unless @player.inside? @screen_rect
68
+ end
69
+
70
+ if @ball.x >= @screen_rect.w
71
+ raise_event(:point_gained, :player)
72
+ elsif @ball.x <= 0
73
+ raise_event(:point_gained, :adv)
74
+ end
75
+
76
+ if @ball.collide?(@player) || @ball.collide?(@adv) ||
77
+ @ball.y >= @screen_rect.h || @ball.y <= 0
78
+ @ball_movement[-1] *= -1
79
+ @ball.y += @ball_movement.last
80
+ end
81
+
82
+ if @ball.collide?(@player) || @ball.collide?(@adv)
83
+ @ball_movement[0] *= -1
84
+ @ball.x += @ball_movement.first
85
+ end
86
+
87
+ adv_center = @adv.y + (@adv.image.h / 2)
88
+ if @ball.y > adv_center
89
+ @adv.y += 4
90
+ elsif @ball.y < adv_center
91
+ @adv.y -= 4
92
+ end
93
+
94
+ @ball.x += @ball_movement.first
95
+ @ball.y += @ball_movement.last
96
+
97
+ need_render!
98
+ end
99
+ end
100
+
101
+ def render(win)
102
+ win.fill(Ray::Color.black)
103
+
104
+ @player.draw
105
+ @adv.draw
106
+ @ball.draw
107
+
108
+ @font.draw("#{@scores[:player]} - #{@scores[:adv]}",
109
+ :at => [0, 0], :on => win,
110
+ :color => Ray::Color.white)
111
+ end
112
+ end
113
+
114
+ class Game < Ray::Game
115
+ def initialize
116
+ super("Pong")
117
+
118
+ Scene.bind(self)
119
+ push_scene :pong_scene
120
+ end
121
+
122
+ def register
123
+ add_hook :quit, method(:exit!)
124
+ end
125
+ end
126
+ end
127
+
128
+ Pong::Game.new.run
@@ -0,0 +1,7 @@
1
+ ####
2
+ # ###
3
+ # $$ #
4
+ ##... #
5
+ # @$ #
6
+ # ###
7
+ #####
@@ -0,0 +1,370 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../lib")
2
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../ext")
3
+
4
+ require 'ray'
5
+
6
+ module Sokoban
7
+ class InvalidLevel < StandardError; end
8
+
9
+ class Level
10
+ include Ray::Helper
11
+ include Enumerable
12
+
13
+ def self.open(filename)
14
+ level = Kernel.open(filename) { |io| new(io) }
15
+
16
+ if block_given?
17
+ yield level
18
+ else
19
+ level
20
+ end
21
+ end
22
+
23
+ def initialize(io_or_string)
24
+ @solved = false
25
+ @moves = []
26
+
27
+ @content = io_or_string.is_a?(String) ? io_or_string : io_or_string.read
28
+ parse_content
29
+ end
30
+
31
+ def [](x, y)
32
+ return :empty if x < 0 || y < 0
33
+
34
+ line = @objects[y]
35
+ return :empty unless line
36
+
37
+ line[x] || :empty
38
+ end
39
+
40
+ def []=(x, y, value)
41
+ @objects[y][x] = value
42
+ end
43
+
44
+ attr_reader :character_pos
45
+
46
+ def can_move?(direction)
47
+ obj = next_obj = nil
48
+ obj, next_obj = next_objects(direction)
49
+
50
+ return true if obj == :empty || obj == :storage
51
+ return false if obj == :wall
52
+
53
+ # obj is a crate
54
+ return next_obj == :empty || next_obj == :storage
55
+ end
56
+
57
+ def move(direction)
58
+ make_move(direction)
59
+ end
60
+
61
+ def each(&block)
62
+ return to_enum unless block_given?
63
+
64
+ @objects.each do |ary|
65
+ ary.each(&block)
66
+ end
67
+ end
68
+
69
+ def each_with_pos
70
+ return to_enum(:each_with_pos) unless block_given?
71
+
72
+ @objects.each_with_index do |ary, y|
73
+ ary.each_with_index do |obj, x|
74
+ yield obj, x, y
75
+ end
76
+ end
77
+ end
78
+
79
+ def reset
80
+ @moves.clear
81
+ parse_content
82
+ end
83
+
84
+ def undo
85
+ return if @moves.empty?
86
+
87
+ move = @moves.last
88
+ opposite_dir = case move[:direction]
89
+ when :left then :right
90
+ when :right then :left
91
+ when :up then :down
92
+ when :down then :up
93
+ end
94
+
95
+ x, y = character_pos
96
+ old_pos, = next_positions(move[:direction])
97
+ crate, = next_objects(move[:direction])
98
+
99
+ replacement = (self[x, y] == :man_on_storage) ? :crate_on_storage : :crate
100
+
101
+ make_move(opposite_dir, false, false)
102
+
103
+ if move[:moved_crate]
104
+ if crate == :crate
105
+ self[*old_pos] = :empty
106
+ self[x, y] = replacement
107
+ else
108
+ self[*old_pos] = :storage
109
+ self[x, y] = replacement
110
+ end
111
+ end
112
+
113
+ @moves.delete_at(-1)
114
+ check_solved
115
+ end
116
+
117
+ def score
118
+ @moves.size
119
+ end
120
+
121
+ def solved?
122
+ @solved
123
+ end
124
+
125
+ private
126
+ def char_to_object(char)
127
+ case char
128
+ when "@" then :man
129
+ when "o", "$" then :crate
130
+ when "#" then :wall
131
+ when "." then :storage
132
+ when "*" then :crate_on_storage
133
+ when "+" then :man_on_storage
134
+ when " " then :empty
135
+ else
136
+ raise InvalidLevel, "'#{char}' isn't a valid level character"
137
+ end
138
+ end
139
+
140
+ def find_character_pos
141
+ @objects.each_with_index do |ary, y|
142
+ ary.each_with_index do |obj, x|
143
+ return [x, y] if obj == :man || obj == :man_on_storage
144
+ end
145
+ end
146
+ end
147
+
148
+ def parse_content
149
+ @objects = []
150
+
151
+ @content.each_line do |line|
152
+ @objects << []
153
+
154
+ line.chomp.each_char do |char|
155
+ @objects.last << char_to_object(char)
156
+ end
157
+ end
158
+
159
+ @character_pos = find_character_pos
160
+ self
161
+ end
162
+
163
+ def check_solved
164
+ if !include? :crate
165
+ @solved = true
166
+ raise_event(:level_solved, self)
167
+ else
168
+ @solved = false
169
+ end
170
+ end
171
+
172
+ def next_positions(direction)
173
+ x, y = character_pos
174
+
175
+ case direction
176
+ when :left
177
+ (1..2).map { |i| [x - i, y] }
178
+ when :right
179
+ (1..2).map { |i| [x + i, y] }
180
+ when :up
181
+ (1..2).map { |i| [x, y - i] }
182
+ when :down
183
+ (1..2).map { |i| [x, y + i] }
184
+ end
185
+ end
186
+
187
+ def next_objects(direction)
188
+ next_positions(direction).map { |(x, y)| self[x, y] }
189
+ end
190
+
191
+ def make_move(direction, check_for_solve = true, count_move = true)
192
+ x, y = character_pos
193
+ obj, next_obj = next_objects(direction)
194
+ first_pos, sec_pos = next_positions(direction)
195
+
196
+ on_storage = (self[x, y] == :man_on_storage)
197
+ replacement = on_storage ? :storage : :empty
198
+
199
+ @moves << {
200
+ :direction => direction,
201
+ :moved_crate => obj == :crate || obj == :crate_on_storage
202
+ } if count_move
203
+
204
+ if obj == :empty
205
+ self[*first_pos] = :man
206
+ self[x, y] = replacement
207
+
208
+ @character_pos = first_pos
209
+ elsif obj == :storage
210
+ self[*first_pos] = :man_on_storage
211
+ self[x, y] = replacement
212
+
213
+ @character_pos = first_pos
214
+ elsif obj == :crate
215
+ if next_obj == :empty
216
+ self[*sec_pos] = :crate
217
+ self[*first_pos] = :man
218
+ self[x, y] = replacement
219
+
220
+ @character_pos = first_pos
221
+ elsif next_obj == :storage
222
+ self[*sec_pos] = :crate_on_storage
223
+ self[*first_pos] = :man
224
+ self[x, y] = replacement
225
+
226
+ @character_pos = first_pos
227
+ end
228
+ elsif obj == :crate_on_storage
229
+ @moves[-1][:moved_crate] = true
230
+
231
+ if next_obj == :empty
232
+ self[*sec_pos] = :crate
233
+ self[*first_pos] = :man_on_storage
234
+ self[x, y] = replacement
235
+
236
+ @character_pos = first_pos
237
+ elsif next_obj == :storage
238
+ self[*sec_pos] = :crate_on_storage
239
+ self[*first_pos] = :man_on_storage
240
+ self[x, y] = replacement
241
+
242
+ @character_pos = first_pos
243
+ end
244
+ end
245
+
246
+ check_solved if check_for_solve
247
+ end
248
+ end
249
+
250
+ class LevelScene < Ray::Scene
251
+ scene_name :sokoban_level
252
+
253
+ TILE_WIDTH = 32
254
+
255
+ def setup(filename)
256
+ @filename = filename
257
+ @level = Level.open(filename)
258
+
259
+ self.loops_per_second = 60
260
+ end
261
+
262
+ def register
263
+ @level.event_runner = event_runner
264
+
265
+ [:left, :right, :up, :down].each do |dir|
266
+ on :key_press, key(dir) do
267
+ next if @level.solved?
268
+
269
+ if @level.can_move?(dir)
270
+ @level.move(dir)
271
+ need_render!
272
+ end
273
+ end
274
+ end
275
+
276
+ on :key_press, key(:r) do
277
+ next if @level.solved?
278
+
279
+ @level.reset
280
+ need_render!
281
+ end
282
+
283
+ on :key_press, key(:u) do
284
+ next if @level.solved?
285
+
286
+ @level.undo
287
+ need_render!
288
+ end
289
+
290
+ on :level_solved do
291
+ puts "Level solved!"
292
+ pop_scene
293
+ end
294
+ end
295
+
296
+ def render(win)
297
+ reset_window(win)
298
+
299
+ @level.each_with_pos do |obj, x, y|
300
+ case obj
301
+ when :man then draw_man(win, x, y)
302
+ when :crate then draw_crate(win, x, y)
303
+ when :wall then draw_wall(win, x, y)
304
+ when :storage then draw_storage(win, x, y)
305
+ when :crate_on_storage then draw_crate_on_storage(win, x, y)
306
+ when :man_on_storage then draw_man_on_storage(win, x, y)
307
+ when :empty then draw_empty_tile(win, x, y)
308
+ end
309
+ end
310
+ end
311
+
312
+ def reset_window(win)
313
+ win.fill(Ray::Color.black)
314
+ end
315
+
316
+ def draw_man(win, x, y)
317
+ x, y = pos_to_coord(x, y)
318
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.white)
319
+ end
320
+
321
+ def draw_crate(win, x, y)
322
+ x, y = pos_to_coord(x, y)
323
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.gray)
324
+ end
325
+
326
+ def draw_wall(win, x, y)
327
+ x, y = pos_to_coord(x, y)
328
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.new(91, 59, 17))
329
+ end
330
+
331
+ def draw_storage(win, x, y)
332
+ x, y = pos_to_coord(x, y)
333
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.yellow)
334
+ end
335
+
336
+ def draw_crate_on_storage(win, x, y)
337
+ x, y = pos_to_coord(x, y)
338
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.green)
339
+ end
340
+
341
+ def draw_man_on_storage(win, x, y)
342
+ x, y = pos_to_coord(x, y)
343
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.red)
344
+ end
345
+
346
+ def draw_empty_tile(win, x, y)
347
+ x, y = pos_to_coord(x, y)
348
+ win.draw_filled_rect([x, y, TILE_WIDTH, TILE_WIDTH], Ray::Color.black)
349
+ end
350
+
351
+ def pos_to_coord(x, y)
352
+ [x * TILE_WIDTH, y * TILE_WIDTH]
353
+ end
354
+ end
355
+
356
+ class Game < Ray::Game
357
+ def initialize
358
+ super("Sokoban")
359
+
360
+ LevelScene.bind(self)
361
+ push_scene(:sokoban_level, File.expand_path(File.join(File.dirname(__FILE__), "level_1")))
362
+ end
363
+
364
+ def register
365
+ add_hook :quit, method(:exit!)
366
+ end
367
+ end
368
+ end
369
+
370
+ Sokoban::Game.new.run