metro 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ module Metro
2
+ module Models
3
+
4
+ #
5
+ # Draws a string of text
6
+ #
7
+ # @example Using the Label in a view file
8
+ # model: "metro::models::label"
9
+ #
10
+ class Label < Model
11
+
12
+ attr_accessor :x, :y, :x_factor, :y_factor, :z_order
13
+
14
+ def initialize
15
+ @x_factor = @y_factor = 1.0
16
+ end
17
+
18
+ def font
19
+ @font ||= Gosu::Font.new(window, Gosu::default_font_name, 20)
20
+ end
21
+
22
+ def draw
23
+ label_text = scene.instance_eval( "\"#{text}\"" )
24
+ font.draw label_text, x, y, z_order, x_factor, y_factor, color
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,188 @@
1
+ module Metro
2
+
3
+ #
4
+ # The Model is a basic, generic representation of a game object
5
+ # that has a visual representation within the scene's window.
6
+ #
7
+ # Model is designed to be an abstract class, to be subclassed by
8
+ # other models.
9
+ #
10
+ # @see Models::Generic
11
+ #
12
+ class Model
13
+
14
+ #
15
+ # The window that this model that this window is currently being
16
+ # displayed.
17
+ #
18
+ # The current value of window is managed by the scene
19
+ # as this is set when the Scene is added to the window. All the
20
+ # models gain access to the window.
21
+ #
22
+ # @see Window
23
+ #
24
+ attr_accessor :window
25
+
26
+ #
27
+ # The scene that this model is currently being displayed.
28
+ #
29
+ # The current value of scene is managed by the scene as this
30
+ # is set when the scene is created.
31
+ #
32
+ # @see Scene
33
+ attr_accessor :scene
34
+
35
+ #
36
+ # This is an entry point for customization. As the model's {#initialize}
37
+ # method performs may perform some initialization that may be necessary.
38
+ #
39
+ # @note This method should be implemented in the Model subclass.
40
+ #
41
+ def after_initialize ; end
42
+
43
+ #
44
+ # Returns the color of the model. In most cases where color is a prominent
45
+ # attribute (e.g. label) this will be the color. In the cases where color
46
+ # is less promenint (e.g. image) this will likely be a color that can be
47
+ # used to influence the drawing of it.
48
+ #
49
+ # @see #alpha
50
+ #
51
+ attr_reader :color
52
+
53
+ #
54
+ # Sets the color of the model.
55
+ #
56
+ # @param [String,Fixnum,Gosu::Color] value the new color to set.
57
+ #
58
+ def color=(value)
59
+ @color = Gosu::Color.new(value)
60
+ end
61
+
62
+ #
63
+ # @return the alpha value of the model's color. This is an integer value
64
+ # between 0 and 255.
65
+ #
66
+ def alpha
67
+ color.alpha
68
+ end
69
+
70
+ #
71
+ # Sets the alpha of the model.
72
+ #
73
+ # @param [String,Fixnum] value the new value of the alpha level for the model.
74
+ # This value should be between 0 and 255.
75
+ #
76
+ def alpha=(value)
77
+ # TODO: coerce the value is between 0 and 255
78
+ color.alpha = value.to_i
79
+ end
80
+
81
+ #
82
+ # Create an instance of a model.
83
+ #
84
+ # @note Overridding initialize method should be avoided, using the {#aftter_initialize)
85
+ # method or done with care to ensure that functionality is preserved.
86
+ #
87
+ def initialize(options = {})
88
+ after_initialize
89
+ end
90
+
91
+ #
92
+ # Loads a hash of content into the model. This process will convert the hash
93
+ # of content into setter and getter methods with appropriate ruby style names.
94
+ #
95
+ # This is used internally when the model is created for the Scene. It is loaded
96
+ # with the contents of the view.
97
+ #
98
+ def _load(options = {})
99
+ options = {} unless options
100
+
101
+ options.each do |raw_key,value|
102
+
103
+ key = raw_key.dup
104
+ key.gsub!(/-/,'_')
105
+ key.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
106
+ key.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
107
+
108
+ unless respond_to? key
109
+ self.class.send :define_method, key do
110
+ instance_variable_get("@#{key}")
111
+ end
112
+ end
113
+
114
+ unless respond_to? "#{key}="
115
+ self.class.send :define_method, "#{key}=" do |value|
116
+ instance_variable_set("@#{key}",value)
117
+ end
118
+ end
119
+
120
+ _loaded_options.push key
121
+ send "#{key}=", value
122
+ end
123
+
124
+ end
125
+
126
+ def _loaded_options
127
+ @_loaded_options ||= []
128
+ end
129
+
130
+ #
131
+ # Generate a hash export of all the fields that were previously stored within
132
+ # the model.
133
+ #
134
+ # This is used internally within the scene to transfer the data from one model
135
+ # to another model.
136
+ #
137
+ def _save
138
+ data_export = @_loaded_options.map {|option| [ option, send(option) ] }.flatten
139
+ Hash[*data_export]
140
+ end
141
+
142
+ #
143
+ # Captures all classes that subclass Model.
144
+ #
145
+ # @see #self.scenes
146
+ #
147
+ def self.inherited(base)
148
+ models << base
149
+ end
150
+
151
+ #
152
+ # All subclasses of Model, this should be all the defined model within the game.
153
+ #
154
+ # @return an Array of Scene subclasses
155
+ #
156
+ def self.models
157
+ @models ||= []
158
+ end
159
+
160
+ #
161
+ # Convert the specified model name into the class of the model.
162
+ #
163
+ # @return the Model class given the specified model name.
164
+ def self.model(name)
165
+ @models_hash ||= begin
166
+
167
+ hash = Hash.new(Models::Generic)
168
+
169
+ models.each do |model|
170
+ common_name = model.to_s.gsub(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
171
+ common_name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
172
+ common_name.downcase!
173
+
174
+ hash[common_name] = model
175
+ end
176
+ hash
177
+ end
178
+
179
+ @models_hash[name]
180
+ end
181
+
182
+ end
183
+ end
184
+
185
+ require_relative 'generic'
186
+ require_relative 'label'
187
+ require_relative 'select'
188
+ require_relative 'image'
@@ -0,0 +1,77 @@
1
+ module Metro
2
+ module Models
3
+
4
+ #
5
+ # Draws a a menu of options. It is called a Select as it is named after the HTML
6
+ # element select. A select model also inserts itself into the scene as an event
7
+ # target as it needs to maintain the state of the menu. When an option is selected
8
+ # an event is fired based on the name of the option.
9
+ #
10
+ # @note Only one 'select' can be defined for a given scene.
11
+ #
12
+ class Select < Model
13
+ attr_accessor :selected_index, :options
14
+
15
+ def initialize
16
+ @selected_index = 0
17
+ end
18
+
19
+ def window=(value)
20
+ @window = value
21
+ events
22
+ end
23
+
24
+ def events
25
+ relay = EventRelay.new(self,window)
26
+
27
+ relay.on_up Gosu::KbLeft, Gosu::GpLeft, Gosu::KbUp, Gosu::GpUp, do: :previous_option
28
+ relay.on_up Gosu::KbRight, Gosu::GpRight, Gosu::KbDown, Gosu::GpDown, do: :next_option
29
+ relay.on_up Gosu::KbEnter, Gosu::KbReturn, Gosu::GpButton0, do: :selection
30
+
31
+ scene.add_event_relay relay
32
+ end
33
+
34
+ def selection
35
+ scene_method = options[selected_index].downcase.gsub(/\s/,'_')
36
+ scene.send scene_method
37
+ end
38
+
39
+ def previous_option
40
+ @selected_index = @selected_index - 1
41
+ @selected_index = options.length - 1 if @selected_index <= -1
42
+ end
43
+
44
+ def next_option
45
+ @selected_index = @selected_index + 1
46
+ @selected_index = 0 if @selected_index >= options.length
47
+ end
48
+
49
+ def font
50
+ @font ||= Gosu::Font.new(window, Gosu::default_font_name, 20)
51
+ end
52
+
53
+ attr_reader :highlight_color
54
+
55
+ def highlight_color=(value)
56
+ @highlight_color = Gosu::Color.new(value)
57
+ end
58
+
59
+ def alpha=(value)
60
+ color.alpha = value.floor
61
+ highlight_color.alpha = value.floor
62
+ end
63
+
64
+ def draw
65
+ options.each_with_index do |option,index|
66
+
67
+ draw_color = color
68
+ draw_color = highlight_color if index == selected_index
69
+
70
+ y_position = y + padding * index
71
+ font.draw option, x, y_position, Metro::Game::UI, 1.0, 1.0, draw_color
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+ end
data/lib/metro/scene.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative 'scene_view/scene_view'
2
+ require_relative 'scene_actor'
2
3
  require_relative 'event_relay'
3
4
  require_relative 'animation/animation'
4
5
 
@@ -20,6 +21,15 @@ module Metro
20
21
  #
21
22
  class Scene
22
23
 
24
+ #
25
+ # As Scene does a lot of work for you with regarding to setting up content, it is
26
+ # best not to override #initialize and instead define an #after_initialize method
27
+ # within the subclasses of Scene.
28
+ #
29
+ # @note This method should be implemented in the Scene subclass.
30
+ #
31
+ def after_initialize ; end
32
+
23
33
  #
24
34
  # The events method is where a scene has access to configure the events that it
25
35
  # would like to listen for during the scene.
@@ -79,6 +89,62 @@ module Metro
79
89
  #
80
90
  def prepare_transition_from(old_scene) ; end
81
91
 
92
+
93
+ #
94
+ # Define a scene actor with the given name and options.
95
+ #
96
+ def self.draw(actor_name,options = {})
97
+ scene_actor = SceneActor.new actor_name, options
98
+
99
+ define_method actor_name do
100
+ instance_variable_get("@#{actor_name}")
101
+ end
102
+
103
+ define_method "#{actor_name}=" do |value|
104
+ instance_variable_set("@#{actor_name}",value)
105
+ end
106
+
107
+ scene_actors.push scene_actor
108
+ end
109
+
110
+ #
111
+ # Define several standard scene actors.
112
+ #
113
+ def self.draws(*actor_names)
114
+ actor_names = actor_names.flatten.compact
115
+
116
+ actor_names.each do |actor_name|
117
+ draw actor_name
118
+ end
119
+
120
+ scene_actors
121
+ end
122
+
123
+ #
124
+ # @return a list of all the SceneActors that have been defined for this Scene.
125
+ #
126
+ def self.scene_actors
127
+ @scene_actors ||= []
128
+ end
129
+
130
+ #
131
+ # Setups up the Actors for the Scene based on the SceneActors that have been
132
+ # defined.
133
+ #
134
+ # @note this method should not be overriden, otherwise the actors will perish!
135
+ # @see #after_initialize
136
+ #
137
+ def initialize
138
+ self.class.scene_actors.each do |scene_actor|
139
+ actor_data = { 'name' => scene_actor.name }.merge (view[scene_actor.name] || {})
140
+ actor_instance = scene_actor.create(actor_data)
141
+ actor_instance.scene = self
142
+ send "#{scene_actor.name}=", actor_instance
143
+ end
144
+
145
+ after_initialize
146
+ end
147
+
82
148
  #
83
149
  # The window is the main instance of the game. Using window can access a lot of
84
150
  # underlying Metro::Window, a subclass of Gosu::Window, that the Scene class is
@@ -104,6 +170,16 @@ module Metro
104
170
 
105
171
  @event_relays << @scene_events
106
172
 
173
+ @updaters = []
174
+
175
+ @drawers = []
176
+
177
+ self.class.scene_actors.each do |scene_actor|
178
+ actor = send(scene_actor.name)
179
+ actor.window = window
180
+ @drawers << actor
181
+ end
182
+
107
183
  show
108
184
  end
109
185
 
@@ -170,17 +246,35 @@ module Metro
170
246
  end
171
247
 
172
248
  #
173
- # The `base_draw` method is called by the Game Window. This is allow for any special
174
- # drawing needs to be handled before calling the traditional `draw` method defined
175
- # in the subclassed Scene.
249
+ # Enqueue will add an updater to the list of updaters that are run initially when
250
+ # update is called. An updater is any object that can respond to #update. This
251
+ # is used for animations.
252
+ #
253
+ def enqueue(updater)
254
+ @updaters << updater
255
+ end
256
+
257
+ #
258
+ # The `base_update` method is called by the Game Window. This is to allow for any
259
+ # special update needs to be handled before calling the traditional `update` method
260
+ # defined in the subclassed Scene.
261
+ #
262
+ def base_update
263
+ @updaters.each { |updater| updater.update }
264
+ update
265
+ end
266
+
267
+ #
268
+ # The `base_draw` method is called by the Game Window. This is to allow for any
269
+ # special drawing needs to be handled before calling the traditional `draw` method
270
+ # defined in the subclassed Scene.
176
271
  #
177
272
  def base_draw
273
+ @drawers.each { |drawer| drawer.draw }
178
274
  draw
179
275
  end
180
276
 
181
277
  # This provides the functionality for view handling.
182
- # @note the inclusion of view functionality redefines {#base_draw} so it must proceed
183
- # the declaration of that method.
184
278
  include SceneView
185
279
 
186
280
  #
@@ -196,7 +290,6 @@ module Metro
196
290
  window.scene = new_scene
197
291
  end
198
292
 
199
-
200
293
  #
201
294
  # Before a scene is transitioned away from to a new scene, this private method is
202
295
  # here to allow for any housekeeping or other work that needs to be done before
@@ -207,6 +300,13 @@ module Metro
207
300
  #
208
301
  def _prepare_transition(new_scene)
209
302
  log.debug "Preparing to transition from scene #{self} to #{new_scene}"
303
+
304
+ new_scene.class.scene_actors.find_all {|actor| actor.load_from_previous_scene? }.each do |scene_actor|
305
+ new_actor = new_scene.send(scene_actor.name)
306
+ current_actor = send(scene_actor.name)
307
+ new_actor._load current_actor._save
308
+ end
309
+
210
310
  prepare_transition_to(new_scene)
211
311
  new_scene.prepare_transition_from(self)
212
312
  end