metro 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/changelog.md +8 -1
  2. data/lib/core_ext/class.rb +14 -0
  3. data/lib/metro.rb +1 -0
  4. data/lib/metro/events/event_data.rb +3 -4
  5. data/lib/metro/events/event_state_manager.rb +63 -0
  6. data/lib/metro/events/events.rb +3 -0
  7. data/lib/metro/events/hit_list.rb +4 -5
  8. data/lib/metro/events/unknown_sender.rb +1 -1
  9. data/lib/metro/models/model.rb +14 -25
  10. data/lib/metro/models/model_factory.rb +1 -1
  11. data/lib/metro/models/models.rb +62 -0
  12. data/lib/metro/models/properties/position_property.rb +1 -1
  13. data/lib/metro/models/ui/animated_sprite.rb +85 -0
  14. data/lib/metro/models/ui/border.rb +44 -18
  15. data/lib/metro/models/ui/fps.rb +1 -1
  16. data/lib/metro/models/ui/generic.rb +24 -3
  17. data/lib/metro/models/ui/model_label.rb +1 -1
  18. data/lib/metro/models/ui/model_labeler.rb +2 -2
  19. data/lib/metro/models/ui/rectangle.rb +1 -1
  20. data/lib/metro/models/ui/sprite.rb +79 -0
  21. data/lib/metro/models/ui/ui.rb +12 -0
  22. data/lib/metro/scene.rb +13 -61
  23. data/lib/metro/scenes.rb +11 -13
  24. data/lib/metro/transitions/edit_transition_scene.rb +2 -2
  25. data/lib/metro/units/calculation_validations.rb +74 -0
  26. data/lib/metro/units/dimensions.rb +11 -19
  27. data/lib/metro/units/point.rb +6 -22
  28. data/lib/metro/units/rectangle_bounds.rb +10 -6
  29. data/lib/metro/units/scale.rb +7 -0
  30. data/lib/metro/units/units.rb +1 -0
  31. data/lib/metro/version.rb +1 -1
  32. data/lib/metro/window.rb +7 -3
  33. data/lib/setup_handlers/reload_game_on_game_file_changes.rb +6 -6
  34. data/lib/templates/game/models/hero.rb +35 -6
  35. data/lib/templates/model.rb.tt +1 -1
  36. data/spec/core_ext/string_spec.rb +0 -20
  37. data/spec/metro/units/point_spec.rb +132 -0
  38. data/spec/setup_handlers/exit_if_dry_run_spec.rb +27 -0
  39. data/spec/setup_handlers/reload_game_on_game_file_changes_spec.rb +68 -0
  40. metadata +21 -9
@@ -1,7 +1,7 @@
1
1
  module Metro
2
2
  module UI
3
3
 
4
- class FPS < Metro::Model
4
+ class FPS < Model
5
5
 
6
6
  property :placement, type: :text, default: 'top'
7
7
  property :color, default: "rgba(255,255,255,1.0)"
@@ -35,9 +35,30 @@ module Metro
35
35
  private
36
36
 
37
37
  def cannot_draw_message
38
- [ "Unable to draw #{name} in #{scene}", "",
39
- " The actor named '#{name}' does not specify a suitable model so it could not be drawn in the scene.",
40
- "", " " + properties.to_s, "" ].join("\n")
38
+ %{Unable to draw #{name} in #{scene}
39
+
40
+ The actor named '#{name}' does not specify a suitable model so it could not be drawn in the scene.
41
+
42
+ #{properties}
43
+
44
+ Did you mean to use one of the following models:
45
+
46
+ Models defined in #{Game.name}:
47
+
48
+ #{user_defined_models.join(', ')}
49
+
50
+ Models defined in Metro:
51
+
52
+ #{metro_models.join(', ')}
53
+ }
54
+ end
55
+
56
+ def metro_models
57
+ Models.list.find_all {|m| m =~ /metro(::|\/).+(::|\/).+/i }
58
+ end
59
+
60
+ def user_defined_models
61
+ Models.list - metro_models
41
62
  end
