metro 0.2.1 → 0.2.2
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.
- 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
|