metro-ld25 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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'