metro 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/Guardfile +4 -0
- data/README.md +8 -0
- data/lib/gosu_ext/color.rb +25 -1
- data/lib/metro.rb +30 -2
- data/lib/metro/animation/animation_factory.rb +14 -0
- data/lib/metro/animation/has_animations.rb +37 -0
- data/lib/metro/error.rb +21 -0
- data/lib/metro/events/event_factory.rb +15 -0
- data/lib/metro/{event_relay.rb → events/event_relay.rb} +83 -25
- data/lib/metro/events/has_events.rb +108 -0
- data/lib/metro/events/unknown_sender.rb +5 -0
- data/lib/metro/game.rb +12 -0
- data/lib/metro/game/dsl.rb +21 -2
- data/lib/metro/missing_scene.rb +21 -0
- data/lib/metro/models/draws.rb +71 -0
- data/lib/metro/models/label.rb +2 -0
- data/lib/metro/models/menu.rb +32 -15
- data/lib/metro/models/model.rb +14 -0
- data/lib/metro/{scene_actor.rb → models/model_factory.rb} +1 -1
- data/lib/metro/scene.rb +124 -70
- data/lib/metro/scenes.rb +8 -5
- data/lib/metro/template_message.rb +31 -0
- data/lib/metro/version.rb +30 -1
- data/lib/templates/message.erb +24 -0
- data/metro.gemspec +38 -6
- data/spec/gosu_ext/color_spec.rb +80 -0
- data/spec/spec_helper.rb +18 -0
- metadata +69 -8
data/lib/metro/game.rb
CHANGED
@@ -42,6 +42,18 @@ module Metro
|
|
42
42
|
config.name
|
43
43
|
end
|
44
44
|
|
45
|
+
def authors
|
46
|
+
config.authors
|
47
|
+
end
|
48
|
+
|
49
|
+
def website
|
50
|
+
config.website
|
51
|
+
end
|
52
|
+
|
53
|
+
def contact
|
54
|
+
config.contact
|
55
|
+
end
|
56
|
+
|
45
57
|
# TODO: ZOrder related constants that belong to Starry Knight
|
46
58
|
Background, Stars, Players, UI = *0..3
|
47
59
|
|
data/lib/metro/game/dsl.rb
CHANGED
@@ -27,15 +27,34 @@ module Metro
|
|
27
27
|
def fullscreen(set_fullscreen = nil)
|
28
28
|
set_fullscreen.nil? ? @fullscreen : @fullscreen = set_fullscreen
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def debug(set_debug = nil)
|
32
32
|
set_debug.nil? ? @debug : @debug = set_debug
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def name(set_name = nil)
|
36
36
|
set_name.nil? ? @name : @name = set_name
|
37
37
|
end
|
38
38
|
|
39
|
+
def author(name)
|
40
|
+
authors.push name
|
41
|
+
end
|
42
|
+
|
43
|
+
def authors
|
44
|
+
@authors ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method :artist, :author
|
48
|
+
alias_method :designer, :author
|
49
|
+
|
50
|
+
def website(game_website = nil)
|
51
|
+
game_website ? @website = game_website : @website
|
52
|
+
end
|
53
|
+
|
54
|
+
def contact(game_contact = nil)
|
55
|
+
game_contact ? @contact = game_contact : @contact
|
56
|
+
end
|
57
|
+
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Metro
|
2
|
+
class MissingScene < Scene
|
3
|
+
scene_name :missing_scene
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :missing_scene
|
7
|
+
end
|
8
|
+
|
9
|
+
draw :title, text: "Missing Scene!",
|
10
|
+
x: 20, y: 20, z_order: 1,
|
11
|
+
x_factor: 3, y_factor: 3,
|
12
|
+
color: 0xffffffff,
|
13
|
+
model: "metro::models::label"
|
14
|
+
|
15
|
+
draw :message, text: 'The scene `#{self.class.missing_scene}` was requested, but is missing!',
|
16
|
+
x: 20, y: 100, z_order: 1,
|
17
|
+
color: 0xffffffff,
|
18
|
+
model: "metro::models::label"
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'model_factory'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
module Draws
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
#
|
13
|
+
# Define an actor with the given name and options.
|
14
|
+
#
|
15
|
+
# As a convience the draw method will define `getter` and `setter`
|
16
|
+
# methods for the specified actor.
|
17
|
+
#
|
18
|
+
# @example Defining a title label within a scene
|
19
|
+
#
|
20
|
+
# class ExampleScene
|
21
|
+
# draw :title, 'text' => 'Title Screen',
|
22
|
+
# 'x' => 20, 'y' => 20, 'z-order' => 0,
|
23
|
+
# 'x-factor' => 3, 'y-factor' => 3,
|
24
|
+
# 'color' => 0xffffffff,
|
25
|
+
# 'model' => 'metro::models::label'
|
26
|
+
#
|
27
|
+
# def show
|
28
|
+
# puts "Where is my title? #{title.x},#{title.y}"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def draw(actor_name,options = {})
|
33
|
+
scene_actor = ModelFactory.new actor_name, options
|
34
|
+
|
35
|
+
define_method actor_name do
|
36
|
+
instance_variable_get("@#{actor_name}")
|
37
|
+
end
|
38
|
+
|
39
|
+
define_method "#{actor_name}=" do |value|
|
40
|
+
instance_variable_set("@#{actor_name}",value)
|
41
|
+
end
|
42
|
+
|
43
|
+
drawings.push scene_actor
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Define several actors to be drawn.
|
48
|
+
#
|
49
|
+
def draws(*actor_names)
|
50
|
+
actor_names = actor_names.flatten.compact
|
51
|
+
|
52
|
+
actor_names.each do |actor_name|
|
53
|
+
draw actor_name
|
54
|
+
end
|
55
|
+
|
56
|
+
drawings
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# All of the model factories that have been defined.
|
61
|
+
#
|
62
|
+
def drawings
|
63
|
+
@drawings ||= []
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :actors, :drawings
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/metro/models/label.rb
CHANGED
data/lib/metro/models/menu.rb
CHANGED
@@ -6,29 +6,34 @@ module Metro
|
|
6
6
|
# target as it needs to maintain the state of the menu. When an option is selected
|
7
7
|
# an event is fired based on the name of the option.
|
8
8
|
#
|
9
|
-
# @note Only one 'menu' can be defined for a given scene
|
9
|
+
# @note Only one 'menu' can be defined for a given scene
|
10
10
|
#
|
11
11
|
class Menu < Model
|
12
|
+
|
13
|
+
event :on_up, Gosu::KbLeft, Gosu::GpLeft, Gosu::KbUp, Gosu::GpUp do
|
14
|
+
previous_option
|
15
|
+
end
|
16
|
+
|
17
|
+
event :on_up, Gosu::KbRight, Gosu::GpRight, Gosu::KbDown, Gosu::GpDown do
|
18
|
+
next_option
|
19
|
+
end
|
20
|
+
|
21
|
+
event :on_up, Gosu::KbEnter, Gosu::KbReturn, Gosu::GpButton0 do
|
22
|
+
selection
|
23
|
+
end
|
24
|
+
|
12
25
|
attr_reader :selected_index, :menu_options
|
13
26
|
|
27
|
+
attr_accessor :padding
|
28
|
+
|
14
29
|
def after_initialize
|
15
30
|
@selected_index = 0
|
31
|
+
@padding = 40
|
16
32
|
end
|
17
33
|
|
18
34
|
def window=(value)
|
19
35
|
@window = value
|
20
36
|
@menu_options = options.map {|option| Option.new option }
|
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
37
|
end
|
33
38
|
|
34
39
|
def selection
|
@@ -78,11 +83,25 @@ module Metro
|
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
86
|
+
#
|
87
|
+
# The Option represents a choice within the menu.
|
88
|
+
#
|
81
89
|
class Option
|
82
90
|
|
91
|
+
#
|
92
|
+
# The raw data that was used to create the option.
|
93
|
+
#
|
83
94
|
attr_reader :data
|
84
95
|
|
85
|
-
|
96
|
+
#
|
97
|
+
# The human readable name of the option.
|
98
|
+
#
|
99
|
+
attr_accessor :name
|
100
|
+
|
101
|
+
#
|
102
|
+
# The method to execute within the scene when the option is selected.
|
103
|
+
#
|
104
|
+
attr_accessor :method
|
86
105
|
|
87
106
|
def initialize(data)
|
88
107
|
@data = data
|
@@ -94,9 +113,7 @@ module Metro
|
|
94
113
|
@name = data
|
95
114
|
@method = data.to_s.downcase.gsub(/\s/,'_').gsub(/^[^a-zA-Z]*/,'').gsub(/[^a-zA-Z0-9\s_]/,'')
|
96
115
|
end
|
97
|
-
|
98
116
|
end
|
99
|
-
|
100
117
|
end
|
101
118
|
|
102
119
|
end
|
data/lib/metro/models/model.rb
CHANGED
@@ -40,6 +40,20 @@ module Metro
|
|
40
40
|
#
|
41
41
|
def after_initialize ; end
|
42
42
|
|
43
|
+
#
|
44
|
+
# Generate a custom notification event with the given name.
|
45
|
+
#
|
46
|
+
# @param [Symbol] event the name of the notification to generate.
|
47
|
+
#
|
48
|
+
def notification(event)
|
49
|
+
scene.notification(event.to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Allows for the definition of events within the scene.
|
54
|
+
#
|
55
|
+
include HasEvents
|
56
|
+
|
43
57
|
#
|
44
58
|
# Returns the color of the model. In most cases where color is a prominent
|
45
59
|
# attribute (e.g. label) this will be the color. In the cases where color
|
data/lib/metro/scene.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
require_relative 'scene_view/scene_view'
|
2
|
-
|
3
|
-
require_relative '
|
2
|
+
|
3
|
+
require_relative 'events/has_events'
|
4
|
+
require_relative 'events/event_relay'
|
5
|
+
require_relative 'events/unknown_sender'
|
6
|
+
|
7
|
+
require_relative 'models/draws'
|
8
|
+
|
9
|
+
require_relative 'animation/has_animations'
|
4
10
|
require_relative 'animation/animation'
|
5
11
|
|
6
12
|
module Metro
|
@@ -30,19 +36,6 @@ module Metro
|
|
30
36
|
#
|
31
37
|
def after_initialize ; end
|
32
38
|
|
33
|
-
#
|
34
|
-
# The events method is where a scene has access to configure the events that it
|
35
|
-
# would like to listen for during the scene.
|
36
|
-
#
|
37
|
-
# @note This method should be implemented in the Scene subclass.
|
38
|
-
#
|
39
|
-
# @param [EventRelay] e is the EventRelay that you can register for button up,
|
40
|
-
# button down, or button held events.
|
41
|
-
#
|
42
|
-
# @see EventRelay
|
43
|
-
#
|
44
|
-
def events(e) ; end
|
45
|
-
|
46
39
|
#
|
47
40
|
# This method is called right after the scene has been adopted by the window
|
48
41
|
#
|
@@ -89,53 +82,58 @@ module Metro
|
|
89
82
|
#
|
90
83
|
def prepare_transition_from(old_scene) ; end
|
91
84
|
|
85
|
+
include Draws
|
92
86
|
|
93
87
|
#
|
94
|
-
#
|
88
|
+
# When an actor is defined, through the class method `draw` a getter and setter method
|
89
|
+
# is defined. However, it is a better interface internally not to rely heavily on send
|
90
|
+
# and have this small amount of obfuscation in the event that this needs to change.
|
95
91
|
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
92
|
+
# @return the actor with the given name.
|
93
|
+
#
|
94
|
+
def actor(name)
|
95
|
+
send(name)
|
108
96
|
end
|
109
97
|
|
110
98
|
#
|
111
|
-
#
|
99
|
+
# Post a custom notification event. This will trigger any objects that are listening
|
100
|
+
# for custom events.
|
112
101
|
#
|
113
|
-
def
|
114
|
-
actor_names = actor_names.flatten.compact
|
102
|
+
def notification(event)
|
115
103
|
|
116
|
-
|
117
|
-
|
118
|
-
|
104
|
+
# __sender__ is made available through the sender gem, this is solely to make the
|
105
|
+
# the api call to generate a notification simply `#notification`. Freeing the caller
|
106
|
+
# from having to include themself in the execution.
|
107
|
+
#
|
108
|
+
# @note if the sender functionality proves troublesome across platforms this can
|
109
|
+
# be dropped and simply require the sender to be included.
|
110
|
+
#
|
111
|
+
sender = __sender__ rescue UnknownSender
|
119
112
|
|
120
|
-
|
113
|
+
event_relays.each do |relay|
|
114
|
+
relay.fire_events_for_notification(event,sender)
|
115
|
+
end
|
121
116
|
end
|
122
117
|
|
123
118
|
#
|
124
|
-
#
|
119
|
+
# A scene has events which it will register when the window is established.
|
125
120
|
#
|
126
|
-
|
127
|
-
@scene_actors ||= []
|
128
|
-
end
|
121
|
+
include HasEvents
|
129
122
|
|
130
123
|
#
|
131
|
-
#
|
124
|
+
# A scene defines animations which it will execute when the scene starts
|
125
|
+
#
|
126
|
+
include HasAnimations
|
127
|
+
|
128
|
+
#
|
129
|
+
# Setups up the Actors for the Scene based on the ModelFactories that have been
|
132
130
|
# defined.
|
133
131
|
#
|
134
132
|
# @note this method should not be overriden, otherwise the actors will perish!
|
135
133
|
# @see #after_initialize
|
136
134
|
#
|
137
135
|
def initialize
|
138
|
-
self.class.
|
136
|
+
self.class.actors.each do |scene_actor|
|
139
137
|
actor_data = { 'name' => scene_actor.name }.merge (view[scene_actor.name] || {})
|
140
138
|
actor_instance = scene_actor.create(actor_data)
|
141
139
|
actor_instance.scene = self
|
@@ -163,24 +161,50 @@ module Metro
|
|
163
161
|
def window=(window)
|
164
162
|
@window = window
|
165
163
|
|
166
|
-
|
164
|
+
event_relays.clear
|
167
165
|
|
168
|
-
|
169
|
-
|
166
|
+
register_events!
|
167
|
+
register_actors!
|
168
|
+
register_animations!
|
170
169
|
|
171
|
-
|
170
|
+
show
|
171
|
+
end
|
172
172
|
|
173
|
-
|
173
|
+
#
|
174
|
+
# Register all the events that were defined for this scene.
|
175
|
+
#
|
176
|
+
def register_events!
|
177
|
+
register_events_for_target(self,self.class.events)
|
178
|
+
end
|
174
179
|
|
175
|
-
|
180
|
+
#
|
181
|
+
# Register all the actors that were defined for this scene.
|
182
|
+
#
|
183
|
+
def register_actors!
|
184
|
+
self.class.actors.each { |actor| register_actor(actor) }
|
185
|
+
end
|
176
186
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
187
|
+
#
|
188
|
+
# Register all the animations that were defined for this scene.
|
189
|
+
#
|
190
|
+
def register_animations!
|
191
|
+
self.class.animations.each do |animation|
|
192
|
+
animate animation.options, &animation.on_complete_block
|
181
193
|
end
|
194
|
+
end
|
182
195
|
|
183
|
-
|
196
|
+
#
|
197
|
+
# Registering an actor involves setting up the actor within
|
198
|
+
# the window, adding them to the list of things that need to be
|
199
|
+
# drawn and then registering any eventst that they might have.
|
200
|
+
#
|
201
|
+
def register_actor(actor_factory)
|
202
|
+
registering_actor = actor(actor_factory.name)
|
203
|
+
registering_actor.window = window
|
204
|
+
|
205
|
+
drawers.push(registering_actor)
|
206
|
+
|
207
|
+
register_events_for_target(registering_actor,registering_actor.class.events)
|
184
208
|
end
|
185
209
|
|
186
210
|
#
|
@@ -210,7 +234,7 @@ module Metro
|
|
210
234
|
#
|
211
235
|
def self.scene_name(scene_name=nil)
|
212
236
|
@scene_name ||= begin
|
213
|
-
root_name = to_s
|
237
|
+
root_name = to_s.gsub(/Scene$/,'')
|
214
238
|
root_name.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
215
239
|
root_name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
216
240
|
root_name.downcase!
|
@@ -251,7 +275,7 @@ module Metro
|
|
251
275
|
# is used for animations.
|
252
276
|
#
|
253
277
|
def enqueue(updater)
|
254
|
-
|
278
|
+
updaters.push(updater)
|
255
279
|
end
|
256
280
|
|
257
281
|
#
|
@@ -259,28 +283,47 @@ module Metro
|
|
259
283
|
# to be an implicit animation.
|
260
284
|
#
|
261
285
|
def animate(options,&block)
|
286
|
+
options[:actor] = actor(options[:actor]) if options[:actor].is_a? Symbol
|
262
287
|
animation = Metro::ImplicitAnimation.new options.merge(context: self)
|
263
288
|
animation.on_complete(&block) if block
|
264
289
|
enqueue animation
|
265
290
|
end
|
266
291
|
|
292
|
+
|
293
|
+
#
|
294
|
+
# The objects that need to be executed on every update. These objects are traditionally
|
295
|
+
# animations or window events for held pressed buttons. But can be any objects that responds
|
296
|
+
# to the method #update.
|
297
|
+
#
|
298
|
+
def updaters
|
299
|
+
@updaters ||= []
|
300
|
+
end
|
301
|
+
|
267
302
|
#
|
268
303
|
# The `base_update` method is called by the Game Window. This is to allow for any
|
269
304
|
# special update needs to be handled before calling the traditional `update` method
|
270
305
|
# defined in the subclassed Scene.
|
271
306
|
#
|
272
307
|
def base_update
|
273
|
-
|
308
|
+
updaters.each { |updater| updater.update }
|
274
309
|
update
|
275
310
|
end
|
276
311
|
|
312
|
+
#
|
313
|
+
# The objects that need to be drawn with every draw cycle. These objects are traditionally
|
314
|
+
# the model objects, like the actors defined within the scene.
|
315
|
+
#
|
316
|
+
def drawers
|
317
|
+
@drawers ||= []
|
318
|
+
end
|
319
|
+
|
277
320
|
#
|
278
321
|
# The `base_draw` method is called by the Game Window. This is to allow for any
|
279
322
|
# special drawing needs to be handled before calling the traditional `draw` method
|
280
323
|
# defined in the subclassed Scene.
|
281
324
|
#
|
282
325
|
def base_draw
|
283
|
-
|
326
|
+
drawers.each { |drawer| drawer.draw }
|
284
327
|
draw
|
285
328
|
end
|
286
329
|
|
@@ -311,9 +354,9 @@ module Metro
|
|
311
354
|
def _prepare_transition(new_scene)
|
312
355
|
log.debug "Preparing to transition from scene #{self} to #{new_scene}"
|
313
356
|
|
314
|
-
new_scene.class.
|
315
|
-
new_actor = new_scene.
|
316
|
-
current_actor =
|
357
|
+
new_scene.class.actors.find_all {|actor_factory| actor_factory.load_from_previous_scene? }.each do |actor_factory|
|
358
|
+
new_actor = new_scene.actor(actor_factory.name)
|
359
|
+
current_actor = actor(actor_factory.name)
|
317
360
|
new_actor._load current_actor._save
|
318
361
|
end
|
319
362
|
|
@@ -321,6 +364,26 @@ module Metro
|
|
321
364
|
new_scene.prepare_transition_from(self)
|
322
365
|
end
|
323
366
|
|
367
|
+
#
|
368
|
+
# Helper method that is used internally to setup the events for the specified target.
|
369
|
+
#
|
370
|
+
# @param [Object] target the intended target for the specified events. This object
|
371
|
+
# will have the appropriate methods and functionality to respond appropriately
|
372
|
+
# to the action blocks defined in the methods.
|
373
|
+
#
|
374
|
+
# @param [Array<EventFactory>] events an array of EventFactory objects that need to now
|
375
|
+
# be mapped to the specified target.
|
376
|
+
#
|
377
|
+
def register_events_for_target(target,events)
|
378
|
+
target_relay = EventRelay.new(target,window)
|
379
|
+
|
380
|
+
events.each do |target_event|
|
381
|
+
target_relay.send target_event.event, *target_event.buttons, &target_event.block
|
382
|
+
end
|
383
|
+
|
384
|
+
event_relays.push(target_relay)
|
385
|
+
end
|
386
|
+
|
324
387
|
#
|
325
388
|
# The events object that is configured through the {#events} method, which stores
|
326
389
|
# all the gamepad and keyboard events defined. By default a scene has an event
|
@@ -329,17 +392,8 @@ module Metro
|
|
329
392
|
# @see Events
|
330
393
|
# @see #add_event_relay
|
331
394
|
#
|
332
|
-
|
333
|
-
|
334
|
-
#
|
335
|
-
# Add an additional event relay to the list of event relays. It is appended
|
336
|
-
# to the end of the list of relays.
|
337
|
-
#
|
338
|
-
# @param [EventRelay] event_relay an event relay instance that will now
|
339
|
-
# receive events generated from this scene.
|
340
|
-
#
|
341
|
-
def add_event_relay(event_relay)
|
342
|
-
@event_relays << event_relay
|
395
|
+
def event_relays
|
396
|
+
@event_relays ||= []
|
343
397
|
end
|
344
398
|
|
345
399
|
#
|