metro 0.1.5 → 0.1.6

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 (63) hide show
  1. data/README.md +19 -0
  2. data/changelog.md +8 -0
  3. data/lib/assets/missing_animation.png +0 -0
  4. data/lib/commands/generate_model.rb +2 -2
  5. data/lib/commands/generate_scene.rb +12 -2
  6. data/lib/commands/generate_view.rb +1 -1
  7. data/lib/gosu_ext/color.rb +1 -1
  8. data/lib/gosu_ext/image.rb +5 -0
  9. data/lib/metro.rb +18 -5
  10. data/lib/metro/animation/animation.rb +31 -0
  11. data/lib/metro/asset_path.rb +9 -0
  12. data/lib/metro/events/event_dictionary.rb +53 -0
  13. data/lib/metro/events/event_factory.rb +5 -3
  14. data/lib/metro/events/has_events.rb +5 -6
  15. data/lib/metro/game.rb +1 -1
  16. data/lib/metro/models/dimensions.rb +21 -0
  17. data/lib/metro/models/model.rb +149 -52
  18. data/lib/metro/models/model_factory.rb +4 -5
  19. data/lib/metro/models/{generic.rb → models/generic.rb} +0 -0
  20. data/lib/metro/models/{grid_drawer.rb → models/grid_drawer.rb} +4 -9
  21. data/lib/metro/models/{image.rb → models/image.rb} +11 -14
  22. data/lib/metro/models/models/label.rb +44 -0
  23. data/lib/metro/models/{menu.rb → models/menu.rb} +23 -18
  24. data/lib/metro/models/{rectangle.rb → models/rectangle.rb} +6 -5
  25. data/lib/metro/models/point.rb +23 -0
  26. data/lib/metro/models/properties/angle.rb +43 -0
  27. data/lib/metro/models/properties/animation.rb +143 -0
  28. data/lib/metro/models/properties/color.rb +113 -0
  29. data/lib/metro/models/properties/dimensions.rb +66 -0
  30. data/lib/metro/models/properties/font.rb +155 -0
  31. data/lib/metro/models/properties/image.rb +101 -0
  32. data/lib/metro/models/properties/numeric.rb +29 -0
  33. data/lib/metro/models/properties/position.rb +84 -0
  34. data/lib/metro/models/properties/property.rb +111 -0
  35. data/lib/metro/models/properties/scale.rb +89 -0
  36. data/lib/metro/models/properties/text.rb +66 -0
  37. data/lib/metro/models/properties/velocity.rb +80 -0
  38. data/lib/metro/models/scale.rb +21 -0
  39. data/lib/metro/scene.rb +19 -1
  40. data/lib/metro/scenes.rb +91 -31
  41. data/lib/metro/transitions/scene_transitions.rb +8 -0
  42. data/lib/metro/version.rb +1 -1
  43. data/lib/metro/views/view.rb +9 -1
  44. data/lib/templates/game/metro.tt +1 -1
  45. data/lib/templates/game/models/game_model.rb +3 -0
  46. data/lib/templates/game/scenes/brand_scene.rb +1 -1
  47. data/lib/templates/game/scenes/brand_to_title_scene.rb +1 -1
  48. data/lib/templates/game/scenes/game_scene.rb +19 -0
  49. data/lib/templates/game/scenes/title_scene.rb +1 -1
  50. data/lib/templates/game/views/brand_to_title.yaml +2 -2
  51. data/lib/templates/game/views/title.yaml +3 -3
  52. data/lib/templates/{model.rb.erb → model.rb.tt} +1 -1
  53. data/lib/templates/{scene.rb.erb → scene.rb.tt} +1 -1
  54. data/lib/templates/view.yaml.tt +6 -0
  55. data/spec/metro/models/models/label_spec.rb +110 -0
  56. data/spec/metro/models/properties/color_spec.rb +85 -0
  57. data/spec/metro/models/properties/font_spec.rb +129 -0
  58. data/spec/metro/models/properties/numeric_property_spec.rb +46 -0
  59. data/spec/metro/models/properties/position_property_spec.rb +90 -0
  60. data/spec/metro/scenes_spec.rb +77 -0
  61. metadata +50 -16
  62. data/lib/metro/models/label.rb +0 -63
  63. data/lib/templates/view.yaml.erb +0 -32