42
63
 
43
64
  end
@@ -7,7 +7,7 @@ module Metro
7
7
  # The model label is used by the model labeler which is a facet of the
8
8
  # edit scene
9
9
  #
10
- class ModelLabel < Metro::Model
10
+ class ModelLabel < Model
11
11
 
12
12
  # Stores the model that is currently being labeled.
13
13
  attr_accessor :target
@@ -9,7 +9,7 @@ module Metro
9
9
  # the bounding boxes and labeles around all the actors within the scene
10
10
  # being edited.
11
11
  #
12
- class ModelLabeler < Metro::Model
12
+ class ModelLabeler < Model
13
13
 
14
14
  # @attribute
15
15
  # The color use for the border surrounding each actor and the background
@@ -58,7 +58,7 @@ module Metro
58
58
  label = labels[drawer.name]
59
59
 
60
60
  unless label
61
- label = create "metro::ui::modellabel", target: drawer
61
+ label = create "metro::ui::model_label", target: drawer
62
62
  labels[drawer.name] = label
63
63
  end
64
64
 
@@ -12,7 +12,7 @@ module Metro
12
12
  # color: "rgba(255,0,0,1.0)", dimensions: "200,200"
13
13
  # end
14
14
  #
15
- class Rectangle < ::Metro::Model
15
+ class Rectangle < Model
16
16
 
17
17
  # @attribute
18
18
  # The position of the upper-left corner of the rectangle
@@ -0,0 +1,79 @@
1
+ module Metro
2
+ module UI
3
+
4
+ #
5
+ # A sprite is a Metro model that is specially designed to draw and manage
6
+ # an image. A sprite maintains an image, location information, and rotation.
7
+ #
8
+ class Sprite < Model
9
+
10
+ # @attribute
11
+ # The image that will be drawn for the sprite
12
+ property :image
13
+
14
+ # @attribute
15
+ # The point at which the sprite should be drawn
16
+ property :position
17
+
18
+ # @attribute
19
+ # This is the color of the spirte. The color usually remains white, and
20
+ # the color property is implemented by the `alpha` value is the one thing
21
+ # that is altered to fade in and fade out the sprite.
22
+ property :color
23
+
24
+ # @attribute
25
+ # The scale at which to draw the sprite. This is default scale of 1.
26
+ property :scale
27
+
28
+ # @attribute
29
+ # The center, horizontal position, as expressed in a ratio, of the image.
30
+ property :center_x, type: :numeric, default: 0.5
31
+
32
+ # @attribute
33
+ # The center, vertical position, as expressed in a ratio, of the image.
34
+ property :center_y, type: :numeric, default: 0.5
35
+
36
+ # @attribute
37
+ # The angle at which the sprite should be drawn. This is by default 0.
38
+ property :angle
39
+
40
+ # @attribute
41
+ # The height and width of the sprite is based on the image of the sprite.
42
+ property :dimensions do
43
+ image.dimensions
44
+ end
45
+
46
+ # @return [RectangleBounds] the bounds of the sprite.
47
+ def bounds
48
+ Bounds.new left: left, right: right, top: top, bottom: bottom
49
+ end
50
+
51
+ # @return [Float] the left-most x position of the sprite
52
+ def left
53
+ x - width * center_x
54
+ end
55
+
56
+ # @return [Float] the right-most x position of the sprite
57
+ def right
58
+ left + width * x_factor
59
+ end
60
+
61
+ # @return [Float] the top-most y position of the sprite
62
+ def top
63
+ y - height * center_y
64
+ end
65
+
66
+ # @return [Float] the bottom-most y position of the sprite
67
+ def bottom
68
+ top + height * y_factor
69
+ end
70
+
71
+ #
72
+ # By default the sprite will draw the image defined for it.
73
+ #
74
+ def draw
75
+ image.draw_rot x, y, z_order, angle, center_x, center_y, x_factor, y_factor, color
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'generic'
2
+ require_relative 'label'
3
+ require_relative 'menu'
4
+ require_relative 'image'
5
+ require_relative 'rectangle'
6
+ require_relative 'grid_drawer'
7
+ require_relative 'border'
8
+ require_relative 'model_label'
9
+ require_relative 'model_labeler'
10
+ require_relative 'fps'
11
+ require_relative 'sprite'
12
+ require_relative 'animated_sprite'
@@ -1,9 +1,5 @@
1
1
  require_relative 'views/scene_view'
