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.
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/README.md +62 -0
- data/Rakefile +33 -23
- data/VERSION +1 -1
- data/ext/audio.c +473 -0
- data/ext/color.c +4 -4
- data/ext/event.c +25 -3
- data/ext/extconf.rb +35 -22
- data/ext/font.c +287 -0
- data/ext/image.c +682 -33
- data/ext/joystick.c +9 -9
- data/ext/ray.c +166 -55
- data/ext/ray.h +120 -9
- data/ext/ray_osx.m +161 -0
- data/ext/rect.c +31 -4
- data/lib/ray/audio.rb +52 -0
- data/lib/ray/color.rb +16 -0
- data/lib/ray/dsl.rb +1 -3
- data/lib/ray/dsl/event.rb +1 -39
- data/lib/ray/dsl/event_listener.rb +38 -0
- data/lib/ray/dsl/event_runner.rb +3 -1
- data/lib/ray/dsl/event_translator.rb +74 -8
- data/lib/ray/dsl/handler.rb +3 -33
- data/lib/ray/dsl/matcher.rb +129 -23
- data/lib/ray/font.rb +108 -0
- data/lib/ray/font_set.rb +37 -0
- data/lib/ray/game.rb +171 -34
- data/lib/ray/helper.rb +43 -5
- data/lib/ray/image.rb +90 -3
- data/lib/ray/image_set.rb +35 -0
- data/lib/ray/joystick.rb +30 -0
- data/lib/ray/music_set.rb +35 -0
- data/lib/ray/ray.rb +17 -9
- data/lib/ray/rect.rb +51 -0
- data/lib/ray/resource_set.rb +92 -0
- data/lib/ray/scene.rb +220 -51
- data/lib/ray/sound_set.rb +35 -0
- data/lib/ray/sprite.rb +184 -0
- data/psp/ext.c +4 -0
- data/samples/hello_world/hello.rb +35 -0
- data/samples/hello_world/hello_dsl.rb +24 -0
- data/samples/pong/pong.rb +128 -0
- data/samples/sokoban/level_1 +7 -0
- data/samples/sokoban/sokoban.rb +370 -0
- data/spec/ray/audio_spec.rb +146 -0
- data/spec/ray/color_spec.rb +13 -0
- data/spec/ray/event_spec.rb +57 -168
- data/spec/ray/font_spec.rb +93 -0
- data/spec/ray/image_set_spec.rb +48 -0
- data/spec/ray/image_spec.rb +130 -44
- data/spec/ray/joystick_spec.rb +13 -9
- data/spec/ray/matcher_spec.rb +32 -55
- data/spec/ray/ray_spec.rb +33 -31
- data/spec/ray/rect_spec.rb +80 -0
- data/spec/ray/resource_set_spec.rb +105 -0
- data/spec/ray/sprite_spec.rb +163 -0
- data/spec/res/VeraMono.ttf +0 -0
- data/spec/res/aqua2.bmp +0 -0
- data/spec/res/pop.wav +0 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +8 -0
- data/yard_ext.rb +91 -0
- metadata +104 -38
- data/bin/ray +0 -5
- data/bin/ray_irb +0 -4
- data/ext/SDLMain.h +0 -17
- data/ext/SDLMain.m +0 -381
- data/lib/ray/config.rb +0 -84
- data/lib/ray/dsl/converter.rb +0 -65
- data/lib/ray/dsl/listener.rb +0 -30
- data/lib/ray/dsl/type.rb +0 -58
- data/spec/ray/config_spec.rb +0 -90
- data/spec/ray/conversion_spec.rb +0 -43
- data/spec/ray/type_spec.rb +0 -17
- data/spec_runner.rb +0 -27
data/lib/ray/dsl/handler.rb
CHANGED
@@ -1,25 +1,9 @@
|
|
1
1
|
module Ray
|
2
2
|
module DSL
|
3
|
-
#
|
4
|
-
# to know it exist. When you say on :foo do something end a handler is
|
5
|
-
# created, so that your EventRunner can see if you are interested in
|
6
|
-
# a specific event.
|
3
|
+
# Used internally to call blocks registred with Ray::DSL::EventListener#on.
|
7
4
|
class Handler
|
8
5
|
def initialize(type, args, block)
|
9
6
|
@type, @args, @block = type, args, block
|
10
|
-
|
11
|
-
if desc = Ray.description_for_event(@type)
|
12
|
-
desc.each_with_index do |type, i|
|
13
|
-
next if @args[i].is_a? Matcher
|
14
|
-
next if @args[i].is_a? Regexp
|
15
|
-
|
16
|
-
begin
|
17
|
-
@args[i] = Ray.convert(@args[i], type)
|
18
|
-
rescue TypeError
|
19
|
-
return
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
7
|
end
|
24
8
|
|
25
9
|
def match?(event)
|
@@ -43,25 +27,11 @@ module Ray
|
|
43
27
|
|
44
28
|
private
|
45
29
|
def match_args?(args)
|
46
|
-
return false if @args.size
|
30
|
+
return false if @args.size > args.size
|
47
31
|
|
48
32
|
@args.each_with_index do |elem, i|
|
49
33
|
other = args[i]
|
50
|
-
|
51
|
-
case elem
|
52
|
-
when Ray::DSL::Matcher
|
53
|
-
return false unless elem.match?(other)
|
54
|
-
when Regexp
|
55
|
-
if other.is_a? Regexp
|
56
|
-
return false unless elem == other
|
57
|
-
elsif other.is_a? String
|
58
|
-
return false unless elem =~ other
|
59
|
-
else
|
60
|
-
return false
|
61
|
-
end
|
62
|
-
else
|
63
|
-
return false unless elem == other
|
64
|
-
end
|
34
|
+
return false unless (elem === args[i]) || (elem == args[i])
|
65
35
|
end
|
66
36
|
|
67
37
|
return true
|
data/lib/ray/dsl/matcher.rb
CHANGED
@@ -1,24 +1,29 @@
|
|
1
1
|
module Ray
|
2
2
|
# This is the module including all of your matchers as private methods,
|
3
3
|
# allowing you to use them when you call on.
|
4
|
-
module Matchers
|
4
|
+
module Matchers
|
5
|
+
# @return [DSL::Matcher] An anonymous matcher, using your block to
|
6
|
+
# know if the argument matches.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# on :foo, where { |i| i > 10 } do |i|
|
10
|
+
# puts "#{i} is greater than 10!"
|
11
|
+
# end
|
12
|
+
def where(&block)
|
13
|
+
DSL::Matcher.new { |o| block.call(o) }
|
14
|
+
end
|
15
|
+
end
|
5
16
|
|
6
17
|
module DSL
|
7
18
|
class Matcher
|
8
|
-
def initialize(
|
9
|
-
@target = Ray.resolve_type(target)
|
19
|
+
def initialize(&block)
|
10
20
|
@block = block
|
11
21
|
end
|
12
22
|
|
13
|
-
# @return [true, false] True if we can match on the object of that class
|
14
|
-
def can_match_on?(klass)
|
15
|
-
Ray.resolve_type(klass).ancestors.include? @target
|
16
|
-
end
|
17
|
-
|
18
23
|
# @return [true, false] True if the block this object was created with
|
19
24
|
# returns true when called with obj.
|
20
25
|
def match?(obj)
|
21
|
-
|
26
|
+
@block.call(obj)
|
22
27
|
end
|
23
28
|
|
24
29
|
alias :=== :match?
|
@@ -28,7 +33,6 @@ module Ray
|
|
28
33
|
# Describes a new matcher.
|
29
34
|
#
|
30
35
|
# @param [Symbol] name The name you'll use to call your matcher
|
31
|
-
# @param [Symbol, Module] target the type on which the matcher operates.
|
32
36
|
# @param [Proc] create_block a block called with the arguments of your matcher
|
33
37
|
# method, and returning the block that will be used
|
34
38
|
# to check if the condition is matched.
|
@@ -36,25 +40,127 @@ module Ray
|
|
36
40
|
# Ray.describe_matcher(:match, :string) do |regex|
|
37
41
|
# lambda { |str| str =~ regex }
|
38
42
|
# end
|
39
|
-
def self.describe_matcher(name,
|
43
|
+
def self.describe_matcher(name, &create_block)
|
40
44
|
Matchers.module_eval do
|
41
|
-
define_method(name) do |*args
|
42
|
-
DSL::Matcher.new(
|
45
|
+
define_method(name) do |*args|
|
46
|
+
DSL::Matcher.new(&create_block.call(*args))
|
43
47
|
end
|
44
|
-
|
45
|
-
private name
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
51
|
+
# @return [DSL::Matcher] A matcher matching anything (always true)
|
52
|
+
describe_matcher(:anything) do
|
53
|
+
lambda { |o| true }
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [DSL::Matcher] A matcher matching anything greater than x
|
57
|
+
# (comparaison using >)
|
58
|
+
describe_matcher(:more_than) do |x|
|
59
|
+
lambda { |o| o > x }
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [DSL::Matcher] A matcher matching anything that is less than x
|
63
|
+
# (comparaison using <)
|
64
|
+
describe_matcher(:less_than) do |x|
|
65
|
+
lambda { |o| o < x }
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [DSL::Matcher] A matcher matching a value close of x.
|
69
|
+
# @note the maximum and the minimum will only be computed once.
|
52
70
|
#
|
53
71
|
# @example
|
54
|
-
# on :
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
lambda { |o|
|
72
|
+
# on :win, almost(10_000, 500) do ... end
|
73
|
+
# on :win, where { |x| x <= 10_000 + 500 && x >= 10_000 - 500 } do ... end
|
74
|
+
describe_matcher(:almost) do |x, precision|
|
75
|
+
min, max = (x - precision), (x + precision)
|
76
|
+
lambda { |o| (o <= max) && (o >= min) }
|
77
|
+
end
|
78
|
+
|
79
|
+
# @overload inside(x, y[, w, h])
|
80
|
+
# @overload inside(rect)
|
81
|
+
# @overload inside(array)
|
82
|
+
#
|
83
|
+
# @return [DSL::Matcher] A matching matching any rect inside the argument.
|
84
|
+
describe_matcher(:inside) do |*args|
|
85
|
+
rect = args.size > 1 ? Ray::Rect.new(*args) : args.first.to_rect
|
86
|
+
lambda { |o| o.inside? rect }
|
87
|
+
end
|
88
|
+
|
89
|
+
# @overload outside(x, y[, w, h])
|
90
|
+
# @overload outside(rect)
|
91
|
+
# @overload outside(array)
|
92
|
+
#
|
93
|
+
# @return [DSL::Matcher] A matching matching any rect outside the argument.
|
94
|
+
describe_matcher(:outside) do |*args|
|
95
|
+
rect = args.size > 1 ? Ray::Rect.new(*args) : args.first.to_rect
|
96
|
+
lambda { |o| o.outside? rect }
|
97
|
+
end
|
98
|
+
|
99
|
+
# @overload colliding_with(x, y[, w, h])
|
100
|
+
# @overload colliding_with(rect)
|
101
|
+
# @overload colliding_with(array)
|
102
|
+
#
|
103
|
+
# @return [DSL::Matcher] A matching matching any rect colliding with the
|
104
|
+
# argument.
|
105
|
+
describe_matcher(:colliding_with) do |*args|
|
106
|
+
rect = args.size > 1 ? Ray::Rect.new(*args) : args.first.to_rect
|
107
|
+
lambda { |o| o.collide? rect }
|
108
|
+
end
|
109
|
+
|
110
|
+
KEYS = Ray::Event.constants.inject({}) do |hash, const|
|
111
|
+
if const =~ /^KEY_(.+)$/
|
112
|
+
hash[$1.downcase.to_sym] = [Ray::Event.const_get(const)]
|
113
|
+
elsif const =~ /^PSP_BUTTON_(.+)$/
|
114
|
+
hash["psp_#{$1.downcase.to_sym}".to_sym] = [Ray::Event.const_get(const)]
|
115
|
+
end
|
116
|
+
|
117
|
+
hash
|
118
|
+
end
|
119
|
+
|
120
|
+
KEYS[:number] = Ray::Event.constants.select { |c| c =~ /^KEY_\d$/ }.map do |c|
|
121
|
+
Ray::Event.const_get(c)
|
122
|
+
end
|
123
|
+
|
124
|
+
KEYS[:number] |= Ray::Event.constants.select { |c| c =~ /^KEY_KP\d$/ }.map do |c|
|
125
|
+
Ray::Event.const_get(c)
|
126
|
+
end
|
127
|
+
|
128
|
+
KEYS[:letter] = Ray::Event.constants.select { |c| c =~ /^KEY_[a-z]$/ }.map do |c|
|
129
|
+
Ray::Event.const_get(c)
|
130
|
+
end
|
131
|
+
|
132
|
+
KEYS[:function] = Ray::Event.constants.select { |c| c =~ /^KEY_F\d$/ }.map do |c|
|
133
|
+
Ray::Event.const_get(c)
|
134
|
+
end
|
135
|
+
|
136
|
+
KEYS[:mod] = [Ray::Event::KEY_RSHIFT, Ray::Event::KEY_LSHIFT,
|
137
|
+
Ray::Event::KEY_RCTRL, Ray::Event::KEY_LCTRL,
|
138
|
+
Ray::Event::KEY_RALT, Ray::Event::KEY_LALT,
|
139
|
+
Ray::Event::KEY_RMETA, Ray::Event::KEY_LMETA,
|
140
|
+
Ray::Event::KEY_RSUPER, Ray::Event::KEY_LSUPER]
|
141
|
+
|
142
|
+
KEYS[:arrow] = [Ray::Event::KEY_UP, Ray::Event::KEY_DOWN,
|
143
|
+
Ray::Event::KEY_LEFT, Ray::Event::KEY_RIGHT]
|
144
|
+
|
145
|
+
MOD = Ray::Event.constants.inject({}) do |hash, const|
|
146
|
+
if const =~ /^KMOD_(.+)$/
|
147
|
+
hash[$1.downcase.to_sym] = [Ray::Event.const_get(const)]
|
148
|
+
end
|
149
|
+
|
150
|
+
hash
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [DSL::Matcher] A matcher matching the given key, which is a symbol
|
154
|
+
# like :space, :a, :b, :number, :letter, :arrow, ...
|
155
|
+
describe_matcher(:key) do |sym|
|
156
|
+
ary = KEYS[sym.to_sym]
|
157
|
+
lambda { |o| ary.include? o }
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [DSL::Matcher] A matcher matching the given modifier key
|
161
|
+
# (:rctrl, :lctrl, :rmeta, :lmeta, ...)
|
162
|
+
describe_matcher(:key_mod) do |sym|
|
163
|
+
ary = MOD[sym.to_sym]
|
164
|
+
lambda { |o| ary.detect { |const| o & const } }
|
59
165
|
end
|
60
166
|
end
|
data/lib/ray/font.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
module Ray
|
2
|
+
class Font
|
3
|
+
extend Ray::ResourceSet
|
4
|
+
need_argument_count 1
|
5
|
+
add_set(/^(.*)$/) { |filename, size| new(filename, size) }
|
6
|
+
|
7
|
+
# @return [true, false] True if the font has no style
|
8
|
+
def normal?
|
9
|
+
style == STYLE_NORMAL
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [true, false] True if the font is italic
|
13
|
+
def italic?
|
14
|
+
(style & STYLE_ITALIC) != 0
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [true, false] True if the font is bold
|
18
|
+
def bold?
|
19
|
+
(style & STYLE_BOLD) != 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [true, false] True if the font is underlined
|
23
|
+
def underlined?
|
24
|
+
(style & STYLE_UNDERLINE) != 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class String
|
30
|
+
# Draws the receiver.
|
31
|
+
#
|
32
|
+
# @option opts [Ray::Font] :font The font used to render the string.
|
33
|
+
# @option opts [Ray::Image] :on The image to draw on.
|
34
|
+
# @option opts [Integer] :w Witdh of the image. Also called :width.
|
35
|
+
# @option opts [Integer] ;h height of the image. Also called :height.
|
36
|
+
# @option opts [Symbol] The encoding. Can be guessed in Ruby 1.9.
|
37
|
+
# @option opts [Ray::Color] :color The color to draw the text in.
|
38
|
+
# @option opts [Ray::Color] :background Background color in shaded mode.
|
39
|
+
# @option opts [Symbol] :mode The drawing mode.
|
40
|
+
# @option opts [Array<Symbol>] :style The different styles to apply.
|
41
|
+
# :italic, :bold, and :underlined.
|
42
|
+
#
|
43
|
+
# @option opts [Array<Integer>] :at Where the image should be drawn.
|
44
|
+
# Defaults to (0, 0)
|
45
|
+
#
|
46
|
+
# @see Ray::Font#draw
|
47
|
+
def draw(opts = {})
|
48
|
+
font = opts[:font]
|
49
|
+
|
50
|
+
lines = split(/\r\n|\n|\r/)
|
51
|
+
line_skip = font.line_skip
|
52
|
+
|
53
|
+
target = opts[:on]
|
54
|
+
|
55
|
+
string_encoding = opts[:encoding]
|
56
|
+
string_encoding ||= if respond_to? :encoding # Ruby 1.9
|
57
|
+
case encoding.to_s
|
58
|
+
when /^utf-?8$/i
|
59
|
+
:utf8
|
60
|
+
when /^iso-8859-/i
|
61
|
+
:latin1
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
target ||= Ray::Image.new(:height => opts[:height] || opts[:h] ||
|
70
|
+
line_skip * lines.size,
|
71
|
+
:width => opts[:width] || opts[:w] ||
|
72
|
+
lines.map { |i|
|
73
|
+
font.size_of(self, string_encoding).width
|
74
|
+
}.max)
|
75
|
+
|
76
|
+
color = opts[:color]
|
77
|
+
background = opts[:background]
|
78
|
+
|
79
|
+
mode = opts[:mode]
|
80
|
+
|
81
|
+
if styles = opts[:style]
|
82
|
+
font.style = styles.inject(0) do |flags, style|
|
83
|
+
flags |= case style
|
84
|
+
when :italic
|
85
|
+
Ray::Font::STYLE_ITALIC
|
86
|
+
when :bold
|
87
|
+
Ray::Font::STYLE_BOLD
|
88
|
+
when :underlined
|
89
|
+
Ray::Font::STYLE_UNDERLINE
|
90
|
+
else
|
91
|
+
raise "Unknown flag #{style}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
x, y = opts[:at]
|
97
|
+
x ||= 0
|
98
|
+
y ||= 0
|
99
|
+
|
100
|
+
lines.each do |line|
|
101
|
+
font.draw(line, :on => target, :at => [x, y], :encoding => string_encoding,
|
102
|
+
:color => color, :background => background, :mode => mode)
|
103
|
+
y += line_skip
|
104
|
+
end
|
105
|
+
|
106
|
+
target
|
107
|
+
end
|
108
|
+
end
|
data/lib/ray/font_set.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Ray
|
2
|
+
module FontSet
|
3
|
+
extend Ray::ResourceSet
|
4
|
+
need_argument_count 1
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def missing_pattern(string, size)
|
8
|
+
Ray::Font[string, size]
|
9
|
+
end
|
10
|
+
|
11
|
+
def select!(&block)
|
12
|
+
super(&block)
|
13
|
+
Ray::Font.select!(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a new font set.
|
19
|
+
#
|
20
|
+
# @param [Regexp] regex Regular expression used to match file
|
21
|
+
# @yield [*args, size] Block returning the font
|
22
|
+
#
|
23
|
+
# @yieldparam args Regex captures
|
24
|
+
# @yieldparam size Size of the font
|
25
|
+
def self.font_set(regex, &block)
|
26
|
+
Ray::FontSet.add_set(regex, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'open-uri'
|
32
|
+
|
33
|
+
Ray.font_set(/^(http|ftp):\/\/(\S+)$/) do |protocol, address, size|
|
34
|
+
open("#{protocol}://#{address}") { |io| Ray::Font.new(io, size) }
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
end
|
data/lib/ray/game.rb
CHANGED
@@ -1,29 +1,93 @@
|
|
1
1
|
module Ray
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# Games are used to manage different scenes. They also init Ray and create a
|
3
|
+
# window.
|
4
|
+
#
|
5
|
+
# == Creating a Game
|
6
|
+
# There are several ways of doing this.
|
7
|
+
# Using a block:
|
8
|
+
# Ray::Game.new("my game") do
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Using the instance directly:
|
13
|
+
# game = Ray::Game.new("my game")
|
14
|
+
# ...
|
15
|
+
# game.run
|
16
|
+
#
|
17
|
+
# Subclassing:
|
18
|
+
# class Game < Ray::Game
|
19
|
+
# def initialize
|
20
|
+
# super("my game")
|
21
|
+
# ...
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Game.new.run
|
26
|
+
#
|
27
|
+
# == Registring scenes to a Game
|
28
|
+
# Games need the scenes they use to be registred. The most obvious way to
|
29
|
+
# do it is to use scene with a block:
|
30
|
+
# scene :game do
|
31
|
+
# # See Ray::Scene
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# You may also call it to register a subclass of Ray::Scene
|
35
|
+
# scene(:game, GameScene)
|
36
|
+
# Which is the same as:
|
37
|
+
# GameScene.bind(self) # Assuming GameScene's scene_name is set to :game
|
38
|
+
#
|
39
|
+
# == Managing the scene stack
|
40
|
+
# You can push a scene to the game:
|
41
|
+
# push_scene :game
|
42
|
+
# When #run will be called, it will show the scene :game. Notice that, if you
|
43
|
+
# push more than one scene, only the last one will be seen directly. However,
|
44
|
+
# if you remove it later, the previous scene will be shown.
|
45
|
+
#
|
46
|
+
# You can thus also remove a scene from your stack:
|
47
|
+
# pop_scene # Removes the last scene
|
48
|
+
#
|
49
|
+
# exit is not exactly the same: it will ask the scene to quit before doing this.
|
50
|
+
# exit! will do something totally different: completely kill the game.
|
51
|
+
#
|
52
|
+
# == Handling events
|
53
|
+
# Games can listen to events just like scenes. Since the event runner will change
|
54
|
+
# often, it needs to register every time it changes it. You can pass a block to
|
55
|
+
# the register method:
|
56
|
+
# register do
|
57
|
+
# on :some_event do some_stuff end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# You may also want to override register in suclasses:
|
61
|
+
# def register
|
62
|
+
# on :some_event do some_stuff end
|
63
|
+
# end
|
4
64
|
#
|
5
|
-
# The game will run eerything when it is created, and free it when
|
6
|
-
# it is done running.
|
7
65
|
class Game
|
66
|
+
include Ray::Helper
|
67
|
+
|
8
68
|
# Creates a new game.
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
69
|
+
#
|
70
|
+
# You can pass all the arguments you would pass to create_window,
|
71
|
+
# except width and height which should be given in :video_mode:
|
72
|
+
# Ray::Game.new('hello', :video_modes => %w(480x272 640x480))
|
12
73
|
#
|
13
74
|
# It will try to get the biggest resolution available (so it will most
|
14
|
-
# likely choose 640x480).
|
75
|
+
# likely choose 640x480 in this case).
|
15
76
|
#
|
16
|
-
# If a block is
|
77
|
+
# If a block is passed, it is instance evaluated, then the game is
|
17
78
|
# directly run.
|
18
|
-
|
19
|
-
|
20
|
-
|
79
|
+
#
|
80
|
+
# This methods creates a new window and inits Ray.
|
81
|
+
def initialize(title, hash = {}, &block)
|
82
|
+
@game_registred_scenes = {}
|
83
|
+
@game_scenes = []
|
21
84
|
|
22
85
|
defaults = {
|
23
|
-
:double_buf
|
24
|
-
:bpp
|
25
|
-
:hw_surface
|
26
|
-
:sw_surface
|
86
|
+
:double_buf => true,
|
87
|
+
:bpp => 32,
|
88
|
+
:hw_surface => true,
|
89
|
+
:sw_surface => false,
|
90
|
+
:video_modes => %w(480x272 640x480)
|
27
91
|
}
|
28
92
|
|
29
93
|
options = defaults.merge(hash)
|
@@ -35,6 +99,7 @@ module Ray
|
|
35
99
|
:async_blit => options[:async_blit],
|
36
100
|
:double_buf => options[:double_buf],
|
37
101
|
:fullscreen => options[:fullscreen],
|
102
|
+
:resizable => options[:resizable],
|
38
103
|
:no_frame => options[:no_frame]
|
39
104
|
}
|
40
105
|
|
@@ -50,7 +115,17 @@ module Ray
|
|
50
115
|
last_mode = modes.select { |mode| Ray.can_use_mode? mode }.last
|
51
116
|
raise ArgumentError, "No valid mode found" unless last_mode
|
52
117
|
|
53
|
-
@
|
118
|
+
if @game_title = title
|
119
|
+
Ray.window_title = @game_title
|
120
|
+
Ray.text_icon = @game_title
|
121
|
+
end
|
122
|
+
|
123
|
+
if icon = options[:icon]
|
124
|
+
Ray.icon = icon.is_a?(Ray::Image) ? icon : icon.to_image
|
125
|
+
end
|
126
|
+
|
127
|
+
@game_last_mode = last_mode
|
128
|
+
@game_window = Ray.create_window(last_mode)
|
54
129
|
|
55
130
|
if block
|
56
131
|
instance_eval(&block)
|
@@ -58,19 +133,26 @@ module Ray
|
|
58
133
|
end
|
59
134
|
end
|
60
135
|
|
61
|
-
# Adds a scene to the stack
|
136
|
+
# Adds a scene to the stack using its name.
|
137
|
+
#
|
62
138
|
# You must call Game#scene before this. If you subclassed scene,
|
63
|
-
# then call bind to register it
|
64
|
-
|
65
|
-
|
139
|
+
# then call bind to register it:
|
140
|
+
# scene :something, SomeClass
|
141
|
+
# SomeClass.bind(self)
|
142
|
+
#
|
143
|
+
# @param [Symbol] scene_name The name of the scene which should be pushed
|
144
|
+
# @param *args Arguments passed to the scene
|
145
|
+
def push_scene(scene_name, *args)
|
146
|
+
scene = @game_registred_scenes[scene_name]
|
66
147
|
raise ArgumentError, "Unknown scene #{scene_name}" unless scene
|
67
148
|
|
68
|
-
@
|
149
|
+
@game_scenes << scene
|
150
|
+
@game_scene_arguments = args
|
69
151
|
end
|
70
152
|
|
71
153
|
# Pops the last scene.
|
72
154
|
def pop_scene
|
73
|
-
@
|
155
|
+
@game_scenes.delete_at(-1)
|
74
156
|
end
|
75
157
|
|
76
158
|
# Registers a new scene with a given name. the block will be passed
|
@@ -79,29 +161,84 @@ module Ray
|
|
79
161
|
# @param [Symobl] name the name of the new scene
|
80
162
|
# @param [Class] klass the class of the scene.
|
81
163
|
def scene(name, klass = Scene, &block)
|
82
|
-
@
|
164
|
+
@game_registred_scenes[name] = klass.new(&block)
|
83
165
|
end
|
84
166
|
|
85
167
|
# Runs the game until the last scene gets popped.
|
168
|
+
# Will call Ray.stop.
|
86
169
|
def run
|
87
|
-
until @
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
scene.
|
93
|
-
scene.
|
94
|
-
|
95
|
-
scene.
|
170
|
+
until @game_scenes.empty?
|
171
|
+
create_event_runner
|
172
|
+
register
|
173
|
+
|
174
|
+
@game_scenes.each do |scene|
|
175
|
+
scene.game = self
|
176
|
+
scene.window = @game_window
|
177
|
+
scene.event_runner = event_runner
|
178
|
+
scene.scene_arguments = @game_scene_arguments
|
96
179
|
end
|
97
180
|
|
98
|
-
scene = @
|
181
|
+
scene = @game_scenes.last
|
99
182
|
|
183
|
+
scene.setup(*@game_scene_arguments)
|
184
|
+
scene.register_events
|
100
185
|
scene.need_render!
|
101
186
|
scene.run
|
102
187
|
end
|
103
188
|
|
104
189
|
Ray.stop
|
105
190
|
end
|
191
|
+
|
192
|
+
# Registers a block to listen to events
|
193
|
+
# Subclasses can also overrid this method to register for events.
|
194
|
+
def register(&block)
|
195
|
+
if block_given?
|
196
|
+
@game_register_block = block
|
197
|
+
else
|
198
|
+
@game_register_block.call if @game_register_block
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Removes the current scene of this game
|
203
|
+
def exit
|
204
|
+
return if @game_scenes.empty?
|
205
|
+
|
206
|
+
@game_scenes.last.exit
|
207
|
+
pop_scene
|
208
|
+
end
|
209
|
+
|
210
|
+
# Kills the game, removing all the scenes it contains.
|
211
|
+
def exit!
|
212
|
+
return if @game_scenes.empty?
|
213
|
+
|
214
|
+
@game_scenes.last.exit
|
215
|
+
@game_scenes.clear
|
216
|
+
end
|
217
|
+
|
218
|
+
# Resizes the window and raises a window_resize event
|
219
|
+
def resize_window(w, h)
|
220
|
+
@game_window = Ray.create_window(@game_last_mode.merge!(:w => w,
|
221
|
+
:h => h))
|
222
|
+
@game_scenes.each do |scene|
|
223
|
+
scene.window = @game_window
|
224
|
+
end
|
225
|
+
|
226
|
+
@game_scenes.last.need_render!
|
227
|
+
|
228
|
+
raise_event(:window_resize, Ray::Rect.new(0, 0, w, h))
|
229
|
+
end
|
230
|
+
|
231
|
+
def title
|
232
|
+
@game_title
|
233
|
+
end
|
234
|
+
|
235
|
+
def inspect
|
236
|
+
"game(#{title.inspect})"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# @see Ray::Game#initialize
|
241
|
+
def self.game(title, opts = {}, &block)
|
242
|
+
Ray::Game.new(title, opts, &block)
|
106
243
|
end
|
107
244
|
end
|