@@ -0,0 +1,89 @@
1
+ module Metro
2
+ class Model
3
+
4
+ #
5
+ # A scale property maintains an x and y scaling factor. This scale is not applied to any
6
+ #
7
+ # A font property also defines a `font_size` property and a `font_name` property which allows a
8
+ # more direct interface. Changing these values will update the font the next time that it is drawn.
9
+ #
10
+ # A font is stored in the properties as a hash representation and is converted into
11
+ # a Gosu::Font when it is retrieved within the system. When retrieving a font the Font
12
+ # Property will attempt to use a font that already exists that meets that criteria.
13
+ #
14
+ # The fonts are cached within the font property to help performance by reducing the unncessary
15
+ # creation of similar fonts.
16
+ #
17
+ # @example Defining a font property
18
+ #
19
+ # class Scoreboard < Metro::Model
20
+ # property :font
21
+ #
22
+ # def draw
23
+ # font.draw text, x, y, z_order, x_factor, y_factor, color
24
+ # end
25
+ #
26
+ # end
27
+ #
28
+ # @example Defining a font property providing a default
29
+ #
30
+ # class Hero < Metro::Model
31
+ # property :font, default: { name: 'Comic Sans', size: 80 }
32
+ # end
33
+ #
34
+ # @example Using the `font_size` and `font_name` properties
35
+ #
36
+ # class Hero < Metro::Model
37
+ # property :color, default: "rgba(255,0,0,1.0)"
38
+ #
39
+ # def dignified
40
+ # self.font_size = 45
41
+ # self.font_name = 'Helvetica'
42
+ # end
43
+ # end
44
+ #
45
+ # @example Using a font property with a different property name
46
+ #
47
+ # class Hero < Metro::Model
48
+ # property :alt_font, type: :font, default: "rgba(255,0,255,1.0)"
49
+ #
50
+ # def draw
51
+ # puts "Font: #{alt_font_name}:#{alt_font_size}"
52
+ # alt_font.draw text, x, y, z_order, x_factor, y_factor, color
53
+ # end
54
+ # end
55
+ #
56
+ class ScaleProperty < Property
57
+
58
+ define_property :x_factor
59
+
60
+ define_property :y_factor
61
+
62
+ get do |value|
63
+ default_scale
64
+ end
65
+
66
+ get String do |value|
67
+ Scale.parse(value)
68
+ end
69
+
70
+ set do |value|
71
+ default_scale.to_s
72
+ end
73
+
74
+ set String do |value|
75
+ value
76
+ end
77
+
78
+ set Scale do |value|
79
+ value.to_s
80
+ end
81
+
82
+ def default_scale
83
+ (options[:default] and options[:default].is_a? Scale) ? options[:default] : Scale.one
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ module Metro
2
+ class Model
3
+
4
+ #
5
+ # A text property maintains a string of text
6
+ #
7
+ # Text is stored as text in properties. When retrieving the text, the contents of the text will
8
+ # be evaluated within the instance of the model's scene. Which means that text may contain
9
+ # escaped variables referencing anything in the scene or the game.
10
+ #
11
+ # @example Defining a text property
12
+ #
13
+ # class Scoreboard < Metro::Model
14
+ # property :text
15
+ #
16
+ # def draw
17
+ # font.draw text, x, y, z_order, x_factor, y_factor, color
18
+ # end
19
+ #
20
+ # end
21
+ #
22
+ # @example Defining with a default and text that will be instance evaluated.
23
+ #
24
+ # class ScoreBoard < Metro::Model
25
+ # property :font, default: 'Score is #{player.score}'
26
+ # end
27
+ #
28
+ # @example Using a text property with a different property name
29
+ #
30
+ # class Hero < Metro::Model
31
+ # property :description, type: :text
32
+ #
33
+ # def draw
34
+ # description_font.draw text, x, y, z_order, x_factor, y_factor, color
35
+ # end
36
+ # end
37
+ #
38
+ class TextProperty < Property
39
+
40
+ # When no text is found for the field use the default text.
41
+ get do |value|
42
+ evalute_within_scene default_text
43
+ end
44
+
45
+ # When getting the text, evaluate the text within the scene.
46
+ get String do |value|
47
+ evalute_within_scene(value)
48
+ end
49
+
50
+ # When saving, simply save whatever is given as text.
51
+ set do |value|
52
+ value.to_s
53
+ end
54
+
55
+ def evalute_within_scene(text)
56
+ model.scene.instance_eval( "\"#{text}\"" )
57
+ end
58
+
59
+ def default_text
60
+ options[:default] || 'Text for #{model} not specified!'
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,80 @@
1
+ module Metro
2
+ class Model
3
+
4
+ class VectorProperty < Property
5
+
6
+ def get(value)
7
+ value ? value : Vector.new
8
+ end
9
+
10
+ def set(value)
11
+ Vector.new value
12
+ end
13
+
14
+ class Vector
15
+
16
+ def initialize(angle,velocity)
17
+ @angle = angle
18
+ @velocity = velocity
19
+ end
20
+
21
+ def decay!
22
+ @velocity.decay!
23
+ end
24
+
25
+ def accelerate(amount,angle)
26
+
27
+ end
28
+
29
+ def apply_x(x_position)
30
+
31
+ end
32
+
33
+ def apply_y(y_position)
34
+
35
+ end
36
+
37
+
38
+ end
39
+
40
+ end
41
+
42
+ class VelocityProperty < Property
43
+
44
+ def get(value)
45
+ value ? value : Velocity.new(0.0,0.95)
46
+ end
47
+
48
+ def set(value)
49
+ Velocity.new value
50
+ end
51
+
52
+ class Velocity
53
+
54
+ def initialize(value,decay = default_decay)
55
+ @value = value.to_f
56
+ @decay = decay || default_decay
57
+ end
58
+
59
+ def default_decay
60
+ 0.95
61
+ end
62
+
63
+ def decay
64
+ @value *= @decay
65
+ end
66
+
67
+ def accelerate(amount)
68
+ @value += amount
69
+ end
70
+
71
+ def to_f
72
+ @value
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,21 @@
1
+ module Metro
2
+ class Scale < Struct.new(:x_factor,:y_factor)
3
+
4
+ def self.one
5
+ new 1.0, 1.0
6
+ end
7
+
8
+ def self.to(x,y)
9
+ new x.to_f, y.to_f
10
+ end
11
+
12
+ def self.parse(string)
13
+ to *string.split(",",2).map(&:to_f)
14
+ end
15
+
16
+ def to_s
17
+ "#{x_factor},#{y_factor}"
18
+ end
19
+
20
+ end
21
+ end
@@ -251,6 +251,7 @@ module Metro
251
251
  registering_actor.window = window
