metro 0.1.4 → 0.1.5
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.
- data/changelog.md +9 -0
- data/lib/commands/generate_model.rb +2 -2
- data/lib/commands/generate_scene.rb +2 -2
- data/lib/commands/generate_view.rb +1 -1
- data/lib/gosu_ext/color.rb +16 -2
- data/lib/metro.rb +50 -10
- data/lib/metro/events/event_data.rb +61 -0
- data/lib/metro/events/event_relay.rb +10 -3
- data/lib/metro/events/hit_list.rb +77 -0
- data/lib/metro/models/draws.rb +6 -1
- data/lib/metro/models/grid_drawer.rb +57 -0
- data/lib/metro/models/image.rb +18 -2
- data/lib/metro/models/label.rb +22 -6
- data/lib/metro/models/menu.rb +20 -0
- data/lib/metro/models/model.rb +47 -6
- data/lib/metro/models/model_factory.rb +2 -2
- data/lib/metro/models/rectangle_bounds.rb +28 -0
- data/lib/metro/scene.rb +46 -11
- data/lib/metro/scenes.rb +10 -9
- data/lib/metro/transitions/edit_transition_scene.rb +73 -0
- data/lib/metro/transitions/scene_transitions.rb +8 -2
- data/lib/metro/transitions/transition_scene.rb +2 -1
- data/lib/metro/version.rb +1 -1
- data/lib/metro/views/json_view.rb +60 -0
- data/lib/metro/{scene_view → views}/no_view.rb +12 -4
- data/lib/metro/views/parsers.rb +26 -0
- data/lib/metro/views/scene_view.rb +107 -0
- data/lib/metro/views/view.rb +125 -0
- data/lib/metro/views/writers.rb +28 -0
- data/lib/metro/views/yaml_view.rb +94 -0
- data/lib/metro/window.rb +19 -0
- data/metro.gemspec +1 -0
- data/spec/core_ext/string_spec.rb +13 -13
- data/spec/metro/scene_spec.rb +15 -0
- data/spec/metro/scene_views/json_view_spec.rb +27 -0
- data/spec/metro/scene_views/yaml_view_spec.rb +1 -1
- data/spec/metro/views/view_spec.rb +53 -0
- metadata +41 -11
- data/lib/core_ext/string.rb +0 -15
- data/lib/metro/scene_view/json_view.rb +0 -41
- data/lib/metro/scene_view/scene_view.rb +0 -83
- data/lib/metro/scene_view/yaml_view.rb +0 -45
data/changelog.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Metro
|
2
2
|
|
3
|
+
## 0.1.5 / 2012-11-01
|
4
|
+
|
5
|
+
* Metro.reload! will reload all game classes
|
6
|
+
* Scenes can now an editable state where the position of items can be
|
7
|
+
updated and saved.
|
8
|
+
* Event blocks can optionally receive an event object. The event object
|
9
|
+
includes modifier key information.
|
10
|
+
|
11
|
+
|
3
12
|
## 0.1.4 / 2012-10-28
|
4
13
|
|
5
14
|
* Removed dependency on the sender gem so metro is playable on Windows
|
@@ -6,12 +6,12 @@ module Metro
|
|
6
6
|
|
7
7
|
def scene_filename
|
8
8
|
scene_name = name.gsub(/_?Scene$/i,'')
|
9
|
-
"#{scene_name.
|
9
|
+
"#{scene_name.underscore}_scene"
|
10
10
|
end
|
11
11
|
|
12
12
|
def scene_class_name
|
13
13
|
scene_name = name.gsub(/_?Scene$/i,'')
|
14
|
-
"#{scene_name.
|
14
|
+
"#{scene_name.classify}Scene"
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
data/lib/gosu_ext/color.rb
CHANGED
@@ -22,9 +22,9 @@ class Gosu::Color
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.parse_string(value)
|
25
|
-
parse_hex(value) || parse_rgb(value) || parse_rgba(value) || [ 255, 255, 255, 255 ]
|
25
|
+
parse_hex(value) || parse_rgb(value) || parse_rgba(value) || parse_gosu_color_string(value) || [ 255, 255, 255, 255 ]
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def self.parse_rgba(rgba)
|
29
29
|
if rgba =~ /rgba\(([\d]{1,3}),([\d]{1,3}),([\d]{1,3}),(\d(?:\.\d)?)\)/
|
30
30
|
[ (255 * $4.to_f).floor.to_i, $1.to_i, $2.to_i, $3.to_i ]
|
@@ -45,4 +45,18 @@ class Gosu::Color
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
def self.parse_gosu_color_string(string)
|
49
|
+
if string =~ /\(ARGB: ([\d]{1,3})\/([\d]{1,3})\/([\d]{1,3})\/([\d]{1,3})\)/
|
50
|
+
[ $1.to_i, $2.to_i, $3.to_i, $4.to_i ]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
"rgba(#{red},#{green},#{blue},#{alpha / 255.to_f})"
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_json(*params)
|
59
|
+
to_s.to_json
|
60
|
+
end
|
61
|
+
|
48
62
|
end
|
data/lib/metro.rb
CHANGED
@@ -2,9 +2,10 @@ require 'gosu'
|
|
2
2
|
require 'gosu_ext/color'
|
3
3
|
require 'gosu_ext/gosu_constants'
|
4
4
|
require 'i18n'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/dependencies'
|
7
|
+
require 'active_support/inflector'
|
5
8
|
|
6
|
-
|
7
|
-
require 'core_ext/string'
|
8
9
|
require 'core_ext/numeric'
|
9
10
|
require 'logger'
|
10
11
|
require 'erb'
|
@@ -49,33 +50,72 @@ module Metro
|
|
49
50
|
# the value uses the default filename.
|
50
51
|
#
|
51
52
|
def run(filename=default_game_filename)
|
52
|
-
|
53
|
-
|
53
|
+
move_to_game_directory!(filename)
|
54
|
+
load_game_files!
|
54
55
|
load_game_configuration(filename)
|
55
56
|
configure_controls!
|
56
57
|
start_game
|
57
58
|
end
|
58
59
|
|
60
|
+
def load_game_files!
|
61
|
+
prepare_watcher!
|
62
|
+
load_game_files
|
63
|
+
execute_watcher!
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :reload!, :load_game_files!
|
67
|
+
|
59
68
|
private
|
60
69
|
|
70
|
+
def move_to_game_directory!(filename)
|
71
|
+
game_directory = File.dirname(filename)
|
72
|
+
Dir.chdir game_directory
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# The watcher will keep track of all the constants that were added to the Object
|
77
|
+
# Namespace after the start of the execution of the game. This will allow for only
|
78
|
+
# those objects to be reloaded.
|
79
|
+
#
|
80
|
+
def watcher
|
81
|
+
@watcher ||= ActiveSupport::Dependencies::WatchStack.new
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# The watcher should watch the Object Namespace for any changes. Any constants
|
86
|
+
# that are added will be tracked from this point forward.
|
87
|
+
#
|
88
|
+
def prepare_watcher!
|
89
|
+
ActiveSupport::Dependencies.clear
|
90
|
+
watcher.watch_namespaces([ Object ])
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# The watcher will now mark all the constants that it has watched being loaded
|
95
|
+
# as unloadable. Doing so exhausts the list of constants found so the watcher
|
96
|
+
# will be empty.
|
97
|
+
#
|
98
|
+
# @note an exception is raised if the watcher is not prepared every time this
|
99
|
+
# is called.
|
100
|
+
#
|
101
|
+
def execute_watcher!
|
102
|
+
watcher.new_constants.each { |constant| unloadable constant }
|
103
|
+
end
|
104
|
+
|
61
105
|
def load_game_files
|
62
106
|
$LOAD_PATH.unshift(Dir.pwd) unless $LOAD_PATH.include?(Dir.pwd)
|
63
107
|
load_paths 'models', 'scenes', 'lib'
|
64
108
|
end
|
65
|
-
|
66
|
-
def change_into_game_directory(filename)
|
67
|
-
game_directory = File.dirname(filename)
|
68
|
-
Dir.chdir game_directory
|
69
|
-
end
|
70
109
|
|
71
110
|
def load_paths(*paths)
|
72
111
|
paths.flatten.compact.each {|path| load_path path }
|
73
112
|
end
|
74
113
|
|
75
114
|
def load_path(path)
|
76
|
-
Dir["#{path}/**/*.rb"].each {|model|
|
115
|
+
Dir["#{path}/**/*.rb"].each {|model| require_or_load model }
|
77
116
|
end
|
78
117
|
|
118
|
+
|
79
119
|
def load_game_configuration(filename)
|
80
120
|
gamefile = File.basename(filename)
|
81
121
|
game_files_exist!(gamefile)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Metro
|
2
|
+
class EventData
|
3
|
+
|
4
|
+
attr_reader :mouse_x, :mouse_y, :created_at
|
5
|
+
|
6
|
+
def initialize(window)
|
7
|
+
@created_at = Time.now
|
8
|
+
@mouse_x = window.mouse_x
|
9
|
+
@mouse_y = window.mouse_y
|
10
|
+
|
11
|
+
capture_modifier_keys(window)
|
12
|
+
end
|
13
|
+
|
14
|
+
def modifier_keys
|
15
|
+
@modifier_keys ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def capture_modifier_keys(window)
|
19
|
+
self.class.modifier_key_list.each do |key|
|
20
|
+
modifier_keys[key] = window.button_down?(key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# TODO: This attempt to reduce duplication is brittle and will likely end in heartache.
|
26
|
+
#
|
27
|
+
|
28
|
+
def self.modifier_key_list_names
|
29
|
+
@modifier_key_list_names ||= %w[ KbLeftControl KbRightControl
|
30
|
+
KbLeftAlt KbRightAlt
|
31
|
+
KbLeftMeta KbRightMeta
|
32
|
+
KbLeftShift KbRightShift ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.modifier_key_list
|
36
|
+
@modifier_key_list ||= modifier_key_list_names.map {|key| "Gosu::#{key}".constantize }
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Generate methods for each left and right modifier key
|
41
|
+
#
|
42
|
+
|
43
|
+
modifier_key_list_names.each_with_index do |key_name,index|
|
44
|
+
define_method "#{key_name.gsub(/^Kb/,'').underscore}?" do
|
45
|
+
modifier_keys[self.class.modifier_key_list[index]]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Define generic modifier keys that is not concerned with whether it
|
51
|
+
# was the left key or the right key.
|
52
|
+
#
|
53
|
+
|
54
|
+
[ :control?, :alt?, :meta?, :shift? ].each do |generic|
|
55
|
+
define_method generic do
|
56
|
+
send("left_#{generic}") or send("right_#{generic}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'event_data'
|
2
|
+
|
1
3
|
module Metro
|
2
4
|
|
3
5
|
#
|
@@ -253,7 +255,7 @@ module Metro
|
|
253
255
|
# has been triggered.
|
254
256
|
#
|
255
257
|
def fire_button_up(id)
|
256
|
-
|
258
|
+
execute_block_for_target( &up_action(id) )
|
257
259
|
end
|
258
260
|
|
259
261
|
#
|
@@ -261,7 +263,7 @@ module Metro
|
|
261
263
|
# event has been triggered.
|
262
264
|
#
|
263
265
|
def fire_button_down(id)
|
264
|
-
|
266
|
+
execute_block_for_target( &down_action(id) )
|
265
267
|
end
|
266
268
|
|
267
269
|
#
|
@@ -271,9 +273,14 @@ module Metro
|
|
271
273
|
#
|
272
274
|
def fire_events_for_held_buttons
|
273
275
|
held_actions.each do |key,action|
|
274
|
-
|
276
|
+
execute_block_for_target(&action) if window and window.button_down?(key)
|
275
277
|
end
|
276
278
|
end
|
279
|
+
|
280
|
+
def execute_block_for_target(&block)
|
281
|
+
event_data = EventData.new(window)
|
282
|
+
target.instance_exec(event_data,&block)
|
283
|
+
end
|
277
284
|
|
278
285
|
# @return a block of code that is mapped for the 'button_up' id or a block that will attempt to call out
|
279
286
|
# to the action missing method.
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Metro
|
2
|
+
|
3
|
+
#
|
4
|
+
# The HitList is an object that maintains when an object is touched/clicked
|
5
|
+
# and then moved and finally released. The object attempts to work through
|
6
|
+
# the process:
|
7
|
+
#
|
8
|
+
# hit_list.hit(first_event)
|
9
|
+
# hit_list.update(next_event)
|
10
|
+
# hit_list.update(next_event)
|
11
|
+
# hit_list.release(last_event)
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# @see EditTransitionScene
|
15
|
+
#
|
16
|
+
class HitList
|
17
|
+
|
18
|
+
def initialize(drawers)
|
19
|
+
@drawers = drawers
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :drawers
|
23
|
+
|
24
|
+
def hit(event)
|
25
|
+
add drawers_at(event.mouse_x,event.mouse_y)
|
26
|
+
save_event event
|
27
|
+
end
|
28
|
+
|
29
|
+
def update(event)
|
30
|
+
offset_x, offset_y = offset_from_last_event(event)
|
31
|
+
|
32
|
+
list.each { |d| d.offset(offset_x,offset_y) }
|
33
|
+
|
34
|
+
save_event event
|
35
|
+
end
|
36
|
+
|
37
|
+
def release(event)
|
38
|
+
offset_x, offset_y = offset_from_last_event(event)
|
39
|
+
|
40
|
+
list.each { |d| d.offset(offset_x,offset_y) }
|
41
|
+
|
42
|
+
save_event event
|
43
|
+
clear
|
44
|
+
end
|
45
|
+
|
46
|
+
def drawers_at(x,y)
|
47
|
+
hit_drawers = drawers.find_all { |drawer| drawer.contains?(x,y) }
|
48
|
+
|
49
|
+
# assumed that we only want one item
|
50
|
+
top_drawer = hit_drawers.inject(hit_drawers.first) {|top,drawer| drawer.z_order > top.z_order ? drawer : top }
|
51
|
+
[ top_drawer ].compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def offset_from_last_event(event)
|
55
|
+
[ (event.mouse_x - @last_event.mouse_x).to_i, (event.mouse_y - @last_event.mouse_y).to_i ]
|
56
|
+
end
|
57
|
+
|
58
|
+
def save_event(event)
|
59
|
+
@first_event = event unless @first_event
|
60
|
+
@last_event = event
|
61
|
+
end
|
62
|
+
|
63
|
+
def list
|
64
|
+
@list ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
def add(hits)
|
68
|
+
Array(hits).each {|hit| list.push hit }
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear
|
72
|
+
list.clear
|
73
|
+
@first_event = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/metro/models/draws.rb
CHANGED
@@ -30,7 +30,12 @@ module Metro
|
|
30
30
|
# end
|
31
31
|
#
|
32
32
|
def draw(actor_name,options = {})
|
33
|
-
|
33
|
+
|
34
|
+
view_content_for_actor = view.content[actor_name.to_s]
|
35
|
+
actor_data = view_content_for_actor.merge(options)
|
36
|
+
actor_data[:name] = actor_name
|
37
|
+
|
38
|
+
scene_actor = ModelFactory.new actor_name, actor_data
|
34
39
|
|
35
40
|
define_method actor_name do
|
36
41
|
instance_variable_get("@#{actor_name}")
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Metro
|
2
|
+
module Models
|
3
|
+
|
4
|
+
class GridDrawer < Model
|
5
|
+
|
6
|
+
attr_writer :color, :spacing, :height, :width
|
7
|
+
|
8
|
+
def name
|
9
|
+
self.class.name
|
10
|
+
end
|
11
|
+
|
12
|
+
def spacing
|
13
|
+
@spacing ||= 10
|
14
|
+
end
|
15
|
+
|
16
|
+
def height
|
17
|
+
@height || Game.height
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
@width || Game.width
|
22
|
+
end
|
23
|
+
|
24
|
+
def color
|
25
|
+
@color ||= Gosu::Color.new("rgba(255,255,255,0.1)")
|
26
|
+
end
|
27
|
+
|
28
|
+
def saveable?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def contains?(x,y)
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def draw
|
37
|
+
draw_horizontal_lines
|
38
|
+
draw_vertical_lines
|
39
|
+
end
|
40
|
+
|
41
|
+
def draw_vertical_lines
|
42
|
+
xs = (width / spacing + 1).times.map {|segment| segment * spacing }
|
43
|
+
xs.each do |x|
|
44
|
+
window.draw_line(x, 1, color, x, height, color, 0, :additive)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def draw_horizontal_lines
|
49
|
+
ys = (height / spacing + 1).times.map {|segment| segment * spacing }
|
50
|
+
ys.each do |y|
|
51
|
+
window.draw_line(1, y, color, width, y, color, 0, :additive)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/metro/models/image.rb
CHANGED
@@ -3,10 +3,10 @@ module Metro
|
|
3
3
|
|
4
4
|
#
|
5
5
|
# Draws an Image
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# @example Using the Image in a view file
|
8
8
|
# model: "metro::models::image"
|
9
|
-
#
|
9
|
+
#
|
10
10
|
class Image < Model
|
11
11
|
|
12
12
|
attr_accessor :x, :y, :angle, :center_x, :center_y, :x_factor, :y_factor, :z_order
|
@@ -25,6 +25,22 @@ module Metro
|
|
25
25
|
@image ||= Gosu::Image.new(window,asset_path(path))
|
26
26
|
end
|
27
27
|
|
28
|
+
def contains?(x,y)
|
29
|
+
bounds.contains?(x,y)
|
30
|
+
end
|
31
|
+
|
32
|
+
def bounds
|
33
|
+
Bounds.new x - (width * center_x), y - (height * center_y), x + (width * center_x), y + (height * center_y)
|
34
|
+
end
|
35
|
+
|
36
|
+
def width
|
37
|
+
image.width
|
38
|
+
end
|
39
|
+
|
40
|
+
def height
|
41
|
+
image.height
|
42
|
+
end
|
43
|
+
|
28
44
|
def draw
|
29
45
|
image.draw_rot x, y, z_order,
|
30
46
|
angle.to_f,
|