metro 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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