2
-
3
- require_relative 'events/has_events'
4
- require_relative 'events/event_relay'
5
- require_relative 'events/unknown_sender'
6
-
2
+ require_relative 'events/events'
7
3
  require_relative 'models/draws'
8
4
 
9
5
  require_relative 'animation/has_animations'
@@ -19,7 +15,6 @@ module Metro
19
15
  # @see #show
20
16
  # @see #update
21
17
  # @see #draw
22
- # @see #events
23
18
  #
24
19
  # A fair number of private methods within Scene are prefaced with an underscore.
25
20
  # These methods often call non-underscored methods within those methods. This allows
@@ -102,16 +97,12 @@ module Metro
102
97
  end
103
98
 
104
99
  #
105
- # Post a custom notification event. This will trigger any objects that are listening
106
- # for custom events.
100
+ # Post a custom notification event. This will trigger an event for all the
101
+ # objects that are registered for notification with the current state.
107
102
  #
108
103
  def notification(event,sender=nil)
109
-
110
104
  sender = sender || UnknownSender
111
-
112
- event_relays.each do |relay|
113
- relay.fire_events_for_notification(event,sender)
114
- end
105
+ state.fire_events_for_notification(event,sender)
115
106
  end
116
107
 
117
108
  #
@@ -203,7 +194,8 @@ module Metro
203
194
  def window=(window)
204
195
  @window = window
205
196
 
206
- event_relays.clear
197
+ state.window = window
198
+ state.clear
207
199
 
208
200
  register_events!
209
201
  register_actors!
@@ -335,11 +327,9 @@ module Metro
335
327
  #
336
328
  # Captures all classes that subclass Scene.
337
329
  #
338
- # @see #self.scenes
339
- #
340
330
  def self.inherited(base)
341
331
  scenes << base.to_s
342
- Scenes.add_scene(base)
332
+ Scenes.add(base)
343
333
  end
344
334
 
345
335
  #
@@ -455,58 +445,20 @@ module Metro
455
445
  # be mapped to the specified target.
456
446
  #
457
447
  def register_events_for_target(target,events)
458
- target_relay = EventRelay.new(target,window)
459
-
460
- events.each do |target_event|
461
- target_relay.send target_event.event, *target_event.buttons, &target_event.block
462
- end
463
-
464
- event_relays.push(target_relay)
448
+ state.add_events_for_target(target,events)
465
449
  end
466
450
 
467
451
  #
468
- # The events object that is configured through the {#events} method, which stores
469
- # all the gamepad and keyboard events defined. By default a scene has an event
470
- # relay defined. Additional relays can be defined based on the components added.
452
+ # The event state manager is configured through the {#events} method, which
453
+ # stores all the gamepad and keyboard events defined. By default a scene is
454
+ # placed in the default state and events that are added to this basic state.
471
455
  #
472
456
  # @see Events
473
- # @see #add_event_relay
474
457
  #
475
- def event_relays
476
- @event_relays ||= []
458
+ def state
459
+ @event_state_manager ||= EventStateManager.new
477
460
  end
478
461
 
