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.
Files changed (45) hide show
  1. data/README.md +2 -0
  2. data/changelog.md +10 -0
  3. data/lib/assets/missing.mp3 +0 -0
  4. data/lib/assets/missing.wav +0 -0
  5. data/lib/gosu_ext/image.rb +4 -0
  6. data/lib/gosu_ext/sample.rb +18 -0
  7. data/lib/gosu_ext/song.rb +18 -0
  8. data/lib/metro.rb +2 -0
  9. data/lib/metro/animation/easing/ease_in.rb +15 -0
  10. data/lib/metro/animation/easing/easing.rb +51 -0
  11. data/lib/metro/animation/easing/linear.rb +15 -0
  12. data/lib/metro/animation/has_animations.rb +17 -0
  13. data/lib/metro/animation/implicit_animation.rb +5 -24
  14. data/lib/metro/models/draws.rb +10 -1
  15. data/lib/metro/models/model.rb +117 -89
  16. data/lib/metro/models/models/image.rb +8 -9
  17. data/lib/metro/models/models/label.rb +4 -8
  18. data/lib/metro/models/models/menu.rb +7 -9
  19. data/lib/metro/models/models/rectangle.rb +7 -12
  20. data/lib/metro/models/models/song.rb +33 -0
  21. data/lib/metro/models/properties/dimensions_property.rb +18 -1
  22. data/lib/metro/models/properties/image_property.rb +2 -2
  23. data/lib/metro/models/properties/position_property.rb +6 -0
  24. data/lib/metro/models/properties/property.rb +129 -23
  25. data/lib/metro/models/properties/sample_property.rb +97 -0
  26. data/lib/metro/models/properties/song_property.rb +113 -0
  27. data/lib/metro/scene.rb +2 -11
  28. data/lib/metro/scenes.rb +14 -7
  29. data/lib/metro/transitions/fade_transition_scene.rb +8 -1
  30. data/lib/metro/units/bounds.rb +8 -0
  31. data/lib/metro/units/dimensions.rb +23 -5
  32. data/lib/metro/units/point.rb +26 -1
  33. data/lib/metro/units/rectangle_bounds.rb +52 -0
  34. data/lib/metro/units/scale.rb +16 -0
  35. data/lib/metro/units/units.rb +3 -1
  36. data/lib/metro/version.rb +1 -1
  37. data/lib/metro/window.rb +7 -0
  38. data/lib/templates/game/README.md.tt +20 -1
  39. data/spec/metro/models/models/label_spec.rb +4 -4
  40. data/spec/metro/models/properties/dimensions_spec.rb +29 -0
  41. data/spec/metro/models/properties/position_property_spec.rb +5 -5
  42. data/spec/spec_helper.rb +3 -1
  43. metadata +22 -9
  44. data/lib/metro/animation/easing.rb +0 -31
  45. 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 - (width * center_x), y - (height * center_y), x + (width * center_x), y + (height * center_y)
31
- end
32
-
33
- def width
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
- def bounds
23
- Bounds.new x, y, x + width, y + height
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 height
31
- font.height * y_factor
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
- Metro::Models::Bounds.new x, y, x + width, y + height
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
- attr_writer :width, :height
13
-
14
- def width
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,width,x,color,
24
- width,height,color,y,height,color,
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
- (options[:default] and options[:default].is_a? Dimensions) ? options[:default] : Dimensions.none
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 Gosu::Image do |image|
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
- def initialize(model,options={})
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
- def self.gets
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
- def self.sets
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
- def self.defined_properties
54
- @defined_properties ||= []
55
- end
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 ||= ActiveSupport::HashWithIndifferentAccess.new { |hash,key| hash[:numeric] }
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