metro-ld25 0.3.3

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.
Files changed (177) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +12 -0
  6. data/Guardfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +189 -0
  9. data/Rakefile +18 -0
  10. data/bin/metro +16 -0
  11. data/changelog.md +157 -0
  12. data/lib/assets/menu-movement.wav +0 -0
  13. data/lib/assets/menu-selection.wav +0 -0
  14. data/lib/assets/missing.ogg +0 -0
  15. data/lib/assets/missing.png +0 -0
  16. data/lib/assets/missing.wav +0 -0
  17. data/lib/assets/missing_animation.png +0 -0
  18. data/lib/commands/generate_game.rb +13 -0
  19. data/lib/commands/generate_model.rb +25 -0
  20. data/lib/commands/generate_scene.rb +36 -0
  21. data/lib/commands/generate_view.rb +21 -0
  22. data/lib/commands/thor.rb +83 -0
  23. data/lib/core_ext/class.rb +14 -0
  24. data/lib/core_ext/numeric.rb +59 -0
  25. data/lib/gosu_ext/color.rb +62 -0
  26. data/lib/gosu_ext/gosu_constants.rb +53 -0
  27. data/lib/locale/en.yml +35 -0
  28. data/lib/locale/locale.rb +1 -0
  29. data/lib/metro.rb +140 -0
  30. data/lib/metro/animation.rb +135 -0
  31. data/lib/metro/animation/after_interval_factory.rb +12 -0
  32. data/lib/metro/animation/animation_factory.rb +15 -0
  33. data/lib/metro/animation/easing/ease_in.rb +15 -0
  34. data/lib/metro/animation/easing/easing.rb +51 -0
  35. data/lib/metro/animation/easing/linear.rb +15 -0
  36. data/lib/metro/animation/has_animations.rb +70 -0
  37. data/lib/metro/animation/implicit_animation.rb +100 -0
  38. data/lib/metro/animation/on_update_operation.rb +96 -0
  39. data/lib/metro/animation/scene_animation.rb +16 -0
  40. data/lib/metro/asset_path.rb +97 -0
  41. data/lib/metro/events/control_definition.rb +11 -0
  42. data/lib/metro/events/controls.rb +42 -0
  43. data/lib/metro/events/event_data.rb +60 -0
  44. data/lib/metro/events/event_dictionary.rb +52 -0
  45. data/lib/metro/events/event_factory.rb +17 -0
  46. data/lib/metro/events/event_relay.rb +300 -0
  47. data/lib/metro/events/event_state_manager.rb +63 -0
  48. data/lib/metro/events/events.rb +3 -0
  49. data/lib/metro/events/has_events.rb +108 -0
  50. data/lib/metro/events/hit_list.rb +75 -0
  51. data/lib/metro/events/unknown_sender.rb +5 -0
  52. data/lib/metro/font.rb +69 -0
  53. data/lib/metro/game.rb +102 -0
  54. data/lib/metro/game/dsl.rb +68 -0
  55. data/lib/metro/image.rb +68 -0
  56. data/lib/metro/logging.rb +33 -0
  57. data/lib/metro/missing_scene.rb +21 -0
  58. data/lib/metro/models/audio/song.rb +33 -0
  59. data/lib/metro/models/draws.rb +86 -0
  60. data/lib/metro/models/key_value_coding.rb +38 -0
  61. data/lib/metro/models/model.rb +236 -0
  62. data/lib/metro/models/model_factory.rb +32 -0
  63. data/lib/metro/models/models.rb +62 -0
  64. data/lib/metro/models/properties/animation_property.rb +115 -0
  65. data/lib/metro/models/properties/array_property.rb +24 -0
  66. data/lib/metro/models/properties/boolean_property.rb +27 -0
  67. data/lib/metro/models/properties/color_property.rb +116 -0
  68. data/lib/metro/models/properties/dimensions_property.rb +84 -0
  69. data/lib/metro/models/properties/font_property.rb +130 -0
  70. data/lib/metro/models/properties/image_property.rb +96 -0
  71. data/lib/metro/models/properties/model_property.rb +84 -0
  72. data/lib/metro/models/properties/numeric_property.rb +29 -0
  73. data/lib/metro/models/properties/options_property/no_option.rb +29 -0
  74. data/lib/metro/models/properties/options_property/options.rb +94 -0
  75. data/lib/metro/models/properties/options_property/options_property.rb +125 -0
  76. data/lib/metro/models/properties/position_property.rb +90 -0
  77. data/lib/metro/models/properties/property.rb +221 -0
  78. data/lib/metro/models/properties/property_owner.rb +137 -0
  79. data/lib/metro/models/properties/sample_property.rb +84 -0
  80. data/lib/metro/models/properties/scale_property.rb +80 -0
  81. data/lib/metro/models/properties/song_property.rb +89 -0
  82. data/lib/metro/models/properties/text_property.rb +75 -0
  83. data/lib/metro/models/ui/animated_sprite.rb +85 -0
  84. data/lib/metro/models/ui/border.rb +95 -0
  85. data/lib/metro/models/ui/fps.rb +54 -0
  86. data/lib/metro/models/ui/generic.rb +66 -0
  87. data/lib/metro/models/ui/grid_drawer.rb +74 -0
  88. data/lib/metro/models/ui/image.rb +87 -0
  89. data/lib/metro/models/ui/label.rb +175 -0
  90. data/lib/metro/models/ui/menu.rb +214 -0
  91. data/lib/metro/models/ui/model_label.rb +65 -0
  92. data/lib/metro/models/ui/model_labeler.rb +79 -0
  93. data/lib/metro/models/ui/rectangle.rb +59 -0
  94. data/lib/metro/models/ui/sprite.rb +79 -0
  95. data/lib/metro/models/ui/tile_map.rb +162 -0
  96. data/lib/metro/models/ui/ui.rb +13 -0
  97. data/lib/metro/parameters/command_line_args_parser.rb +68 -0
  98. data/lib/metro/parameters/options.rb +25 -0
  99. data/lib/metro/parameters/parameters.rb +2 -0
  100. data/lib/metro/sample.rb +40 -0
  101. data/lib/metro/scene.rb +477 -0
  102. data/lib/metro/scenes.rb +154 -0
  103. data/lib/metro/song.rb +56 -0
  104. data/lib/metro/template_message.rb +60 -0
  105. data/lib/metro/transitions/edit_transition_scene.rb +100 -0
  106. data/lib/metro/transitions/fade_transition_scene.rb +66 -0
  107. data/lib/metro/transitions/scene_transitions.rb +44 -0
  108. data/lib/metro/transitions/transition_scene.rb +19 -0
  109. data/lib/metro/units/bounds.rb +8 -0
  110. data/lib/metro/units/calculation_validations.rb +74 -0
  111. data/lib/metro/units/dimensions.rb +60 -0
  112. data/lib/metro/units/point.rb +51 -0
  113. data/lib/metro/units/rectangle_bounds.rb +85 -0
  114. data/lib/metro/units/scale.rb +46 -0
  115. data/lib/metro/units/units.rb +6 -0
  116. data/lib/metro/version.rb +32 -0
  117. data/lib/metro/views/json_view.rb +60 -0
  118. data/lib/metro/views/no_view.rb +34 -0
  119. data/lib/metro/views/parsers.rb +42 -0
  120. data/lib/metro/views/scene_view.rb +107 -0
  121. data/lib/metro/views/view.rb +133 -0
  122. data/lib/metro/views/writers.rb +43 -0
  123. data/lib/metro/views/yaml_view.rb +94 -0
  124. data/lib/metro/window.rb +94 -0
  125. data/lib/setup_handlers/exit_if_dry_run.rb +26 -0
  126. data/lib/setup_handlers/game_execution.rb +65 -0
  127. data/lib/setup_handlers/load_game_configuration.rb +65 -0
  128. data/lib/setup_handlers/load_game_files.rb +101 -0
  129. data/lib/setup_handlers/move_to_game_directory.rb +25 -0
  130. data/lib/setup_handlers/reload_game_on_game_file_changes.rb +79 -0
  131. data/lib/templates/game/README.md.tt +52 -0
  132. data/lib/templates/game/assets/brand.jpg +0 -0
  133. data/lib/templates/game/assets/hero.png +0 -0
  134. data/lib/templates/game/lib/custom_easing.rb +32 -0
  135. data/lib/templates/game/metro.tt +63 -0
  136. data/lib/templates/game/models/hero.rb +62 -0
  137. data/lib/templates/game/scenes/brand_scene.rb +19 -0
  138. data/lib/templates/game/scenes/brand_to_title_scene.rb +13 -0
  139. data/lib/templates/game/scenes/first_scene.rb +28 -0
  140. data/lib/templates/game/scenes/game_scene.rb +43 -0
  141. data/lib/templates/game/scenes/title_scene.rb +15 -0
  142. data/lib/templates/game/views/brand.yaml +4 -0
  143. data/lib/templates/game/views/brand_to_title.yaml +8 -0
  144. data/lib/templates/game/views/first.yaml +26 -0
  145. data/lib/templates/game/views/title.yaml +11 -0
  146. data/lib/templates/message.erb +23 -0
  147. data/lib/templates/model.rb.tt +111 -0
  148. data/lib/templates/scene.rb.tt +140 -0
  149. data/lib/templates/view.yaml.tt +11 -0
  150. data/lib/tmxed_ext/tile_set.rb +34 -0
  151. data/metro.gemspec +56 -0
  152. data/spec/core_ext/numeric_spec.rb +78 -0
  153. data/spec/core_ext/string_spec.rb +33 -0
  154. data/spec/gosu_ext/color_spec.rb +80 -0
  155. data/spec/metro/events/event_state_manager_spec.rb +5 -0
  156. data/spec/metro/models/key_value_coding_spec.rb +61 -0
  157. data/spec/metro/models/properties/array_property_spec.rb +60 -0
  158. data/spec/metro/models/properties/color_property_spec.rb +85 -0
  159. data/spec/metro/models/properties/dimensions_spec.rb +29 -0
  160. data/spec/metro/models/properties/font_property_spec.rb +127 -0
  161. data/spec/metro/models/properties/numeric_property_spec.rb +46 -0
  162. data/spec/metro/models/properties/options_property/no_option_spec.rb +25 -0
  163. data/spec/metro/models/properties/options_property/options_property_spec.rb +133 -0
  164. data/spec/metro/models/properties/options_property/options_spec.rb +125 -0
  165. data/spec/metro/models/properties/position_property_spec.rb +90 -0
  166. data/spec/metro/models/ui/label_spec.rb +259 -0
  167. data/spec/metro/parameters/command_line_args_parser_spec.rb +42 -0
  168. data/spec/metro/scene_spec.rb +15 -0
  169. data/spec/metro/scene_views/json_view_spec.rb +27 -0
  170. data/spec/metro/scene_views/yaml_view_spec.rb +38 -0
  171. data/spec/metro/scenes_spec.rb +77 -0
  172. data/spec/metro/units/point_spec.rb +132 -0
  173. data/spec/metro/views/view_spec.rb +53 -0
  174. data/spec/setup_handlers/exit_if_dry_run_spec.rb +27 -0
  175. data/spec/setup_handlers/reload_game_on_game_file_changes_spec.rb +68 -0
  176. data/spec/spec_helper.rb +20 -0
  177. metadata +374 -0