479
- #
480
- # This method is called during a scene update and will fire all the events
481
- # that have been defined for all held buttons for all defined event relays.
482
- #
483
- def fire_events_for_held_buttons
484
- event_relays.each do |relay|
485
- relay.fire_events_for_held_buttons
486
- end
487
- end
488
-
489
- #
490
- # This method is called before a scene update and passes the button up events
491
- # to each of the defined event relays.
492
- #
493
- def button_up(id)
494
- event_relays.each do |relay|
495
- relay.fire_button_up(id)
496
- end
497
- end
498
-
499
- #
500
- # This method is called before a scene update and passes the button down events
501
- # to each of the defined event relays.
502
- #
503
- def button_down(id)
504
- event_relays.each do |relay|
505
- relay.fire_button_down(id)
506
- end
507
- end
508
-
509
-
510
462
  #
511
463
  # A Scene represented as a hash currently only contains the drawers
512
464
  #
@@ -33,7 +33,7 @@ module Metro
33
33
  #
34
34
  # @param [Scene] scene the scene to be added to the hash of Scenes.
35
35
  #
36
- def add_scene(scene)
36
+ def add(scene)
37
37
  all_scenes_for(scene).each { |scene| scenes_hash[scene.scene_name] = scene.to_s }
38
38
  end
39
39
 
@@ -44,7 +44,14 @@ module Metro
44
44
  # @return the Scene class that is found matching the specified scene name.
45
45
  #
46
46
  def find(scene_name)
47
- scene_class( scenes_hash[scene_name] )
47
+ scenes_hash[scene_name].constantize
48
+ end
49
+
50
+ #
51
+ # @return [Array<String>] all the names supported by the scenes hash.
52
+ #
53
+ def list
54
+ scenes_hash.keys
48
55
  end
49
56
 
50
57
  #
@@ -119,7 +126,7 @@ module Metro
119
126
  #
120
127
  def hash_with_missing_scene_default
121
128
  hash = HashWithIndifferentAccess.new do |hash,key|
122
- missing_scene = scene_class(hash[:missing_scene])
129
+ missing_scene = hash[:missing_scene].constantize
123
130
  missing_scene.missing_scene = key.to_sym
124
131
  missing_scene
125
132
  end
@@ -136,20 +143,11 @@ module Metro
136
143
  #
137
144
  def all_scenes_for(scenes)
138
145
  Array(scenes).map do |scene_class_name|
139
- scene = scene_class(scene_class_name)
146
+ scene = scene_class_name.constantize
140
147
  [ scene ] + all_scenes_for(scene.scenes)
141
148
  end.flatten.compact
142
149
  end
143
150
 
144
- #
145
- # @param [String,Symbol] class_name the name of the class that you want the class
146
- #
147
- # @return the class with the given class name
148
- #
149
- def scene_class(class_or_class_name)
150
- class_or_class_name.class == Class ? class_or_class_name : class_or_class_name.constantize
151
- end
152
-
153
151
  end
154
152
  end
155
153
 
@@ -28,8 +28,8 @@ module Metro
28
28
  # easier to dup scenes.
29
29
  #
30
30
  self.class.drawings.clear
31
- self.class.draw :overlay, model: "metro::ui::griddrawer"
32
- self.class.draw :labeler, model: "metro::ui::modellabeler"
31
+ self.class.draw :overlay, model: "metro::ui::grid_drawer"
32
+ self.class.draw :labeler, model: "metro::ui::model_labeler"
33
33
  add_actors_to_scene
34
34
  after_initialize
35
35
  end