252
252
 
253
253
  drawers.push(registering_actor)
254
+ updaters.push(registering_actor)
254
255
 
255
256
  register_events_for_target(registering_actor,registering_actor.class.events)
256
257
  end
@@ -281,12 +282,29 @@ module Metro
281
282
  #
282
283
  def self.scene_name(scene_name=nil)
283
284
  @scene_name ||= begin
284
- to_s.gsub(/_?Scene$/i,'').underscore
285
+ if to_s == "Metro::Scene"
286
+ to_s.underscore
287
+ else
288
+ to_s.gsub(/_?Scene$/i,'').underscore
289
+ end
285
290
  end
286
291
 
287
292
  scene_name ? @scene_name = scene_name.to_s : @scene_name
288
293
  end
289
294
 
295
+ #
296
+ # @return a common name that can be used through the system as a common identifier.
297
+ #
298
+ def self.metro_name
299
+ scene_name
300
+ end
301
+
302
+ #
303
+ # @return an array of all the scene names of all the ancestor scenes
304
+ #
305
+ def self.hierarchy
306
+ ancestors.find_all {|a| a.respond_to? :metro_name }.map(&:metro_name)
307
+ end
290
308
 
291
309
  #
292
310
  # Allows you to set or retrieve the scene name for the Scene.
@@ -1,5 +1,3 @@
1
- require_relative 'transitions/scene_transitions'
2
-
3
1
  module Metro
4
2
 
5
3
  #
@@ -11,7 +9,6 @@ module Metro
11
9
  #
12
10
  # Scenes.find("intro") # => IntroScene
13
11
  # Scenes.find(:intro) # => IntroScene
14
- # Scenes.find(IntroScene) # => IntroScene
15
12
  #
16
13
  # @example Creating a scene instance based on the scene name
17
14
  #
@@ -21,7 +18,11 @@ module Metro
21
18
  #
22
19
  # Scenes.generate("intro") # => [SCENE: title]
23
20
  # Scenes.generate(:intro) # => [SCENE: title]
24
- # Scenes.generate(IntroScene) # => [SCENE: title]
21
+ #
22
+ # @example Finding a scene that does not exist
23
+ #
24
+ # scene = Scenes.find(:unknown)
25
+ # scene.missing_scene # => :unknown
25
26
  #
26
27
  module Scenes
27
28
  extend self
@@ -33,18 +34,7 @@ module Metro
33
34
  # @return the Scene class that is found matching the specified scene name.
34
35
  #
35
36
  def find(scene_name)
36
- found_scene = scenes_hash[scene_name]
37
-
38
- if found_scene
39
- found_scene.constantize
40
- else
41
- create_missing_scene(scene_name)
42
- end
43
- end
44
-
45
- def create_missing_scene(scene_name)
46
- MissingScene.missing_scene = scene_name
47
- MissingScene
37
+ scene_class( scenes_hash[scene_name] )
48
38
  end
49
39
 
50
40
  #
@@ -55,16 +45,18 @@ module Metro
55
45
  # @return an instance of Scene that is found matching the specified scene name
56
46
  #
57
47
  def generate(scene_or_scene_name,options = {})