Binary file
Binary file
Binary file
@@ -0,0 +1,13 @@
1
+ module Metro
2
+
3
+ class GenerateGame < Generator
4
+
5
+ argument :name
6
+
7
+ def create_metro_file
8
+ directory "game", name
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,25 @@
1
+ module Metro
2
+
3
+ class GenerateModel < Generator
4
+
5
+ no_tasks do
6
+
7
+ def model_filename
8
+ name.underscore
9
+ end
10
+
11
+ def model_name
12
+ name.camelize
13
+ end
14
+
15
+ end
16
+
17
+ argument :name
18
+
19
+ def create_model_file
20
+ template "model.rb.tt", "models/#{model_filename}.rb"
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,36 @@
1
+ module Metro
2
+
3
+ class GenerateScene < Generator
4
+
5
+ no_tasks do
6
+
7
+ def scene_filename
8
+ scene_name = name.gsub(/_?Scene$/i,'')
9
+ "#{scene_name.underscore}_scene"
10
+ end
11
+
12
+ def scene_class_name
13
+ scene_name = name.gsub(/_?Scene$/i,'')
14
+ "#{scene_name.camelize}Scene"
15
+ end
16
+
17
+ def view_filename
18
+ view_name = name.to_s.gsub(/_?Scene$/i,'')
19
+ view_name.underscore
20
+ end
21
+
22
+ end
23
+
24
+ argument :name
25
+
26
+ def create_scene_file
27
+ template "scene.rb.tt", "scenes/#{scene_filename}.rb"
28
+ end
29
+
30
+ def create_view_file
31
+ template "view.yaml.tt", "views/#{view_filename}.yaml"
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,21 @@
1
+ module Metro
2
+
3
+ class GenerateView < Generator
4
+
5
+ no_tasks do
6
+
7
+ def view_filename
8
+ view_name = name.to_s.gsub(/_?Scene$/i,'')
9
+ view_name.underscore
10
+ end
11
+
12
+ end
13
+
14
+ argument :name
15
+
16
+ def create_view_file
17
+ template "view.yaml.tt", "views/#{view_filename}.yaml"
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,83 @@
1
+ require 'thor'
2
+ require 'thor/group'
3
+
4
+ module Metro
5
+
6
+ class Generator < ::Thor::Group
7
+ include Thor::Actions
8
+
9
+ def self.source_root
10
+ File.join File.dirname(__FILE__), "..", "templates"
11
+ end
12
+ end
13
+
14
+ class UnknownGenerator
15
+
16
+ def self.start(commands)
17
+ raise "There is no command: [ #{commands.join(', ')} ]"
18
+ end
19
+
20
+ end
21
+
22
+
23
+ class Thor < Thor
24
+
25
+ no_tasks do
26
+
27
+ def banner
28
+ """
29
+ ********************************************************************************
30
+ ______ ___ _____
31
+ ___ |/ /_____ __ /_______________
32
+ __ /|_/ / _ _ \\_ __/__ ___/_ __ \\
33
+ _ / / / / __// /_ _ / / /_/ /
34
+ /_/ /_/ \\___/ \\__/ /_/ \\____/
35
+
36
+ -------------------------------------------------------------------------------"""
37
+ end
38
+
39
+ def generators
40
+ Hash.new(UnknownGenerator).merge model: Metro::GenerateModel,
41
+ scene: Metro::GenerateScene,
42
+ view: Metro::GenerateView
43
+ end
44
+
45
+ def generator(name)
46
+ generators[name.to_sym]
47
+ end
48
+
49
+ end
50
+
51
+ desc "new GAMENAME",
52
+ "Create a new game within the directory using with the GAMENAME given."
53
+ def new(game_name)
54
+ Metro::GenerateGame.start [ game_name ]
55
+ end
56
+
57
+ desc "generate TYPE NAME",
58
+ "Create a new type: model, view, or scene."
59
+ def generate(type,name)
60
+ gen = generator(type)
61
+ gen.start [ name ]
62
+ end
63
+
64
+ desc "g TYPE NAME",
65
+ "Create a new type: model, view, or scene."
66
+ def g(type,name)
67
+ generate(type,name)
68
+ end
69
+
70
+ desc "help", "This commoand"
71
+ def help
72
+ say banner
73
+ print_table self.class.printable_tasks, indent: 4
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+
80
+ require_relative 'generate_game'
81
+ require_relative 'generate_model'
82
+ require_relative 'generate_scene'
83
+ require_relative 'generate_view'
@@ -0,0 +1,14 @@
1
+
2
+ class Class
3
+
4
+ #
5
+ # Within Metro often times a Class or the name of the class is being used.
6
+ # ActiveSupport provides the constantize on Strings and Symbols but does
7
+ # not provide it on Class. So instead of providing redundant checks in
8
+ # places this monkeypatch simply makes Classes adhere to the same interface.
9
+ #
10
+ # @return [Class] itself.
11
+ def constantize
12
+ self
13
+ end
14
+ end
@@ -0,0 +1,59 @@
1
+ class Numeric
2
+
3
+ #
4
+ # Set the tick interval which is used in conversion of seconds to
5
+ # ticks.
6
+ #
7
+ def self.tick_interval=(value)
8
+ @tick_interval = value.to_f
9
+ end
10
+
11
+ #
12
+ # @return the game tick interval. By default the tick interval is 16.66666
13
+ #
14
+ def self.tick_interval
15
+ @tick_interval.to_f == 0 ? 16.666666 : @tick_interval.to_f
16
+ end
17
+
18
+
19
+
20
+ #
21
+ # Provides the suffix 'second' which will translate seconds to
22
+ # game ticks. This is to allow for a number of seconds to be expressed
23
+ # in situations where game ticks are used (e.g. animations).
24
+ #
25
+ # @example Expressing an animation that takes place over 3 seconds (~180 ticks)
26
+ #
27
+ # class ExampleScene < Metro::Scene
28
+ # draws :title
29
+ #
30
+ # animate :title, to: { x: 320, y: 444 }, interval: 3.seconds
31
+ # end
32
+ #
33
+ def second
34
+ (self * 1000 / Numeric.tick_interval).floor
35
+ end
36
+
37
+ alias_method :seconds, :second
38
+ alias_method :secs, :second
39
+ alias_method :sec, :second
40
+
41
+ #
42
+ # Provides the suffix 'tick' which will simply express the number
43
+ # with a vanity suffix.
44
+ #
45
+ # @example Expressing an animation that takes place over 60 game ticks
46
+ #
47
+ # class ExampleScene < Metro::Scene
48
+ # draws :title
49
+ #
50
+ # animate :title, to: { x: 320, y: 444 }, interval: 60.ticks
51
+ # end
52
+ #
53
+ def tick
54
+ self
55
+ end
56
+
57
+ alias_method :ticks, :tick
58
+
59
+ end
@@ -0,0 +1,62 @@
1
+ class Gosu::Color
2
+
3
+ alias_method :gosu_initialize, :initialize
4
+
5
+ #
6
+ # Monkey-patching the initialize to allow for another Gosu::Color
7
+ # and Strings.
8
+ #
9
+ def initialize(*args)
10
+ if args.length == 1
11
+ value = args.first
12
+ if value.is_a? Gosu::Color
13
+ gosu_initialize value.alpha, value.red, value.green, value.blue
14
+ elsif value.is_a? String
15
+ gosu_initialize *Array(self.class.parse_string(value))
16
+ else
17
+ gosu_initialize value
18
+ end
19
+ else
20
+ gosu_initialize *args
21
+ end
22
+ end
23
+
24
+ def self.parse_string(value)
25
+ parse_hex(value) || parse_rgb(value) || parse_rgba(value) || parse_gosu_color_string(value) || [ 255, 255, 255, 255 ]
26
+ end
27
+
28
+ def self.parse_rgba(rgba)
29
+ if rgba =~ /rgba\(([\d]{1,3}(?:\.\d*)?),([\d]{1,3}(?:\.\d*)?),([\d]{1,3}(?:\.\d*)?),(\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
+
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
+
62
+ end
@@ -0,0 +1,53 @@
1
+ module Metro
2
+
3
+ #
4
+ # The GosuEvents module creates aliases for the Keyboard and the Gamepad events
5
+ # within the Gosu Namespace. This is so Metro can use the events without requiring
6
+ # the namespace.
7
+ #
8
+ # This makes the interface of these events more portable.
9
+ #
10
+ module GosuConstants
11
+
12
+ def self.extended(base)
13
+ add constants: keyboard_events, to: base
14
+ add constants: gamepad_events, to: base
15
+ add constants: mouse_events, to: base
16
+ end
17
+
18
+ #
19
+ # @return the constant from which to search for all the other constants.
20
+ # This helper method is to to save sprinkling the constant value
21
+ # throughout the rest of the module.
22
+ def self.gosu
23
+ Gosu
24
+ end
25
+
26
+ def self.keyboard_events
27
+ find_all_constants_with_prefix "Kb"
28
+ end
29
+
30
+ def self.gamepad_events
31
+ find_all_constants_with_prefix "Gp"
32
+ end
33
+
34
+ def self.mouse_events
35
+ find_all_constants_with_prefix "Ms"
36
+ end
37
+
38
+ def self.find_all_constants_with_prefix(prefix)
39
+ gosu.constants.find_all { |c| c.to_s.start_with? prefix }
40
+ end
41
+
42
+ #
43
+ # Alias the list of given constants within the given class.
44
+ #
45
+ def self.add(options={})
46
+ events = options[:constants]
47
+ target = options[:to]
48
+
49
+ events.each {|event| target.const_set event, gosu.const_get(event) }
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ ---
2
+ en:
3
+ website: "%{website}"
4
+ error:
5
+ unloadable_source:
6
+ title: "Metro CANNOT load your game code!"
7
+ message: "There is an error within your game code that needs to be fixed before Metro\nis able to replace the current running game code:\n\n%{output}"
8
+ actions: []
9
+ missing_metro_file:
10
+ title: "Unable to find Metro game file"
11
+ message: "The specified file `%{file}` which is required to run the game could not be found."
12
+ actions:
13
+ - "Ensure you have specified the correct file"
14
+ - "Ensure that the file exists at that location"
15
+ - "Did you mean to generate a new game, scene, or model? Try: `metro help`"
16
+ specified_directory:
17
+ title: "Path specified is a directory"
18
+ message: "The specified path is a `%{directory}` and not a valid Metro game file."
19
+ actions:
20
+ - "Ensure you have specified the correct file"
21
+ - "Specify the path to the metro game file with the directory included."
22
+ - "Did you mean to generate a new game, scene, or model? Try: `metro help`"
23
+ reserved_control_name:
24
+ title: "Unable to define control: %{name}"
25
+ message: "The specified control name `%{name}` is RESERVED or ALREADY DEFINED."
26
+ actions:
27
+ - "Ensure that the control name is not already defined."
28
+ - "Replace the use of this control name with name"
29
+ dry_run:
30
+ success:
31
+ title: "Metro Game Dependencies Have Been Met!"
32
+ message: "Your game should be able to be run."
33
+ actions:
34
+ - "Metro dependencies have successfully been loaded"
35
+ - "Game dependencies have successfully been loaded"
@@ -0,0 +1 @@
1
+ I18n.load_path.push "#{File.dirname(__FILE__)}/en.yml"
@@ -0,0 +1,140 @@
1
+ require 'delegate'
2
+ require 'logger'
3
+ require 'erb'
4
+ require 'open3'
5
+
6
+ require 'gosu'
7
+ require 'i18n'
8
+ require 'listen'
9
+ require 'tmxed'
10
+ require 'active_support'
11
+ require 'active_support/dependencies'
12
+ require 'active_support/inflector'
13
+ require 'active_support/core_ext/hash'
14
+ require 'active_support/hash_with_indifferent_access'
15
+
16
+ require 'gosu_ext/color'
17
+ require 'gosu_ext/gosu_constants'
18
+ require 'core_ext/numeric'
19
+ require 'core_ext/class'
20
+ require 'tmxed_ext/tile_set'
21
+
22
+ require 'locale/locale'
23
+
24
+ require 'metro/parameters/parameters'
25
+ require 'metro/asset_path'
26
+ require 'metro/units/units'
27
+ require 'metro/logging'
28
+ require 'metro/version'
29
+ require 'metro/animation'
30
+ require 'metro/font'
31
+ require 'metro/image'
32
+ require 'metro/sample'
33
+ require 'metro/song'
34
+ require 'metro/template_message'
35
+ require 'metro/window'
36
+ require 'metro/game'
37
+ require 'metro/scene'
38
+ require 'metro/scenes'
39
+ require 'metro/models/model'
40
+ require 'metro/missing_scene'
41
+
42
+ #
43
+ # To allow an author an easier time accessing the Game object from within their game.
44
+ # They do not have to use the `Metro::Game` an instead use the `Game` constant.
45
+ #
46
+ Game = Metro::Game
47
+
48
+ module Metro
49
+ extend self
50
+ extend GosuConstants
51
+
52
+ #
53
+ # @return [String] the filepath to the Metro executable
54
+ #
55
+ def executable_path
56
+ File.absolute_path File.join(File.dirname(__FILE__), "..", "bin", "metro")
57
+ end
58
+
59
+ #
60
+ # @return [String] the filepath to the Metro assets
61
+ #
62
+ def asset_dir
63
+ File.join File.dirname(__FILE__), "assets"
64
+ end
65
+
66
+ #
67
+ # @return [Array] an array of all the handlers that will be executed prior
68
+ # to the game being launched.
69
+ #
70
+ def setup_handlers
71
+ @setup_handlers ||= []
72
+ end
73
+
74
+ #
75
+ # Register a setup handler. While this method is present, it is far
76
+ # too late for game code to be executed as these pregame handlers will already
77
+ # have started executing. This allows for modularity within the Metro library
78
+ # with the possibility that this functionality could become available to
79
+ # individual games if the load process were to be updated.
80
+ #
81
+ def register_setup_handler(handler)
82
+ setup_handlers.push handler
83
+ end
84
+
85
+ #
86
+ # Run will load the contents of the game contents and game files
87
+ # within the current working directory and start the game.
88
+ #
89
+ # @param [Array<String>] parameters an array of parameters that contains
90
+ # the commands in the format that would normally be parsed into the
91
+ # ARGV array.
92
+ #
93
+ def run(*parameters)
94
+ options = Parameters::CommandLineArgsParser.parse(parameters)
95
+ setup_handlers.each { |handler| handler.setup(options) }
96
+ start_game
97
+ end
98
+
99
+ #
100
+ # Start the game by lanunching a window with the game configuration and data
101
+ # that has been loaded.
102
+ #
103
+ def start_game
104
+ Game.start!
105
+ end
106
+
107
+ #
108
+ # When called all the game-related code will be unloaded and reloaded.
109
+ # Providding an opportunity for a game author to tweak the code without having
110
+ # to restart the game.
111
+ #
112
+ def reload!
113
+ SetupHandlers::LoadGameFiles.new.load_game_files!
114
+ end
115
+
116
+ #
117
+ # When called the game-related code will be loaded in a sub-process to see
118
+ # if the code is valid. This is used in tandem with {#reload} and should be
119
+ # called prior to ensure that the code that is replacing the current code
120
+ # is valid.
121
+ #
122
+ # @return [TrueClass,FalseClass] true if the game code that was loaded was
123
+ # loaded successfully. false if the game code was not able to be loaded.
124
+ #
125
+ def game_has_valid_code?
126
+ execution = SetupHandlers::LoadGameFiles.new.launch_game_in_dry_run_mode
127
+
128
+ if execution.invalid?
129
+ error! 'error.unloadable_source', output: execution.output, exit: false
130
+ end
131
+
132
+ execution.valid?
133
+ end
134
+ end
135
+
136
+ require 'setup_handlers/move_to_game_directory'
137
+ require 'setup_handlers/load_game_files'
138
+ require 'setup_handlers/load_game_configuration'
139
+ require 'setup_handlers/exit_if_dry_run'
140
+ require 'setup_handlers/reload_game_on_game_file_changes'