@@ -0,0 +1,74 @@
1
+ module Metro
2
+ module Units
3
+ module CalculationValidations
4
+
5
+ #
6
+ # @param [Object] value the other object that needs to be validated.
7
+ #
8
+ def check_calculation_requirements(value)
9
+ if calculation_requirements.find { |method| ! value.respond_to?(method) }
10
+ raise "Unable to perform operation with #{value} #{value.class} It is missing a property #{calculation_requirements.join(",")}"
11
+ end
12
+ end
13
+
14
+ #
15
+ # @return [Array] an array of methods that are required to be on the
16
+ # object for it to be correctly calculated.
17
+ #
18
+ # @note this method is intended to be defined in the including class. This
19
+ # method is included here when one has not been provided.
20
+ def calculation_requirements
21
+ []
22
+ end
23
+
24
+ #
25
+ # Add this object to another object.
26
+ #
27
+ # @return a new object that is the sum of the two objects
28
+ #
29
+ def +(value)
30
+ self.class.new *calculate(value,:+)
31
+ end
32
+
33
+ #
34
+ # Subtract the other object from this object.
35
+ #
36
+ # @return a new object that is the difference of the original object
37
+ # and the value specified.
38
+ #
39
+ def -(value)
40
+ self.class.new *calculate(value,:-)
41
+ end
42
+
43
+ #
44
+ # Multiply this object and another object.
45
+ #
46
+ # @return a new object that is the product of the two objects.
47
+ #
48
+ def *(value)
49
+ self.class.new *calculate(value,:*)
50
+ end
51
+
52
+ #
53
+ # This generic method will perform the calculation defined by the
54
+ # operation for all the calculation requirements defined.
55
+ #
56
+ # @param [value] value this is the other value that is being added,
57
+ # subtracted, etc. to the current object.
58
+ # @param [Symbol] operation this is the mathematical operation that
59
+ # is being performed between all the calc requirements of the current
60
+ # object and other value.
61
+ #
62
+ # @return [Array] an array of reults from the calculations of all the
63
+ # requirements.
64
+ #
65
+ def calculate(value,operation)
66
+ check_calculation_requirements(value)
67
+ calculation_requirements.map do |requirement|
68
+ send(requirement).send(operation,value.send(requirement))
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -5,6 +5,7 @@ module Metro
5
5
  # Represents an object that contains both the width and height.
6
6
  #
7
7
  class Dimensions < Struct.new(:width,:height)
8
+ include CalculationValidations
8
9
 
9
10
  #
10
11
  # Create a dimensions objects with zero width and zero height.
@@ -37,30 +38,21 @@ module Metro
37
38
  end
38
39
 
39
40
  #
40
- # Add the dimensions to another dimensions-like structure. A
41
- # dimensions like structure is anything that responds to width and height.
41
+ # Compare the dimension to another dimensions-like structure.
42
42
  #
43
- # @return a new dimensions which is the sum of the two dimensions
43
+ # @return [Fixnum] -1 if the dimensions is smaller than the other dimension,
44
+ # 0 if the dimensions are exactly the same, 1 if the dimensions are bigger
45
+ # then the other dimensions.
44
46
  #
45
- def +(value)
46
- raise "Unable to add dimension to #{value} #{value.class}" if [ :width, :height ].find { |method| ! value.respond_to?(method) }
47
- self.class.of (width + value.width.to_f), (height + value.height.to_f)
47
+ def <=>(value)
48
+ check_calculation_requirements(value)
49
+ (width * height) <=> (value.width * value.height)
48
50
  end
49
51
 
50
- #
51
- # Subtract the dimensions-like structure from this dimension. A
52
- # dimensions like structure is anything that responds to width and height.
53
- #
54
- # @return a new dimensions which is the different of the two dimensions
55
- #
56
- def -(value)
57
- raise "Unable to subtract from these dimensions with #{value} #{value.class}" if [ :width, :height ].find { |method| ! value.respond_to?(method) }
58
- self.class.of (width - value.width.to_f), (height - value.height.to_f)
59
- end
52
+ private
60
53
 
61
- def <=>(value)
62
- raise "Unable to subtract from these dimensions with #{value} #{value.class}" if [ :width, :height ].find { |method| ! value.respond_to?(method) }
63
- (width * height) <=> (value.width * value.height)
54
+ def calculation_requirements
55
+ [ :width, :height ]
64
56
  end
65
57
 
66
58
  end