58
- new_scene = use_scene_or_generate_scene(scene_or_scene_name)
59
-
60
- post_filters.inject(new_scene) {|scene,post| post.filter(scene,options) }
48
+ new_scene = generate_scene_from(scene_or_scene_name)
49
+ apply_post_filters(new_scene,options)
61
50
  end
62
51
 
63
52
  #
64
53
  # If we have been given a scene, then we simply want to use it otherwise
65
54
  # we need to find and generate our scene from the scene name.
66
- #
67
- def use_scene_or_generate_scene(scene_or_scene_name)
55
+ #
56
+ # @param [String,Sybmol,Class] scene_or_scene_name the name of the scene or an instance
57
+ # of Scene.
58
+ #
59
+ def generate_scene_from(scene_or_scene_name)
68
60
  if scene_or_scene_name.is_a? Scene
69
61
  scene_or_scene_name
70
62
  else
@@ -75,27 +67,95 @@ module Metro
75
67
  #
76
68
  # Post filters are applied to the scene after it has been found. These are
77
69
  # all objects that can respond to the #filter method.
78
- #
70
+ #
79
71
  def post_filters
80
- [ SceneTransitions ]
72
+ @post_filters ||= []
73
+ end
74
+
75
+ #
76
+ # Register a filter that will be executed after a scene is found and generated. This
77
+ # allows for the scene to be modified or changed based on the provided options.
78
+ #
79
+ # A filter is any object that responds to #filter and accepts two parameters: the
80
+ # scene and a hash of options.
81
+ #
82
+ # @param [#filter] post_filter a filter is an object that can act as a filter.
83
+ #
84
+ def register_post_filter(post_filter)
85
+ post_filters.push(post_filter)
81
86
  end
82
87
 
83
88
  private
84
89
 
90
+ #
91
+ # Apply all the post filtering to the specified scene with the given options
92
+ #
93
+ # @return a Scene object that has been filtered.
94
+ #
95
+ def apply_post_filters(new_scene,options)
96
+ post_filters.inject(new_scene) {|scene,post| post.filter(scene,options) }
97
+ end
98
+
85
99
  #
86
100
  # @return a Hash that allows for accessing symbol names of the scenes
87
101
  # as well as the class name constants to allow for the scenes to be found.
88
102
  #
89
103
  def scenes_hash
90
- @scenes_hash ||= Scene.scenes.inject({}) do |dict,scene_classname|
91
- scene = scene_classname.constantize
92
- name = scene.scene_name
93
- dict[name] = scene_classname
94
- dict[name.to_sym] = scene_classname
95
- dict[scene] = scene_classname
96
- dict
104
+ @scenes_hash ||= build_map_of_scenes(Scene.scenes)
105
+ end
106
+
107
+ #
108
+ # Generate a map from the scene or scenes to include all the sub-classes of
109
+ # these scenes.
110
+ #
111
+ # @param [Scene,Array<Scene>] scenes a scene or scene subclass or an array of
112
+ # scene subclasses.
113
+ #
114
+ # @see #scenes_hash
115
+ #
116
+ def build_map_of_scenes(scenes)
117
+ hash = hash_with_missing_scene_default
118
+ all_scenes_for(scenes).inject(hash) do |hash,scene|
119
+ hash[scene.scene_name] = scene.to_s
120
+ hash
121
+ end
122
+ end
123
+
124
+ #
125
+ # Create a hash that will return a setup missing scene by default.
126
+ #
127
+ def hash_with_missing_scene_default
128
+ ActiveSupport::HashWithIndifferentAccess.new do |hash,key|
129
+ missing_scene = scene_class(hash[:missing_scene])
130
+ missing_scene.missing_scene = key.to_sym
131
+ missing_scene
97
132
  end
98
133
  end
99
134
 
135
+ #
136
+ # Returns all subclassed scenes of the scene or scenes provided. This method is
137
+ # meant to be called recursively to generate the entire list of all the scenes.
138
+ #
139
+ # @param [Scene,Array<Scene>] scenes a scene or scene subclass or an array of
140
+ # scene subclasses.
141
+ #
142
+ def all_scenes_for(scenes)
143
+ Array(scenes).map do |scene_class_name|
144
+ scene = scene_class(scene_class_name)
145
+ [ scene ] + all_scenes_for(scene.scenes)
146
+ end.flatten.compact
147
+ end
148
+
149
+ #
150
+ # @param [String,Symbol] class_name the name of the class that you want the class
151
+ #
152
+ # @return the class with the given class name
153
+ #
154
+ def scene_class(class_or_class_name)
155
+ class_or_class_name.class == Class ? class_or_class_name : class_or_class_name.constantize
156
+ end
157
+
100
158
  end
101
- end
159
+ end
160
+
161
+ require_relative 'transitions/scene_transitions'