metro 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/changelog.md +10 -0
- data/lib/assets/missing.mp3 +0 -0
- data/lib/assets/missing.wav +0 -0
- data/lib/gosu_ext/image.rb +4 -0
- data/lib/gosu_ext/sample.rb +18 -0
- data/lib/gosu_ext/song.rb +18 -0
- data/lib/metro.rb +2 -0
- data/lib/metro/animation/easing/ease_in.rb +15 -0
- data/lib/metro/animation/easing/easing.rb +51 -0
- data/lib/metro/animation/easing/linear.rb +15 -0
- data/lib/metro/animation/has_animations.rb +17 -0
- data/lib/metro/animation/implicit_animation.rb +5 -24
- data/lib/metro/models/draws.rb +10 -1
- data/lib/metro/models/model.rb +117 -89
- data/lib/metro/models/models/image.rb +8 -9
- data/lib/metro/models/models/label.rb +4 -8
- data/lib/metro/models/models/menu.rb +7 -9
- data/lib/metro/models/models/rectangle.rb +7 -12
- data/lib/metro/models/models/song.rb +33 -0
- data/lib/metro/models/properties/dimensions_property.rb +18 -1
- data/lib/metro/models/properties/image_property.rb +2 -2
- data/lib/metro/models/properties/position_property.rb +6 -0
- data/lib/metro/models/properties/property.rb +129 -23
- data/lib/metro/models/properties/sample_property.rb +97 -0
- data/lib/metro/models/properties/song_property.rb +113 -0
- data/lib/metro/scene.rb +2 -11
- data/lib/metro/scenes.rb +14 -7
- data/lib/metro/transitions/fade_transition_scene.rb +8 -1
- data/lib/metro/units/bounds.rb +8 -0
- data/lib/metro/units/dimensions.rb +23 -5
- data/lib/metro/units/point.rb +26 -1
- data/lib/metro/units/rectangle_bounds.rb +52 -0
- data/lib/metro/units/scale.rb +16 -0
- data/lib/metro/units/units.rb +3 -1
- data/lib/metro/version.rb +1 -1
- data/lib/metro/window.rb +7 -0
- data/lib/templates/game/README.md.tt +20 -1
- data/spec/metro/models/models/label_spec.rb +4 -4
- data/spec/metro/models/properties/dimensions_spec.rb +29 -0
- data/spec/metro/models/properties/position_property_spec.rb +5 -5
- data/spec/spec_helper.rb +3 -1
- metadata +22 -9
- data/lib/metro/animation/easing.rb +0 -31
- data/lib/metro/models/rectangle_bounds.rb +0 -28
@@ -22,20 +22,19 @@ module Metro
|
|
22
22
|
|
23
23
|
property :image
|
24
24
|
|
25
|
+
property :dimensions do
|
26
|
+
image.dimensions
|
27
|
+
end
|
28
|
+
|
25
29
|
def contains?(x,y)
|
26
30
|
bounds.contains?(x,y)
|
27
31
|
end
|
28
32
|
|
29
33
|
def bounds
|
30
|
-
Bounds.new x
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
image.width
|
35
|
-
end
|
36
|
-
|
37
|
-
def height
|
38
|
-
image.height
|
34
|
+
Bounds.new x: x - (width * center_x),
|
35
|
+
y: y - (height * center_y),
|
36
|
+
width: width,
|
37
|
+
height: height
|
39
38
|
end
|
40
39
|
|
41
40
|
def draw
|
@@ -19,16 +19,12 @@ module Metro
|
|
19
19
|
|
20
20
|
property :text
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def width
|
27
|
-
font.text_width(text) * x_factor
|
22
|
+
property :dimensions do
|
23
|
+
Dimensions.of (font.text_width(text) * x_factor), (font.height * y_factor)
|
28
24
|
end
|
29
25
|
|
30
|
-
def
|
31
|
-
|
26
|
+
def bounds
|
27
|
+
Bounds.new x: x, y: y, width: width, height: height
|
32
28
|
end
|
33
29
|
|
34
30
|
def contains?(x,y)
|
@@ -19,6 +19,12 @@ module Metro
|
|
19
19
|
property :unselected_color, type: :color, default: "rgba(119,119,119,1.0)"
|
20
20
|
property :selected_color, type: :color, default: "rgba(255,255,255,1.0)"
|
21
21
|
|
22
|
+
property :dimensions do
|
23
|
+
width = font.text_width(longest_option_text)
|
24
|
+
height = options.length * font.height + (options.length - 1) * padding
|
25
|
+
Dimensions.of width, height
|
26
|
+
end
|
27
|
+
|
22
28
|
# This is a temporary method as there is no options propery yet defined
|
23
29
|
def options
|
24
30
|
properties[:options]
|
@@ -82,21 +88,13 @@ module Metro
|
|
82
88
|
end
|
83
89
|
|
84
90
|
def bounds
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
def width
|
89
|
-
font.text_width(longest_option_text)# * x_factor
|
91
|
+
Bounds.new x: x, y: y, width: width, height: height
|
90
92
|
end
|
91
93
|
|
92
94
|
def longest_option_text
|
93
95
|
longest = options.map {|opt| opt }.inject("") {|longest,opt| opt.length > longest.length ? opt : longest }
|
94
96
|
end
|
95
97
|
|
96
|
-
def height
|
97
|
-
options.length * font.height + (options.length - 1) * padding
|
98
|
-
end
|
99
|
-
|
100
98
|
def option_at_index(index)
|
101
99
|
menu_options[index]
|
102
100
|
end
|
@@ -5,23 +5,18 @@ module Metro
|
|
5
5
|
|
6
6
|
property :position
|
7
7
|
|
8
|
-
property :z_order, type: :numeric, default: 0
|
9
|
-
|
10
8
|
property :color
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@width || window.width
|
16
|
-
end
|
17
|
-
|
18
|
-
def height
|
19
|
-
@height || window.height
|
10
|
+
property :dimensions do
|
11
|
+
# By default the dimensions of the rectangle will be the size of the window
|
12
|
+
window.dimensions
|
20
13
|
end
|
21
14
|
|
22
15
|
def draw
|
23
|
-
window.draw_quad(x,y,color,
|
24
|
-
width,
|
16
|
+
window.draw_quad(x,y,color,
|
17
|
+
width,x,color,
|
18
|
+
width,height,color,
|
19
|
+
y,height,color,
|
25
20
|
z_order)
|
26
21
|
end
|
27
22
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Metro
|
2
|
+
module Models
|
3
|
+
|
4
|
+
#
|
5
|
+
# A song represents an audio representation.
|
6
|
+
#
|
7
|
+
class Song < Metro::Model
|
8
|
+
|
9
|
+
property :song
|
10
|
+
property :volume, default: 1.0
|
11
|
+
property :state, type: :text, default: 'play'
|
12
|
+
|
13
|
+
def show
|
14
|
+
song.volume = self.volume
|
15
|
+
play if state == "play"
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
song.stop
|
20
|
+
end
|
21
|
+
|
22
|
+
def play
|
23
|
+
song.play if not song.playing? and not song.paused?
|
24
|
+
end
|
25
|
+
|
26
|
+
def pause
|
27
|
+
song.playing? ? song.pause : song.play
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -30,6 +30,17 @@ module Metro
|
|
30
30
|
# # box_width, box_height
|
31
31
|
# end
|
32
32
|
#
|
33
|
+
# @example Using a dimensions property providing a default block (to be calculated
|
34
|
+
# when the model retrieves the value - allowing for the model's scene and window
|
35
|
+
# to be set.)
|
36
|
+
#
|
37
|
+
# class Hero < Metro::Model
|
38
|
+
# property :dimensions do
|
39
|
+
# # Return the dimensions of the current window for the hero
|
40
|
+
# model.window.dimensions
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
33
44
|
class DimensionsProperty < Property
|
34
45
|
|
35
46
|
define_property :width
|
@@ -57,7 +68,13 @@ module Metro
|
|
57
68
|
end
|
58
69
|
|
59
70
|
def default_dimensions
|
60
|
-
|
71
|
+
if block
|
72
|
+
model.instance_eval(&block)
|
73
|
+
elsif options[:default] and options[:default].is_a? Dimensions
|
74
|
+
options[:default]
|
75
|
+
else
|
76
|
+
Dimensions.none
|
77
|
+
end
|
61
78
|
end
|
62
79
|
|
63
80
|
end
|
@@ -63,7 +63,7 @@ module Metro
|
|
63
63
|
|
64
64
|
# Set the image with the given image. A Gosu::Image does not normally
|
65
65
|
# store it's path, however, this functionality has been monkey-patched.
|
66
|
-
set
|
66
|
+
set Metro::Image do |image|
|
67
67
|
image.path
|
68
68
|
end
|
69
69
|
|
@@ -98,7 +98,7 @@ module Metro
|
|
98
98
|
images[path] = gosu_image
|
99
99
|
end
|
100
100
|
|
101
|
-
Image.new gosu_image, path, tileable
|
101
|
+
Metro::Image.new gosu_image, path, tileable
|
102
102
|
end
|
103
103
|
|
104
104
|
def self.images
|
@@ -54,6 +54,12 @@ module Metro
|
|
54
54
|
default_point
|
55
55
|
end
|
56
56
|
|
57
|
+
# When getting a point, then simply send that point on. This is often
|
58
|
+
# the case when the property is initailized by a scene.
|
59
|
+
get Point do |point|
|
60
|
+
point
|
61
|
+
end
|
62
|
+
|
57
63
|
# When getting a string convert it to a point.
|
58
64
|
get String do |value|
|
59
65
|
Point.parse(value)
|
@@ -1,63 +1,145 @@
|
|
1
1
|
module Metro
|
2
2
|
class Model
|
3
3
|
|
4
|
+
#
|
5
|
+
# A property is a value filter that allows you to specify filtering options
|
6
|
+
# both on the setting and the getting of a value. The property itself does
|
7
|
+
# not maintain any of these values but keeps in mind the particular model
|
8
|
+
# and options that it is created when applying the filtering.
|
9
|
+
#
|
10
|
+
# @note Property is intented to be subclassed to provide filtering of particular types.
|
11
|
+
#
|
4
12
|
class Property
|
5
13
|
include Units
|
6
14
|
|
7
|
-
attr_reader :model, :options
|
15
|
+
attr_reader :model, :options, :block
|
8
16
|
|
9
|
-
|
17
|
+
#
|
18
|
+
# @param [Model] model the model associated with this property.
|
19
|
+
# @param [Types] options the additional options that may be set with
|
20
|
+
# this property. This may be default values or options on how
|
21
|
+
# this properby should behave.
|
22
|
+
#
|
23
|
+
def initialize(model,options={},&block)
|
10
24
|
@model = model
|
11
25
|
@options = options
|
26
|
+
@block = block
|
12
27
|
end
|
13
28
|
|
14
|
-
|
15
|
-
@gets ||= begin
|
16
|
-
hash = HashWithIndifferentAccess.new { |hash,key| hash["NilClass"] }
|
17
|
-
hash["NilClass"] = lambda { |value| raise "#{self} is not able to translate the #{value} (#{value.class})" }
|
18
|
-
hash
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
29
|
+
# Define a filter block for getting a value of that type.
|
22
30
|
def self.get(type=NilClass,&block)
|
23
31
|
gets[type.to_s] = block
|
24
32
|
end
|
25
33
|
|
34
|
+
# All get filter blocks defined
|
35
|
+
def self.gets
|
36
|
+
@gets ||= hash_with_default_to_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Perform a get of the value, running it through the get
|
40
|
+
# filter block that matches the type.
|
26
41
|
def get(value)
|
27
42
|
get_block = self.class.gets[value.class.to_s]
|
28
43
|
instance_exec(value,&get_block)
|
29
44
|
end
|
30
45
|
|
31
|
-
|
32
|
-
@sets ||= begin
|
33
|
-
hash = HashWithIndifferentAccess.new { |hash,key| hash["NilClass"] }
|
34
|
-
hash["NilClass"] = lambda { |value| raise "#{self} is not able to translate the #{value} (#{value.class})" }
|
35
|
-
hash
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
46
|
+
# Define a filter block for setting a value of the type.
|
39
47
|
def self.set(type=NilClass,&block)
|
40
48
|
sets[type.to_s] = block
|
41
49
|
end
|
42
50
|
|
51
|
+
# All set filter blocks defined
|
52
|
+
def self.sets
|
53
|
+
@sets ||= hash_with_default_to_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# Perform a set of the value, running it through the set
|
57
|
+
# filter block that matches the type.
|
43
58
|
def set(value)
|
44
59
|
set_block = self.class.sets[value.class.to_s]
|
45
60
|
instance_exec(value,&set_block)
|
46
61
|
end
|
47
62
|
|
63
|
+
# Define a filter block that applies to both the setting and getting of a property.
|
48
64
|
def self.get_or_set(type=NilClass,&block)
|
49
65
|
gets[type.to_s] = block
|
50
66
|
sets[type.to_s] = block
|
51
67
|
end
|
52
68
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
69
|
+
#
|
70
|
+
# Allow a property to define a sub-property.
|
71
|
+
#
|
72
|
+
# A sub-property that is defined can be any type of exiting properties. Which
|
73
|
+
# may also define more sub-properties. These sub-properties will be available as properties
|
74
|
+
# through the names provided.
|
75
|
+
#
|
76
|
+
# @example DimensionProperty defining two numeric sub-properties for height and width
|
77
|
+
#
|
78
|
+
# class DimensionsProperty < Property
|
79
|
+
# define_property :width
|
80
|
+
# define_property :height
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# class Frogger < Metro::Model
|
84
|
+
# property :dimensions
|
85
|
+
#
|
86
|
+
# def after_initialize
|
87
|
+
# puts "Frog dimensions are #{dimensions}"
|
88
|
+
# puts "(#{dimensions.width},#{dimensions.height}) == (#{width},#{height})"
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# The sub-properties can also be prefixed with the parent property name:
|
93
|
+
#
|
94
|
+
# @example FontProperty defines sub-properties which include the parent propery prefix
|
95
|
+
#
|
96
|
+
# class FontProperty < Property
|
97
|
+
# define_property :size, prefix: true
|
98
|
+
# define_property :name, type: :text, prefix: true
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# class MyLabel < Metro::Model
|
102
|
+
# property :font
|
103
|
+
#
|
104
|
+
# def after_initialize
|
105
|
+
# puts "Font is: #{font} - #{font_name}:#{font_size}"
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# If you define a property with the non-default name it will automatically add the prefix
|
110
|
+
# to all the sub-properties. This is to prevent any getter/setter method name collisions.
|
111
|
+
#
|
112
|
+
# @example DimensionProperty defining two numeric sub-properties for height and width
|
113
|
+
#
|
114
|
+
# class DimensionsProperty < Property
|
115
|
+
# define_property :width
|
116
|
+
# define_property :height
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# class Frogger < Metro::Model
|
120
|
+
# property :dims, type: :dimensions
|
121
|
+
#
|
122
|
+
# def after_initialize
|
123
|
+
# puts "Frog dimensions are #{dims}"
|
124
|
+
# puts "(#{dims.width},#{dims.height}) == (#{dims_width},#{dims_height})"
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
#
|
57
128
|
def self.define_property(name,options = {})
|
58
129
|
defined_properties.push PropertyDefinition.new name, options
|
59
130
|
end
|
60
131
|
|
132
|
+
#
|
133
|
+
# @return an array of all the defined properties.
|
134
|
+
#
|
135
|
+
def self.defined_properties
|
136
|
+
@defined_properties ||= []
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Capture all the subclassed properties and add them to the availble list of
|
141
|
+
# properties.
|
142
|
+
#
|
61
143
|
def self.inherited(subclass)
|
62
144
|
property_name = subclass.to_s.gsub(/Property$/,'').split("::").last.underscore
|
63
145
|
properties_hash[property_name] = subclass.to_s
|
@@ -67,19 +149,41 @@ module Metro
|
|
67
149
|
@properties ||= []
|
68
150
|
end
|
69
151
|
|
152
|
+
#
|
153
|
+
# @return a Property Class that matches the specified name.
|
154
|
+
#
|
70
155
|
def self.property(name)
|
71
156
|
property_classname = properties_hash[name]
|
72
157
|
property_classname.constantize
|
73
158
|
end
|
74
159
|
|
160
|
+
#
|
161
|
+
# Generate a properties hash. This properties will default to the numeric key when the property
|
162
|
+
# type cannot be found.
|
163
|
+
#
|
75
164
|
def self.properties_hash
|
76
|
-
@properties_hash ||=
|
165
|
+
@properties_hash ||= HashWithIndifferentAccess.new { |hash,key| hash[:numeric] }
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Generate a hash that will default all missing keys to the NilClass key and the NilClass key will
|
170
|
+
# return a cource of action that will raise an exception. This means by default that if it is not
|
171
|
+
# overridden an error is generated.
|
172
|
+
#
|
173
|
+
def self.hash_with_default_to_nil
|
174
|
+
hash = HashWithIndifferentAccess.new { |hash,key| hash["NilClass"] }
|
175
|
+
hash["NilClass"] = lambda { |value| raise "#{self} is not able to translate the #{value} (#{value.class})" }
|
176
|
+
hash
|
77
177
|
end
|
78
178
|
|
79
179
|
end
|
80
180
|
|
81
181
|
end
|
82
182
|
|
183
|
+
#
|
184
|
+
# A property definition contains the name of the property and the options specified with it.
|
185
|
+
# This is used internally to define properties and sub-properties.
|
186
|
+
#
|
83
187
|
class PropertyDefinition
|
84
188
|
|
85
189
|
attr_reader :name, :options
|
@@ -102,3 +206,5 @@ require_relative 'font_property'
|
|
102
206
|
require_relative 'image_property'
|
103
207
|
require_relative 'position_property'
|
104
208
|
require_relative 'scale_property'
|
209
|
+
require_relative 'song_property'
|
210
|
+
require_relative 'sample_property'
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Metro
|
2
|
+
class Model
|
3
|
+
|
4
|
+
#
|
5
|
+
# A sample property maintains a Gosu::Sample.
|
6
|
+
#
|
7
|
+
# A sample is stored in the properties as the path in the assets folder and is converted into
|
8
|
+
# a Gosu::Sample when it is retrieved within the system. When retrieving a sample the Sample
|
9
|
+
# Property will attempt to use a sample that already exists that meets that criteria.
|
10
|
+
#
|
11
|
+
# @example Defining a sample property
|
12
|
+
#
|
13
|
+
# class Hero < Metro::Model
|
14
|
+
# property :sample
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example Defining a sample property providing a default
|
18
|
+
#
|
19
|
+
# class Hero < Metro::Model
|
20
|
+
# property :sample, path: 'pickup.wav'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example Using a sample property with a different property name
|
24
|
+
#
|
25
|
+
# class Hero < Metro::Model
|
26
|
+
# property :pickup_sample, type: :sample, path: 'pickup.wav'
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
class SampleProperty < Metro::Model::Property
|
30
|
+
|
31
|
+
# By default, getting an unsupported value will return the default sample
|
32
|
+
get do |value|
|
33
|
+
default_sample
|
34
|
+
end
|
35
|
+
|
36
|
+
# Bu default, setting sn unsupported value will save the default sample filename
|
37
|
+
set do |value|
|
38
|
+
default_sample_filename
|
39
|
+
end
|
40
|
+
|
41
|
+
# Generate a sample from the specified string filepath
|
42
|
+
get String do |filename|
|
43
|
+
self.class.sample_for path: filename, window: model.window
|
44
|
+
end
|
45
|
+
|
46
|
+
# The assumption here is that the string is a sample filepath
|
47
|
+
set String do |filename|
|
48
|
+
filename
|
49
|
+
end
|
50
|
+
|
51
|
+
# Setting the song value with a Metro::Sample will save the string filepath
|
52
|
+
set Metro::Sample do |sample|
|
53
|
+
sample.path
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @return the default sample for the sample property. This is based on the default
|
58
|
+
# sample name.
|
59
|
+
#
|
60
|
+
def default_sample
|
61
|
+
self.class.sample_for path: default_sample_filename, window: model.window
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# @return a string sample name that is default. If the property was not created with
|
66
|
+
# a default value the the default sample is the missing sample found in Metro.
|
67
|
+
#
|
68
|
+
def default_sample_filename
|
69
|
+
options[:path] || metro_asset_path("missing.wav")
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Returns a Metro::Sample. This is composed of the metadata provided and a Gosu::Sample.
|
74
|
+
#
|
75
|
+
# @param [Hash] options the path, window, and other parameters necessary to generate
|
76
|
+
# a sample.
|
77
|
+
#
|
78
|
+
def self.sample_for(options)
|
79
|
+
options.symbolize_keys!
|
80
|
+
relative_path = options[:path]
|
81
|
+
window = options[:window]
|
82
|
+
|
83
|
+
absolute_path = path = options[:path]
|
84
|
+
absolute_path = asset_path(absolute_path) unless absolute_path.start_with? "/"
|
85
|
+
|
86
|
+
gosu_sample = create_sample(window,absolute_path)
|
87
|
+
|
88
|
+
Metro::Sample.new gosu_sample, relative_path
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.create_sample(window,filename)
|
92
|
+
Gosu::Sample.new(window, filename)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|