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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +12 -0
- data/Guardfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +189 -0
- data/Rakefile +18 -0
- data/bin/metro +16 -0
- data/changelog.md +157 -0
- data/lib/assets/menu-movement.wav +0 -0
- data/lib/assets/menu-selection.wav +0 -0
- data/lib/assets/missing.ogg +0 -0
- data/lib/assets/missing.png +0 -0
- data/lib/assets/missing.wav +0 -0
- data/lib/assets/missing_animation.png +0 -0
- data/lib/commands/generate_game.rb +13 -0
- data/lib/commands/generate_model.rb +25 -0
- data/lib/commands/generate_scene.rb +36 -0
- data/lib/commands/generate_view.rb +21 -0
- data/lib/commands/thor.rb +83 -0
- data/lib/core_ext/class.rb +14 -0
- data/lib/core_ext/numeric.rb +59 -0
- data/lib/gosu_ext/color.rb +62 -0
- data/lib/gosu_ext/gosu_constants.rb +53 -0
- data/lib/locale/en.yml +35 -0
- data/lib/locale/locale.rb +1 -0
- data/lib/metro.rb +140 -0
- data/lib/metro/animation.rb +135 -0
- data/lib/metro/animation/after_interval_factory.rb +12 -0
- data/lib/metro/animation/animation_factory.rb +15 -0
- data/lib/metro/animation/easing/ease_in.rb +15 -0
- data/lib/metro/animation/easing/easing.rb +51 -0
- data/lib/metro/animation/easing/linear.rb +15 -0
- data/lib/metro/animation/has_animations.rb +70 -0
- data/lib/metro/animation/implicit_animation.rb +100 -0
- data/lib/metro/animation/on_update_operation.rb +96 -0
- data/lib/metro/animation/scene_animation.rb +16 -0
- data/lib/metro/asset_path.rb +97 -0
- data/lib/metro/events/control_definition.rb +11 -0
- data/lib/metro/events/controls.rb +42 -0
- data/lib/metro/events/event_data.rb +60 -0
- data/lib/metro/events/event_dictionary.rb +52 -0
- data/lib/metro/events/event_factory.rb +17 -0
- data/lib/metro/events/event_relay.rb +300 -0
- data/lib/metro/events/event_state_manager.rb +63 -0
- data/lib/metro/events/events.rb +3 -0
- data/lib/metro/events/has_events.rb +108 -0
- data/lib/metro/events/hit_list.rb +75 -0
- data/lib/metro/events/unknown_sender.rb +5 -0
- data/lib/metro/font.rb +69 -0
- data/lib/metro/game.rb +102 -0
- data/lib/metro/game/dsl.rb +68 -0
- data/lib/metro/image.rb +68 -0
- data/lib/metro/logging.rb +33 -0
- data/lib/metro/missing_scene.rb +21 -0
- data/lib/metro/models/audio/song.rb +33 -0
- data/lib/metro/models/draws.rb +86 -0
- data/lib/metro/models/key_value_coding.rb +38 -0
- data/lib/metro/models/model.rb +236 -0
- data/lib/metro/models/model_factory.rb +32 -0
- data/lib/metro/models/models.rb +62 -0
- data/lib/metro/models/properties/animation_property.rb +115 -0
- data/lib/metro/models/properties/array_property.rb +24 -0
- data/lib/metro/models/properties/boolean_property.rb +27 -0
- data/lib/metro/models/properties/color_property.rb +116 -0
- data/lib/metro/models/properties/dimensions_property.rb +84 -0
- data/lib/metro/models/properties/font_property.rb +130 -0
- data/lib/metro/models/properties/image_property.rb +96 -0
- data/lib/metro/models/properties/model_property.rb +84 -0
- data/lib/metro/models/properties/numeric_property.rb +29 -0
- data/lib/metro/models/properties/options_property/no_option.rb +29 -0
- data/lib/metro/models/properties/options_property/options.rb +94 -0
- data/lib/metro/models/properties/options_property/options_property.rb +125 -0
- data/lib/metro/models/properties/position_property.rb +90 -0
- data/lib/metro/models/properties/property.rb +221 -0
- data/lib/metro/models/properties/property_owner.rb +137 -0
- data/lib/metro/models/properties/sample_property.rb +84 -0
- data/lib/metro/models/properties/scale_property.rb +80 -0
- data/lib/metro/models/properties/song_property.rb +89 -0
- data/lib/metro/models/properties/text_property.rb +75 -0
- data/lib/metro/models/ui/animated_sprite.rb +85 -0
- data/lib/metro/models/ui/border.rb +95 -0
- data/lib/metro/models/ui/fps.rb +54 -0
- data/lib/metro/models/ui/generic.rb +66 -0
- data/lib/metro/models/ui/grid_drawer.rb +74 -0
- data/lib/metro/models/ui/image.rb +87 -0
- data/lib/metro/models/ui/label.rb +175 -0
- data/lib/metro/models/ui/menu.rb +214 -0
- data/lib/metro/models/ui/model_label.rb +65 -0
- data/lib/metro/models/ui/model_labeler.rb +79 -0
- data/lib/metro/models/ui/rectangle.rb +59 -0
- data/lib/metro/models/ui/sprite.rb +79 -0
- data/lib/metro/models/ui/tile_map.rb +162 -0
- data/lib/metro/models/ui/ui.rb +13 -0
- data/lib/metro/parameters/command_line_args_parser.rb +68 -0
- data/lib/metro/parameters/options.rb +25 -0
- data/lib/metro/parameters/parameters.rb +2 -0
- data/lib/metro/sample.rb +40 -0
- data/lib/metro/scene.rb +477 -0
- data/lib/metro/scenes.rb +154 -0
- data/lib/metro/song.rb +56 -0
- data/lib/metro/template_message.rb +60 -0
- data/lib/metro/transitions/edit_transition_scene.rb +100 -0
- data/lib/metro/transitions/fade_transition_scene.rb +66 -0
- data/lib/metro/transitions/scene_transitions.rb +44 -0
- data/lib/metro/transitions/transition_scene.rb +19 -0
- data/lib/metro/units/bounds.rb +8 -0
- data/lib/metro/units/calculation_validations.rb +74 -0
- data/lib/metro/units/dimensions.rb +60 -0
- data/lib/metro/units/point.rb +51 -0
- data/lib/metro/units/rectangle_bounds.rb +85 -0
- data/lib/metro/units/scale.rb +46 -0
- data/lib/metro/units/units.rb +6 -0
- data/lib/metro/version.rb +32 -0
- data/lib/metro/views/json_view.rb +60 -0
- data/lib/metro/views/no_view.rb +34 -0
- data/lib/metro/views/parsers.rb +42 -0
- data/lib/metro/views/scene_view.rb +107 -0
- data/lib/metro/views/view.rb +133 -0
- data/lib/metro/views/writers.rb +43 -0
- data/lib/metro/views/yaml_view.rb +94 -0
- data/lib/metro/window.rb +94 -0
- data/lib/setup_handlers/exit_if_dry_run.rb +26 -0
- data/lib/setup_handlers/game_execution.rb +65 -0
- data/lib/setup_handlers/load_game_configuration.rb +65 -0
- data/lib/setup_handlers/load_game_files.rb +101 -0
- data/lib/setup_handlers/move_to_game_directory.rb +25 -0
- data/lib/setup_handlers/reload_game_on_game_file_changes.rb +79 -0
- data/lib/templates/game/README.md.tt +52 -0
- data/lib/templates/game/assets/brand.jpg +0 -0
- data/lib/templates/game/assets/hero.png +0 -0
- data/lib/templates/game/lib/custom_easing.rb +32 -0
- data/lib/templates/game/metro.tt +63 -0
- data/lib/templates/game/models/hero.rb +62 -0
- data/lib/templates/game/scenes/brand_scene.rb +19 -0
- data/lib/templates/game/scenes/brand_to_title_scene.rb +13 -0
- data/lib/templates/game/scenes/first_scene.rb +28 -0
- data/lib/templates/game/scenes/game_scene.rb +43 -0
- data/lib/templates/game/scenes/title_scene.rb +15 -0
- data/lib/templates/game/views/brand.yaml +4 -0
- data/lib/templates/game/views/brand_to_title.yaml +8 -0
- data/lib/templates/game/views/first.yaml +26 -0
- data/lib/templates/game/views/title.yaml +11 -0
- data/lib/templates/message.erb +23 -0
- data/lib/templates/model.rb.tt +111 -0
- data/lib/templates/scene.rb.tt +140 -0
- data/lib/templates/view.yaml.tt +11 -0
- data/lib/tmxed_ext/tile_set.rb +34 -0
- data/metro.gemspec +56 -0
- data/spec/core_ext/numeric_spec.rb +78 -0
- data/spec/core_ext/string_spec.rb +33 -0
- data/spec/gosu_ext/color_spec.rb +80 -0
- data/spec/metro/events/event_state_manager_spec.rb +5 -0
- data/spec/metro/models/key_value_coding_spec.rb +61 -0
- data/spec/metro/models/properties/array_property_spec.rb +60 -0
- data/spec/metro/models/properties/color_property_spec.rb +85 -0
- data/spec/metro/models/properties/dimensions_spec.rb +29 -0
- data/spec/metro/models/properties/font_property_spec.rb +127 -0
- data/spec/metro/models/properties/numeric_property_spec.rb +46 -0
- data/spec/metro/models/properties/options_property/no_option_spec.rb +25 -0
- data/spec/metro/models/properties/options_property/options_property_spec.rb +133 -0
- data/spec/metro/models/properties/options_property/options_spec.rb +125 -0
- data/spec/metro/models/properties/position_property_spec.rb +90 -0
- data/spec/metro/models/ui/label_spec.rb +259 -0
- data/spec/metro/parameters/command_line_args_parser_spec.rb +42 -0
- 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 +38 -0
- data/spec/metro/scenes_spec.rb +77 -0
- data/spec/metro/units/point_spec.rb +132 -0
- data/spec/metro/views/view_spec.rb +53 -0
- data/spec/setup_handlers/exit_if_dry_run_spec.rb +27 -0
- data/spec/setup_handlers/reload_game_on_game_file_changes_spec.rb +68 -0
- data/spec/spec_helper.rb +20 -0
- metadata +374 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'on_update_operation'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
|
5
|
+
module SceneAnimation
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def build(options,&block)
|
9
|
+
animation = Metro::ImplicitAnimation.new options
|
10
|
+
animation.on_complete(&block) if block
|
11
|
+
animation
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# The asset_path is a helper which will generate a filepath based on the current
|
4
|
+
# working directory of the game. This allows for game author's to specify a path
|
5
|
+
# relative within the assets directory of their game.
|
6
|
+
#
|
7
|
+
# @note Paths that are defined within views use this helper and are assumed to
|
8
|
+
# be paths relative within the assets directory of the game. For images,
|
9
|
+
# samples, and songs in a model consider using a property.
|
10
|
+
#
|
11
|
+
# @see Metro::Model::ImageProperty
|
12
|
+
# @see Metro::Model::AnimationProperty
|
13
|
+
# @see Metro::Model::SongProperty
|
14
|
+
# @see Metro::Model::SampleProperty
|
15
|
+
#
|
16
|
+
# @note Also consider the model classes Image, Animation, Song, and Sample for
|
17
|
+
# creating the assets found within the assets folder. Each of those will
|
18
|
+
# assist with using the asset_path internally to find the asset.
|
19
|
+
#
|
20
|
+
# @see Metro::Image
|
21
|
+
# @see Metro::Animation
|
22
|
+
# @see Metro::Song
|
23
|
+
# @see Metro::Sample
|
24
|
+
#
|
25
|
+
# @example Loading a raw Gosu::Image with an `asset_path`
|
26
|
+
#
|
27
|
+
# class Player < Metro::Model
|
28
|
+
# def image
|
29
|
+
# @image ||= Gosu::Image.new( window, asset_path("player.png"), false )
|
30
|
+
# end
|
31
|
+
# def draw
|
32
|
+
# image.draw_rot(x,y,2,angle)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
def asset_path(name)
|
37
|
+
File.join Dir.pwd, "assets", name
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# The metro_asset_path is a helper which will generate a filepath based on the
|
42
|
+
# directory of the metro library. This is used to retrieve internal assets which
|
43
|
+
# can be used to serve up missing images, animations, samples, or songs or
|
44
|
+
# defaults that may come with the metro game library.
|
45
|
+
#
|
46
|
+
#
|
47
|
+
def metro_asset_path(name)
|
48
|
+
File.join Metro.asset_dir, name
|
49
|
+
end
|
50
|
+
|
51
|
+
module Metro
|
52
|
+
|
53
|
+
#
|
54
|
+
# An AssetPath searches through the various paths based on the path provided.
|
55
|
+
#
|
56
|
+
# First it assumes the path is absolute, second it assuemts that path is within
|
57
|
+
# the game, and third it assumes it is within metro itself.
|
58
|
+
#
|
59
|
+
class AssetPath
|
60
|
+
|
61
|
+
def self.with(path)
|
62
|
+
path.is_a?(AssetPath) ? path : new(path.to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialize(path)
|
66
|
+
@path = path
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :path
|
70
|
+
|
71
|
+
def filepath
|
72
|
+
@filepath ||= begin
|
73
|
+
absolute_asset?(path) or game_asset?(path) or metro_asset?(path)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def absolute_asset?(path)
|
78
|
+
asset_at_path? path
|
79
|
+
end
|
80
|
+
|
81
|
+
def game_asset?(path)
|
82
|
+
asset_at_path? asset_path(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
def metro_asset?(path)
|
86
|
+
asset_at_path? metro_asset_path(path)
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :to_s, :filepath
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def asset_at_path?(asset_path)
|
94
|
+
asset_path if File.exists?(asset_path) and File.file?(asset_path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'control_definition'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
#
|
5
|
+
# Assists in creating and the storing of ControlDefinitions.
|
6
|
+
#
|
7
|
+
# @see DSL
|
8
|
+
#
|
9
|
+
class Controls
|
10
|
+
|
11
|
+
#
|
12
|
+
# Creation through controls is usually done with an instance_eval
|
13
|
+
# of a block and this allows for a flexible interface.
|
14
|
+
#
|
15
|
+
# @param [String,Symbol] name the name or the alias of the control
|
16
|
+
# as it will be used within the course of the game.
|
17
|
+
#
|
18
|
+
def method_missing(name,*params,&block)
|
19
|
+
options = params.find {|param| param.is_a? Hash }
|
20
|
+
define_control(name,options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_control(name,options)
|
24
|
+
event = _event_type(options)
|
25
|
+
args = _event_args(options)
|
26
|
+
|
27
|
+
defined_controls.push ControlDefinition.new name, event, args
|
28
|
+
end
|
29
|
+
|
30
|
+
def _event_type(options)
|
31
|
+
options[:is]
|
32
|
+
end
|
33
|
+
|
34
|
+
def _event_args(options)
|
35
|
+
options[:with]
|
36
|
+
end
|
37
|
+
|
38
|
+
def defined_controls
|
39
|
+
@defined_controls ||= []
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Metro
|
2
|
+
class EventData
|
3
|
+
|
4
|
+
attr_reader :mouse_point, :created_at
|
5
|
+
|
6
|
+
def initialize(window)
|
7
|
+
@created_at = Time.now
|
8
|
+
@mouse_point = Metro::Units::Point.at window.mouse_x, window.mouse_y
|
9
|
+
|
10
|
+
capture_modifier_keys(window)
|
11
|
+
end
|
12
|
+
|
13
|
+
def modifier_keys
|
14
|
+
@modifier_keys ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def capture_modifier_keys(window)
|
18
|
+
self.class.modifier_key_list.each do |key|
|
19
|
+
modifier_keys[key] = window.button_down?(key)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# TODO: This attempt to reduce duplication is brittle and will likely end in heartache.
|
25
|
+
#
|
26
|
+
|
27
|
+
def self.modifier_key_list_names
|
28
|
+
@modifier_key_list_names ||= %w[ KbLeftControl KbRightControl
|
29
|
+
KbLeftAlt KbRightAlt
|
30
|
+
KbLeftMeta KbRightMeta
|
31
|
+
KbLeftShift KbRightShift ]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.modifier_key_list
|
35
|
+
@modifier_key_list ||= modifier_key_list_names.map {|key| "Gosu::#{key}".constantize }
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Generate methods for each left and right modifier key
|
40
|
+
#
|
41
|
+
|
42
|
+
modifier_key_list_names.each_with_index do |key_name,index|
|
43
|
+
define_method "#{key_name.gsub(/^Kb/,'').underscore}?" do
|
44
|
+
modifier_keys[self.class.modifier_key_list[index]]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Define generic modifier keys that is not concerned with whether it
|
50
|
+
# was the left key or the right key.
|
51
|
+
#
|
52
|
+
|
53
|
+
[ :control?, :alt?, :meta?, :shift? ].each do |generic|
|
54
|
+
define_method generic do
|
55
|
+
send("left_#{generic}") or send("right_#{generic}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'event_factory'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
|
5
|
+
module EventDictionary
|
6
|
+
extend self
|
7
|
+
|
8
|
+
#
|
9
|
+
# All defined events within this dictionary.
|
10
|
+
#
|
11
|
+
def events
|
12
|
+
@events ||= HashWithIndifferentAccess.new
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# @example Adding a new SceneEvent to the Dictionary
|
17
|
+
#
|
18
|
+
# SceneEventDictionary.add target: scene_name, type: event_type, args: args, block: block
|
19
|
+
#
|
20
|
+
def add(params = {})
|
21
|
+
target = params[:target]
|
22
|
+
event = EventFactory.new params[:type], params[:args], ¶ms[:block]
|
23
|
+
events[target] = events_for_target(target).push event
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Return all the events for all the specified targets.
|
28
|
+
#
|
29
|
+
def events_for_targets(*list)
|
30
|
+
found_events = Array(list).flatten.compact.map {|s| events_for_target(s) }.flatten.compact
|
31
|
+
found_events
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Return the events for the specified target
|
36
|
+
#
|
37
|
+
def events_for_target(scene_name)
|
38
|
+
events[scene_name] ||= []
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# When the game is reset the event dictionary needs to flush out all of the events that it
|
43
|
+
# has loaded as the game files will be reloaded. All metro related components will not
|
44
|
+
# be removed as those files are not reloaded when the game is reloaded.
|
45
|
+
#
|
46
|
+
def reset!
|
47
|
+
events.delete_if { |name,events| ! name.start_with? "metro/" }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require_relative 'event_data'
|
2
|
+
|
3
|
+
module Metro
|
4
|
+
|
5
|
+
#
|
6
|
+
# An EventRelay represents a target's willingness to respond to events
|
7
|
+
# generate from the window. An event relay is generate for every scene
|
8
|
+
# but additional relays can be generated to also listen for events.
|
9
|
+
#
|
10
|
+
# An event relay can register a target to listen for the following window
|
11
|
+
# events: 'button_down'; 'button_up'; and 'button_held'.
|
12
|
+
#
|
13
|
+
# @note registering for 'button_held' events require that the window be
|
14
|
+
# speicfied. As that is the only way to ask if the button is currently
|
15
|
+
# pressed.
|
16
|
+
#
|
17
|
+
# @see #on_up
|
18
|
+
# @see #on_down
|
19
|
+
# @see #on_hold
|
20
|
+
#
|
21
|
+
class EventRelay
|
22
|
+
|
23
|
+
#
|
24
|
+
# Defines the provided controls for every EventRelay that is created.
|
25
|
+
#
|
26
|
+
# @see #define_control
|
27
|
+
#
|
28
|
+
# @param [Array<ControlDefinition>] controls the definitions of controls
|
29
|
+
# that should be added to all EventRelays.
|
30
|
+
#
|
31
|
+
def self.define_controls(controls)
|
32
|
+
controls.each { |control| define_control control }
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Defines a control from a ControlDefinition for all EventRelays. A
|
37
|
+
# control is a way of defining a shortcut for a common event. This
|
38
|
+
# could be the use of a common set of keys for confirmation or canceling.
|
39
|
+
#
|
40
|
+
def self.define_control(control)
|
41
|
+
check_for_already_defined_control!(control)
|
42
|
+
|
43
|
+
define_method control.name do |&block|
|
44
|
+
send(control.event,*control.args,&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.check_for_already_defined_control!(control)
|
49
|
+
if instance_methods.include? control.name
|
50
|
+
error! "error.reserved_control_name", name: control.name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# An event relay is created a with a target and optionally a window.
|
56
|
+
#
|
57
|
+
# @param [Object] target the object that will execute the code when
|
58
|
+
# the button events have fired have been triggered.
|
59
|
+
# @param [Window] window the window of the game. This parameter is
|
60
|
+
# optional and only required if the events are interested in buttons
|
61
|
+
# being held.
|
62
|
+
#
|
63
|
+
def initialize(target,window = nil)
|
64
|
+
@target = target
|
65
|
+
@window = window
|
66
|
+
@up_actions ||= {}
|
67
|
+
@down_actions ||= {}
|
68
|
+
@held_actions ||= {}
|
69
|
+
@custom_notifications ||= HashWithIndifferentAccess.new([])
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :target, :window
|
73
|
+
|
74
|
+
#
|
75
|
+
# Register for a button_down event. These events are fired when
|
76
|
+
# the button is pressed down. This event only fires once when the
|
77
|
+
# button moves from the not pressed to the down state.
|
78
|
+
#
|
79
|
+
# @example Registering for a button down event to call a method named 'previous_option'
|
80
|
+
#
|
81
|
+
# class ExampleScene
|
82
|
+
# event :on_down, GpLeft, GpUp, do: :previous_option
|
83
|
+
#
|
84
|
+
# def previous_option
|
85
|
+
# @selected_index = @selected_index - 1
|
86
|
+
# @selected_index = options.length - 1 if @selected_index <= -1
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# Here in this scene if the GpLeft or GpUp buttons are pressed down the method
|
91
|
+
# `previous_options` will be executed.
|
92
|
+
#
|
93
|
+
#
|
94
|
+
# @example Registering for a button down event with a block of code to execute
|
95
|
+
#
|
96
|
+
# class ExampleScene
|
97
|
+
# event :on_down, GpLeft, GpUp do
|
98
|
+
# @selected_index = @selected_index - 1
|
99
|
+
# @selected_index = options.length - 1 if @selected_index <= -1
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# This example uses a block instead of a method name but it is absolultey the same
|
104
|
+
# as the last example.
|
105
|
+
#
|
106
|
+
def on_down(*args,&block)
|
107
|
+
_on(@down_actions,args,block)
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :button_down, :on_down
|
111
|
+
|
112
|
+
#
|
113
|
+
# Register for a button_up event. These events are fired when
|
114
|
+
# the button is released (from being pressed down). This event only fires
|
115
|
+
# once when the button moves from the pressed state to the up state.
|
116
|
+
#
|
117
|
+
# @example Registering for a button down event to call a method named 'next_option'
|
118
|
+
#
|
119
|
+
# class ExampleScene
|
120
|
+
# event :on_up, KbEscape, do: :leave_scene
|
121
|
+
#
|
122
|
+
# def leave_scene
|
123
|
+
# transition_to :title
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# Here in this scene if the Escape Key is pressed and released the example scene
|
128
|
+
# will transition to the title scene.
|
129
|
+
#
|
130
|
+
# @example Registering for a button up event with a block of code to execute
|
131
|
+
#
|
132
|
+
# class ExampleScene
|
133
|
+
# event :on_up, KbEscape do
|
134
|
+
# transition_to :title
|
135
|
+
# end
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# This example uses a block instead of a method name but it is absolultey the same
|
139
|
+
# as the last example.
|
140
|
+
#
|
141
|
+
def on_up(*args,&block)
|
142
|
+
_on(@up_actions,args,block)
|
143
|
+
end
|
144
|
+
|
145
|
+
alias_method :button_up, :on_up
|
146
|
+
|
147
|
+
#
|
148
|
+
# Register for a button_held event. These events are fired when
|
149
|
+
# the button is currently in the downstate. This event continues to fire at the
|
150
|
+
# beginning of every update of a scene until the button is released.
|
151
|
+
#
|
152
|
+
# @note button_held events require that the window be specified during initialization.
|
153
|
+
#
|
154
|
+
# @example Registering for button held events
|
155
|
+
#
|
156
|
+
# class ExampleScene
|
157
|
+
# event :on_hold KbLeft, GpLeft do
|
158
|
+
# player.turn_left
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# event :on_hold, KbRight, GpRight do
|
162
|
+
# player.turn_right
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# event :on_hold, KbUp, Gosu::GpButton0, do: :calculate_accleration
|
166
|
+
#
|
167
|
+
# def calculate_acceleration
|
168
|
+
# long_complicated_calculated_result = 0
|
169
|
+
# # ... multi-line calculations to determine the player acceleration ...
|
170
|
+
# player.accelerate = long_complicated_calculated_result
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
def on_hold(*args,&block)
|
175
|
+
log.warn "Registering for a on_hold event requires that a window be provided." unless window
|
176
|
+
_on(@held_actions,args,block)
|
177
|
+
end
|
178
|
+
|
179
|
+
alias_method :button_hold, :on_hold
|
180
|
+
alias_method :button_held, :on_hold
|
181
|
+
|
182
|
+
#
|
183
|
+
# Register for a custom notification event. These events are fired when
|
184
|
+
# another object within the game posts a notification with matching criteria.
|
185
|
+
# If there has indeed been a match, then the stored action block will be fired.
|
186
|
+
#
|
187
|
+
# When the action block is specified is defined with no parameters it is assumed that
|
188
|
+
# that the code should be executed within the context of the object that defined
|
189
|
+
# the action, the 'target'.
|
190
|
+
#
|
191
|
+
# @example Registering for a save complete event that would re-enable a menu.
|
192
|
+
#
|
193
|
+
# class ExampleScene
|
194
|
+
# event :notification, :save_complete do
|
195
|
+
# menu.enabled!
|
196
|
+
# end
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# The action block can also be specified with two parameters. In this case the code is
|
200
|
+
# no longer executed within the context of the object and is instead provided the
|
201
|
+
# the action target and the action source.
|
202
|
+
#
|
203
|
+
# @example Registering for a win game event that explicitly states the target and source.
|
204
|
+
#
|
205
|
+
# class ExampleScene
|
206
|
+
#
|
207
|
+
# event :notification, :win_game do |target,winner|
|
208
|
+
# target.declare_winner winner
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# def declare_winner(winning_player)
|
212
|
+
# # ...
|
213
|
+
# end
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
def notification(param,&block)
|
217
|
+
custom_notifications[param.to_sym] = custom_notifications[param.to_sym] + [ block ]
|
218
|
+
end
|
219
|
+
|
220
|
+
attr_reader :up_actions, :down_actions, :held_actions, :custom_notifications
|
221
|
+
|
222
|
+
def _on(hash,args,block)
|
223
|
+
options = (args.last.is_a?(Hash) ? args.pop : {})
|
224
|
+
|
225
|
+
args.each do |keystroke|
|
226
|
+
hash[keystroke] = block || lambda { |instance| send(options[:do]) }
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
#
|
231
|
+
# This is called by external or parent source of events, usually a Scene, when a button up event
|
232
|
+
# has been triggered.
|
233
|
+
#
|
234
|
+
def fire_button_up(id)
|
235
|
+
execute_block_for_target( &up_action(id) )
|
236
|
+
end
|
237
|
+
|
238
|
+
#
|
239
|
+
# This is called by external or parent source of events, usually a Scene, when a button down
|
240
|
+
# event has been triggered.
|
241
|
+
#
|
242
|
+
def fire_button_down(id)
|
243
|
+
execute_block_for_target( &down_action(id) )
|
244
|
+
end
|
245
|
+
|
246
|
+
#
|
247
|
+
# Fire the events mapped to the held buttons within the context
|
248
|
+
# of the specified target. This method is differently formatted because held buttons are not
|
249
|
+
# events but polling to see if the button is still being held.
|
250
|
+
#
|
251
|
+
def fire_events_for_held_buttons
|
252
|
+
held_actions.each do |key,action|
|
253
|
+
execute_block_for_target(&action) if window and window.button_down?(key)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def execute_block_for_target(&block)
|
258
|
+
event_data = EventData.new(window)
|
259
|
+
target.instance_exec(event_data,&block)
|
260
|
+
end
|
261
|
+
|
262
|
+
# @return a block of code that is mapped for the 'button_up' id
|
263
|
+
def up_action(id)
|
264
|
+
up_actions[id] || lambda {|no_op| }
|
265
|
+
end
|
266
|
+
|
267
|
+
# @return a block of code that is mapped for the 'button_down' id
|
268
|
+
def down_action(id)
|
269
|
+
down_actions[id] || lambda {|no_op| }
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# Fire all events mapped to the matching notification.
|
274
|
+
#
|
275
|
+
def fire_events_for_notification(event,sender)
|
276
|
+
notification_actions = custom_notifications[event]
|
277
|
+
notification_actions.each do |action|
|
278
|
+
_fire_event_for_notification(event,sender,action)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
#
|
283
|
+
# Fire a single event based on the matched notification.
|
284
|
+
#
|
285
|
+
# An action without any parameters is assumed to be executed within the contexxt
|
286
|
+
# of the target. If there are two parameters we will simply execute the action and
|
287
|
+
# pass it both the target and the sender.
|
288
|
+
#
|
289
|
+
def _fire_event_for_notification(event,sender,action)
|
290
|
+
if action.arity == 2
|
291
|
+
target.instance_exec(sender,event,&action)
|
292
|
+
elsif action.arity == 1
|
293
|
+
target.instance_exec(sender,&action)
|
294
|
+
else
|
295
|
+
target.instance_eval(&action)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
end
|