metro 0.1.5 → 0.1.6

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