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
@@ -0,0 +1,63 @@
1
+ require_relative 'event_relay'
2
+
3
+ module Metro
4
+
5
+ class EventStateManager
6
+ def initialize
7
+ @current_state = []
8
+ end
9
+
10
+ attr_accessor :window
11
+
12
+ attr_reader :current_state
13
+
14
+ #
15
+ # Clear all the event relays of the current game state
16
+ #
17
+ def clear
18
+ current_state.clear
19
+ end
20
+
21
+ #
22
+ # Fire events for held buttons within the current game state
23
+ #
24
+ def fire_events_for_held_buttons
25
+ current_state.each {|cs| cs.fire_events_for_held_buttons }
26
+ end
27
+
28
+ #
29
+ # Fire events for button up for the current game state
30
+ #
31
+ def fire_button_up(id)
32
+ current_state.each {|cs| cs.fire_button_up(id) }
33
+ end
34
+
35
+ #
36
+ # Fire events for button down within the current game state
37
+ #
38
+ def fire_button_down(id)
39
+ current_state.each {|cs| cs.fire_button_down(id) }
40
+ end
41
+
42
+ #
43
+ # Fire notification events within the current game state
44
+ #
45
+ def fire_events_for_notification(event,sender)
46
+ current_state.each {|cs| cs.fire_events_for_notification(event,sender) }
47
+ end
48
+
49
+ #
50
+ # An an event relay to the current game state
51
+ #
52
+ def add_events_for_target(target,events)
53
+ relay = EventRelay.new(target,window)
54
+
55
+ events.each do |target_event|
56
+ relay.send target_event.event, *target_event.buttons, &target_event.block
57
+ end
58
+
59
+ current_state.push relay
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'has_events'
2
+ require_relative 'event_state_manager'
3
+ require_relative 'unknown_sender'
@@ -0,0 +1,108 @@
1
+ require_relative 'event_dictionary'
2
+
3
+ module Metro
4
+
5
+ module HasEvents
6
+
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ base.extend GosuConstants
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ #
15
+ # Register an event for the scene.
16
+ #
17
+ # @example Registering for a save complete event that would re-enable a menu.
18
+ #
19
+ # class ExampleScene
20
+ # event :notification, :save_complete do
21
+ # menu.enabled!
22
+ # end
23
+ # end
24
+ #
25
+ # @example Registering for button held events
26
+ #
27
+ # class ExampleScene
28
+ # event :on_hold KbLeft, GpLeft do
29
+ # player.turn_left
30
+ # end
31
+ #
32
+ # event :on_hold, KbRight, GpRight do
33
+ # player.turn_right
34
+ # end
35
+ #
36
+ # event :on_hold, KbUp, GpButton0, do: :calculate_accleration
37
+ #
38
+ # def calculate_acceleration
39
+ # long_complicated_calculated_result = 0
40
+ # # ... multi-line calculations to determine the player acceleration ...
41
+ # player.accelerate = long_complicated_calculated_result
42
+ # end
43
+ # end
44
+ #
45
+ # @example Registering for a button down event to call a method named 'next_option'
46
+ #
47
+ # class ExampleScene
48
+ # event :on_up, KbEscape, do: :leave_scene
49
+ #
50
+ # def leave_scene
51
+ # transition_to :title
52
+ # end
53
+ # end
54
+ #
55
+ # Here in this scene if the Escape Key is pressed and released the example scene
56
+ # will transition to the title scene.
57
+ #
58
+ # @example Registering for a button up event with a block of code to execute
59
+ #
60
+ # class ExampleScene
61
+ # event :on_up, KbEscape do
62
+ # transition_to :title
63
+ # end
64
+ # end
65
+ #
66
+ # @example Registering for a button down event to call a method named 'previous_option'
67
+ #
68
+ # class ExampleScene
69
+ # event :on_down, GpLeft, GpUp, do: :previous_option
70
+ #
71
+ # def previous_option
72
+ # @selected_index = @selected_index - 1
73
+ # @selected_index = options.length - 1 if @selected_index <= -1
74
+ # end
75
+ # end
76
+ #
77
+ # Here in this scene if the GpLeft or GpUp buttons are pressed down the method
78
+ # `previous_options` will be executed.
79
+ #
80
+ #
81
+ # @example Registering for a button down event with a block of code to execute
82
+ #
83
+ # class ExampleScene
84
+ # event :on_down, GpLeft, GpUp do
85
+ # @selected_index = @selected_index - 1
86
+ # @selected_index = options.length - 1 if @selected_index <= -1
87
+ # end
88
+ # end
89
+ #
90
+ # This example uses a block instead of a method name but it is absolultey the same
91
+ # as the last example.
92
+ #
93
+ def event(event_type,*args,&block)
94
+ EventDictionary.add target: metro_name, type: event_type, args: args, block: block
95
+ end
96
+
97
+ #
98
+ # @return a list of all the EventFactories defined for this event holding object
99
+ #
100
+ def events
101
+ EventDictionary.events_for_targets(hierarchy)
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end
@@ -0,0 +1,75 @@
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
+ # @see EditTransitionScene
14
+ #
15
+ class HitList
16
+
17
+ def initialize(drawers)
18
+ @drawers = drawers
19
+ end
20
+
21
+ attr_reader :drawers
22
+
23
+ def hit(event)
24
+ add drawers_at(event.mouse_point)
25
+ save_event event
26
+ end
27
+
28
+ def update(event)
29
+ offset = offset_from_last_event(event)
30
+ list.each { |d| d.position = d.position + offset }
31
+
32
+ save_event event
33
+ end
34
+
35
+ def release(event)
36
+ offset = offset_from_last_event(event)
37
+ list.each { |d| d.position = d.position + offset }
38
+
39
+ save_event event
40
+ clear
41
+ end
42
+
43
+ def drawers_at(point)
44
+ hit_drawers = drawers.find_all { |drawer| drawer.bounds.contains?(point) }
45
+
46
+ # assumed that we only want one item
47
+ top_drawer = hit_drawers.inject(hit_drawers.first) {|top,drawer| drawer.z_order > top.z_order ? drawer : top }
48
+ [ top_drawer ].compact
49
+ end
50
+
51
+ def offset_from_last_event(event)
52
+ return Point.zero unless @last_event
53
+ event.mouse_point - @last_event.mouse_point
54
+ end
55
+
56
+ def save_event(event)
57
+ @first_event = event unless @first_event
58
+ @last_event = event
59
+ end
60
+
61
+ def list
62
+ @list ||= []
63
+ end
64
+
65
+ def add(hits)
66
+ Array(hits).each {|hit| list.push hit }
67
+ end
68
+
69
+ def clear
70
+ list.clear
71
+ @first_event = nil
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ module Metro
2
+ class UnknownSender
3
+
4
+ end
5
+ end
@@ -0,0 +1,69 @@
1
+ module Metro
2
+
3
+ #
4
+ # Font is a wrapper class for a Gosu::Font. This allows for additional data
5
+ # to be stored without relying on monkey-patching on functionality.
6
+ #
7
+ class Font < SimpleDelegator
8
+
9
+ def initialize(gosu_font)
10
+ super(gosu_font)
11
+ end
12
+
13
+ # An alias to Gosu::Font's height method
14
+ def size ; height ; end
15
+
16
+ #
17
+ # Return a font that matches the specified criteria. Using the name, size,
18
+ # and window a font will be generated or retrieved from the cache.
19
+ #
20
+ # @example Finding or creating a Font
21
+ #
22
+ # Metro::Font.find_or_create window: model.window,
23
+ # name: "Times New Roman", size: 24
24
+ #
25
+ # @param [Hash] value the hash that contains the `name`, `size` and `window`
26
+ # that describe the font.
27
+ #
28
+ def self.find_or_create(options)
29
+ window, name, size = create_params(options)
30
+ gosu_font = fonts["#{name}:#{size}:#{window}"]
31
+ gosu_font ? new(gosu_font) : create(options)
32
+ end
33
+
34
+ #
35
+ # Return a font that matches the specified criteria. Using the name, size,
36
+ # and window a font will be generated and stored in the cache.
37
+ #
38
+ # @example Creating a Font
39
+ #
40
+ # Metro::Font.create window: model.window,
41
+ # name: "Comic Sans", size: 48
42
+ #
43
+ # @param [Hash] value the hash that contains the `name`, `size` and `window`
44
+ # that describe the font.
45
+ #
46
+ def self.create(options)
47
+ window, name, size = create_params(options)
48
+ gosu_font = create_gosu_font(window,name,size)
49
+ fonts["#{name}:#{size}:#{window}"] = gosu_font
50
+ new(gosu_font)
51
+ end
52
+
53
+ private
54
+
55
+ def self.create_params(options)
56
+ options.symbolize_keys!
57
+ [ options[:window], options[:name], options[:size] ]
58
+ end
59
+
60
+ def self.create_gosu_font(window, name, size)
61
+ Gosu::Font.new window, name, size
62
+ end
63
+
64
+ def self.fonts
65
+ @fonts ||= {}
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,102 @@
1
+ require 'metro/game/dsl'
2
+
3
+ module Metro
4
+ module Game
5
+ extend self
6
+
7
+ def setup(game_configuration)
8
+ @config = game_configuration
9
+ end
10
+
11
+ #
12
+ # Creates a window and starts the game with the game parameters.
13
+ #
14
+ def start!
15
+ @window = Window.new width, height, fullscreen?
16
+ window.caption = name
17
+ window.scene = Scenes.generate(first_scene)
18
+ window.show
19
+ end
20
+
21
+ # The original parameters specified during execution. These are the args
22
+ # found on the command-line that are passed in when the game started.
23
+ def execution_parameters
24
+ @execution_parameters ||= []
25
+ end
26
+
27
+ attr_writer :execution_parameters
28
+
29
+ #
30
+ # @return the current game window.
31
+ #
32
+ attr_reader :window
33
+
34
+ #
35
+ # @return [Scene,NilClass] the current scene that is being displayed. If
36
+ # this is called before the window is being displayed when this will return
37
+ # a nil value.
38
+ #
39
+ def current_scene
40
+ window ? window.scene : nil
41
+ end
42
+
43
+ def first_scene
44
+ fetch(:first_scene)
45
+ end
46
+
47
+ def width
48
+ fetch(:width,800)
49
+ end
50
+
51
+ def height
52
+ fetch(:height,600)
53
+ end
54
+
55
+ def bounds
56
+ Units::RectangleBounds.new left: 0, right: width, top: 0, bottom: height
57
+ end
58
+
59
+ def dimensions
60
+ Units::Dimensions.of width, height
61
+ end
62
+
63
+ def center
64
+ Units::Point.at width / 2 , height / 2
65
+ end
66
+
67
+ def fullscreen?
68
+ !!fetch(:fullscreen)
69
+ end
70
+
71
+ def debug?
72
+ !!fetch(:debug)
73
+ end
74
+
75
+ def name
76
+ fetch(:name)
77
+ end
78
+
79
+ def authors
80
+ fetch(:authors)
81
+ end
82
+
83
+ def website
84
+ fetch(:website,Metro::WEBSITE)
85
+ end
86
+
87
+ def contact
88
+ fetch(:contact)
89
+ end
90
+
91
+ def controls
92
+ config.controls.defined_controls
93
+ end
94
+
95
+ def fetch(name,fallback = nil)
96
+ config.send(name) rescue fallback
97
+ end
98
+
99
+ attr_reader :config
100
+
101
+ end
102
+ end
@@ -0,0 +1,68 @@
1
+ require_relative '../events/controls'
2
+
3
+ module Metro
4
+ module Game
5
+ class DSL
6
+
7
+ def self.parse(&block)
8
+ config = new
9
+ config.instance_eval(&block)
10
+ config
11
+ end
12
+
13
+ def first_scene(scene_name = nil)
14
+ scene_name ? @first_scene = scene_name : @first_scene
15
+ end
16
+
17
+ def width(game_width = nil)
18
+ game_width ? @width = game_width : @width
19
+ end
20
+
21
+ def height(game_height = nil)
22
+ game_height ? @height = game_height : @height
23
+ end
24
+
25
+ def resolution(w,h)
26
+ [ width(w), height(h) ]
27
+ end
28
+
29
+ def fullscreen(set_fullscreen = nil)
30
+ set_fullscreen.nil? ? @fullscreen : @fullscreen = set_fullscreen
31
+ end
32
+
33
+ def debug(set_debug = nil)
34
+ set_debug.nil? ? @debug : @debug = set_debug
35
+ end
36
+
37
+ def name(set_name = nil)
38
+ set_name.nil? ? @name : @name = set_name
39
+ end
40
+
41
+ def author(name)
42
+ authors.push name
43
+ end
44
+
45
+ def authors
46
+ @authors ||= []
47
+ end
48
+
49
+ alias_method :artist, :author
50
+ alias_method :designer, :author
51
+
52
+ def website(game_website = nil)
53
+ game_website ? @website = game_website : @website
54
+ end
55
+
56
+ def contact(game_contact = nil)
57
+ game_contact ? @contact = game_contact : @contact
58
+ end
59
+
60
+ def controls(&block)
61
+ @controls ||= Controls.new
62
+ @controls.instance_eval(&block) if block
63
+ @controls
64
+ end
65
+
66
+ end
67
+ end
68
+ end