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 ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile CHANGED
@@ -2,3 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in metro.gemspec
4
4
  gemspec
5
+
6
+
7
+ group 'development' do
8
+ gem 'guard'
9
+ gem 'guard-rspec'
10
+ gem 'rb-fsevent', '~> 0.9.1'
11
+ end
data/Guardfile ADDED
@@ -0,0 +1,4 @@
1
+ guard 'rspec', version: 2, cli: "-c -f d" do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ end
data/README.md CHANGED
@@ -1,3 +1,11 @@
1
+ ```
2
+ ______ ___ _____
3
+ ___ |/ /_____ __ /_______________
4
+ __ /|_/ / _ _ \_ __/__ ___/_ __ \
5
+ _ / / / / __// /_ _ / / /_/ /
6
+ /_/ /_/ \___/ \__/ /_/ \____/
7
+
8
+ ```
1
9
  # metro
2
10
 
3
11
  Metro is a framework built around [gosu](https://github.com/jlnr/gosu) (the 2D game development library in Ruby). The goal of Metro is to enforce common conceptual structures and conventions making it easier to quickly generate a game.
@@ -12,7 +12,7 @@ class Gosu::Color
12
12
  if value.is_a? Gosu::Color
13
13
  gosu_initialize value.alpha, value.red, value.green, value.blue
14
14
  elsif value.is_a? String
15
- gosu_initialize value.to_i(16)
15
+ gosu_initialize *Array(self.class.parse_string(value))
16
16
  else
17
17
  gosu_initialize value
18
18
  end
@@ -21,4 +21,28 @@ class Gosu::Color
21
21
  end
22
22
  end
23
23
 
24
+ def self.parse_string(value)
25
+ parse_hex(value) || parse_rgb(value) || parse_rgba(value) || [ 255, 255, 255, 255 ]
26
+ end
27
+
28
+ def self.parse_rgba(rgba)
29
+ if rgba =~ /rgba\(([\d]{1,3}),([\d]{1,3}),([\d]{1,3}),(\d(?:\.\d)?)\)/
30
+ [ (255 * $4.to_f).floor.to_i, $1.to_i, $2.to_i, $3.to_i ]
31
+ end
32
+ end
33
+
34
+ def self.parse_rgb(rgb)
35
+ if rgb =~ /rgb\(([\d]{1,3}),([\d]{1,3}),([\d]{1,3})\)/
36
+ [ 255, $1.to_i, $2.to_i, $3.to_i ]
37
+ end
38
+ end
39
+
40
+ def self.parse_hex(hex)
41
+ if hex =~ /0x([A-Fa-f0-9]{8})/
42
+ hex.to_i(16)
43
+ elsif hex =~ /#([A-Fa-f0-9]{6})/
44
+ "0xFF#{$1}".to_i(16)
45
+ end
46
+ end
47
+
24
48
  end
data/lib/metro.rb CHANGED
@@ -2,20 +2,24 @@ require 'gosu'
2
2
  require 'gosu_ext/color'
3
3
 
4
4
  require 'logger'
5
-
5
+ require 'erb'
6
6
 
7
7
  require 'metro/version'
8
+ require 'metro/error'
9
+ require 'metro/template_message'
8
10
  require 'metro/window'
9
11
  require 'metro/game'
10
12
  require 'metro/scene'
11
13
  require 'metro/models/model'
12
14
  require 'metro/models/generic'
13
15
 
16
+ require_relative 'metro/missing_scene'
17
+
18
+
14
19
  def asset_path(name)
15
20
  File.join Dir.pwd, "assets", name
16
21
  end
17
22
 
18
-
19
23
  def log
20
24
  @log ||= begin
21
25
  logger = Logger.new(STDOUT)
@@ -57,6 +61,7 @@ module Metro
57
61
  end
58
62
 
59
63
  def load_game_configuration(filename)
64
+ game_files_exist!(filename)
60
65
  game_contents = File.read(filename)
61
66
  game_block = lambda {|instance| eval(game_contents) }
62
67
  game = Game::DSL.parse(&game_block)
@@ -70,4 +75,27 @@ module Metro
70
75
  window.show
71
76
  end
72
77
 
78
+ def game_files_exist!(*files)
79
+ error_messages = files.compact.flatten.map { |file| game_file_exists?(file) }.reject {|exist| exist == true }
80
+ unless error_messages.empty?
81
+ display_error_message(error_messages)
82
+ exit 1
83
+ end
84
+ end
85
+
86
+ def game_file_exists?(file)
87
+ unless File.exists? file
88
+ Error.new title: "Unable to find Metro game file",
89
+ message: "The specified file `#{file}` which is required to run the game could not be found.",
90
+ details: [ "Ensure you have specified the correct file", "Ensure that the file exists at that location" ]
91
+ else
92
+ true
93
+ end
94
+ end
95
+
96
+ def display_error_message(messages)
97
+ message = TemplateMessage.new messages: messages, website: WEBSITE, email: CONTACT_EMAILS
98
+ warn message
99
+ end
100
+
73
101
  end
@@ -0,0 +1,14 @@
1
+ module Metro
2
+
3
+ class AnimationFactory
4
+
5
+ attr_reader :options, :on_complete_block
6
+
7
+ def initialize(options = {},&block)
8
+ @options = options
9
+ @on_complete_block = block
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'animation_factory'
2
+
3
+ module Metro
4
+ module HasAnimations
5
+
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ #
13
+ # Define an animation to execute when the scene starts.
14
+ #
15
+ # @example Defining an animation that fades in and moves a logo when it is
16
+ # done, transition to the title scene.
17
+ #
18
+ # animate actor: :logo, to: { y: 80, alpha: 50 }, interval: 120 do
19
+ # transition_to :title
20
+ # end
21
+ #
22
+ def animate(options,&block)
23
+ scene_animation = AnimationFactory.new options, &block
24
+ animations.push scene_animation
25
+ end
26
+
27
+ #
28
+ # All the animations that are defined for the scene to be run the scene starts.
29
+ #
30
+ def animations
31
+ @animations ||= []
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ class Error
2
+
3
+ def initialize(details = {})
4
+ @title = details[:title]
5
+ @message = details[:message]
6
+ @details = details[:details]
7
+ end
8
+
9
+ def title
10
+ "ERROR: #{@title.upcase}"
11
+ end
12
+
13
+ def message
14
+ @message
15
+ end
16
+
17
+ def details
18
+ @details.map {|detail| "* #{detail}" }.join("\n")
19
+ end
20
+
21
+ end
@@ -0,0 +1,15 @@
1
+ module Metro
2
+
3
+ class EventFactory
4
+
5
+ attr_reader :event, :buttons, :block
6
+
7
+ def initialize(event,buttons = [],&block)
8
+ @event = event
9
+ @buttons = buttons
10
+ @block = block
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -56,6 +56,7 @@ module Metro
56
56
  @up_actions ||= {}
57
57
  @down_actions ||= {}
58
58
  @held_actions ||= {}
59
+ @custom_notifications ||= Hash.new([])
59
60
  end
60
61
 
61
62
  attr_reader :target, :window
@@ -68,9 +69,7 @@ module Metro
68
69
  # @example Registering for a button down event to call a method named 'previous_option'
69
70
  #
70
71
  # class ExampleScene
71
- # def events(e)
72
- # e.on_down Gosu::GpLeft, Gosu::GpUp, do: :previous_option
73
- # end
72
+ # event :on_down, Gosu::GpLeft, Gosu::GpUp, do: :previous_option
74
73
  #
75
74
  # def previous_option
76
75
  # @selected_index = @selected_index - 1
@@ -85,11 +84,9 @@ module Metro
85
84
  # @example Registering for a button down event with a block of code to execute
86
85
  #
87
86
  # class ExampleScene
88
- # def events(e)
89
- # e.on_down Gosu::GpLeft, Gosu::GpUp do
90
- # @selected_index = @selected_index - 1
91
- # @selected_index = options.length - 1 if @selected_index <= -1
92
- # end
87
+ # event :on_down, Gosu::GpLeft, Gosu::GpUp do
88
+ # @selected_index = @selected_index - 1
89
+ # @selected_index = options.length - 1 if @selected_index <= -1
93
90
  # end
94
91
  # end
95
92
  #
@@ -108,9 +105,7 @@ module Metro
108
105
  # @example Registering for a button down event to call a method named 'next_option'
109
106
  #
110
107
  # class ExampleScene
111
- # def events(e)
112
- # e.on_up Gosu::KbEscape, do: :leave_scene
113
- # end
108
+ # event :on_up, Gosu::KbEscape, do: :leave_scene
114
109
  #
115
110
  # def leave_scene
116
111
  # transition_to :title
@@ -123,10 +118,8 @@ module Metro
123
118
  # @example Registering for a button up event with a block of code to execute
124
119
  #
125
120
  # class ExampleScene
126
- # def events(e)
127
- # e.on_up Gosu::KbEscape do
128
- # transition_to :title
129
- # end
121
+ # event :on_up, Gosu::KbEscape do
122
+ # transition_to :title
130
123
  # end
131
124
  # end
132
125
  #
@@ -147,18 +140,16 @@ module Metro
147
140
  # @example Registering for button held events
148
141
  #
149
142
  # class ExampleScene
150
- # def events(e)
151
- # e.on_hold Gosu::KbLeft, Gosu::GpLeft do
152
- # player.turn_left
153
- # end
154
- #
155
- # e.on_hold Gosu::KbRight, Gosu::GpRight do
156
- # player.turn_right
157
- # end
143
+ # event :on_hold Gosu::KbLeft, Gosu::GpLeft do
144
+ # player.turn_left
145
+ # end
158
146
  #
159
- # e.on_hold Gosu::KbUp, Gosu::GpButton0, do: :calculate_accleration
147
+ # event :on_hold, Gosu::KbRight, Gosu::GpRight do
148
+ # player.turn_right
160
149
  # end
161
150
  #
151
+ # event :on_hold, Gosu::KbUp, Gosu::GpButton0, do: :calculate_accleration
152
+ #
162
153
  # def calculate_acceleration
163
154
  # long_complicated_calculated_result = 0
164
155
  # # ... multi-line calculations to determine the player acceleration ...
@@ -171,7 +162,45 @@ module Metro
171
162
  _on(@held_actions,args,block)
172
163
  end
173
164
 
174
- attr_reader :up_actions, :down_actions, :held_actions
165
+ #
166
+ # Register for a custom notification event. These events are fired when
167
+ # another object within the game posts a notification with matching criteria.
168
+ # If there has indeed been a match, then the stored action block will be fired.
169
+ #
170
+ # When the action block is specified is defined with no parameters it is assumed that
171
+ # that the code should be executed within the context of the object that defined
172
+ # the action, the 'target'.
173
+ #
174
+ # @example Registering for a save complete event that would re-enable a menu.
175
+ #
176
+ # class ExampleScene
177
+ # event :notification, :save_complete do
178
+ # menu.enabled!
179
+ # end
180
+ # end
181
+ #
182
+ # The action block can also be specified with two parameters. In this case the code is
183
+ # no longer executed within the context of the object and is instead provided the
184
+ # the action target and the action source.
185
+ #
186
+ # @example Registering for a win game event that explicitly states the target and source.
187
+ #
188
+ # class ExampleScene
189
+ #
190
+ # event :notification, :win_game do |target,winner|
191
+ # target.declare_winner winner
192
+ # end
193
+ #
194
+ # def declare_winner(winning_player)
195
+ # # ...
196
+ # end
197
+ # end
198
+ #
199
+ def notification(param,&block)
200
+ custom_notifications[param.to_sym] = custom_notifications[param.to_sym] + [ block ]
201
+ end
202
+
203
+ attr_reader :up_actions, :down_actions, :held_actions, :custom_notifications
175
204
 
176
205
  def _on(hash,args,block)
177
206
  options = (args.last.is_a?(Hash) ? args.pop : {})
@@ -220,6 +249,35 @@ module Metro
220
249
  down_actions[id] || lambda {|instance| send(:down_action_missing,id) if respond_to?(:down_action_missing) }
221
250
  end
222
251
 
252
+ #
253
+ # Fire all events mapped to the matching notification.
254
+ #
255
+ def fire_events_for_notification(event,sender)
256
+ notification_actions = custom_notifications[event]
257
+ notification_actions.each do |action|
258
+ _fire_event_for_notification(event,sender,action)
259
+ end
260
+ end
261
+
262
+ #
263
+ # Fire a single event based on the matched notification.
264
+ #
265
+ # An action without any parameters is assumed to be executed within the contexxt
266
+ # of the target. If there are two parameters we will simply execute the action and
267
+ # pass it both the target and the sender.
268
+ #
269
+ # @TODO: Allow for the blocks to be specified with one parameter: source (and executed
270
+ # within the context of the target)
271
+ #
272
+ # @TODO: Allow for the blocks to be specified with three parameters: source, target, event
273
+ #
274
+ def _fire_event_for_notification(event,sender,action)
275
+ if action.arity == 2
276
+ action.call(target,sender)
277
+ else
278
+ target.instance_eval(&action)
279
+ end
280
+ end
223
281
 
224
282
  end
225
283
  end
@@ -0,0 +1,108 @@
1
+ require_relative 'event_factory'
2
+
3
+ module Metro
4
+
5
+ module HasEvents
6
+
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ #
14
+ # Register an event for the scene.
15
+ #
16
+ # @example Registering for a save complete event that would re-enable a menu.
17
+ #
18
+ # class ExampleScene
19
+ # event :notification, :save_complete do
20
+ # menu.enabled!
21
+ # end
22
+ # end
23
+ #
24
+ # @example Registering for button held events
25
+ #
26
+ # class ExampleScene
27
+ # event :on_hold Gosu::KbLeft, Gosu::GpLeft do
28
+ # player.turn_left
29
+ # end
30
+ #
31
+ # event :on_hold, Gosu::KbRight, Gosu::GpRight do
32
+ # player.turn_right
33
+ # end
34
+ #
35
+ # event :on_hold, Gosu::KbUp, Gosu::GpButton0, do: :calculate_accleration
36
+ #
37
+ # def calculate_acceleration
38
+ # long_complicated_calculated_result = 0
39
+ # # ... multi-line calculations to determine the player acceleration ...
40
+ # player.accelerate = long_complicated_calculated_result
41
+ # end
42
+ # end
43
+ #
44
+ # @example Registering for a button down event to call a method named 'next_option'
45
+ #
46
+ # class ExampleScene
47
+ # event :on_up, Gosu::KbEscape, do: :leave_scene
48
+ #
49
+ # def leave_scene
50
+ # transition_to :title
51
+ # end
52
+ # end
53
+ #
54
+ # Here in this scene if the Escape Key is pressed and released the example scene
55
+ # will transition to the title scene.
56
+ #
57
+ # @example Registering for a button up event with a block of code to execute
58
+ #
59
+ # class ExampleScene
60
+ # event :on_up, Gosu::KbEscape do
61
+ # transition_to :title
62
+ # end
63
+ # end
64
+ #
65
+ # @example Registering for a button down event to call a method named 'previous_option'
66
+ #
67
+ # class ExampleScene
68
+ # event :on_down, Gosu::GpLeft, Gosu::GpUp, do: :previous_option
69
+ #
70
+ # def previous_option
71
+ # @selected_index = @selected_index - 1
72
+ # @selected_index = options.length - 1 if @selected_index <= -1
73
+ # end
74
+ # end
75
+ #
76
+ # Here in this scene if the GpLeft or GpUp buttons are pressed down the method
77
+ # `previous_options` will be executed.
78
+ #
79
+ #
80
+ # @example Registering for a button down event with a block of code to execute
81
+ #
82
+ # class ExampleScene
83
+ # event :on_down, Gosu::GpLeft, Gosu::GpUp do
84
+ # @selected_index = @selected_index - 1
85
+ # @selected_index = options.length - 1 if @selected_index <= -1
86
+ # end
87
+ # end
88
+ #
89
+ # This example uses a block instead of a method name but it is absolultey the same
90
+ # as the last example.
91
+ #
92
+ def event(event_type,*buttons,&block)
93
+ scene_event = EventFactory.new event_type, buttons, &block
94
+ events.push scene_event
95
+ end
96
+
97
+ #
98
+ # @return a list of all the EventFactories defined for the scene
99
+ #
100
+ def events
101
+ @events ||= []
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end