gemini 1.0.0 → 1.0.1

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 (52) hide show
  1. data/bin/gemini +20 -12
  2. data/package/jar/gemini.jar +0 -0
  3. data/src/base_state.rb +6 -12
  4. data/src/behaviors/animated_sprite.rb +6 -2
  5. data/src/behaviors/audible.rb +6 -1
  6. data/src/behaviors/axis_stateful.rb +2 -0
  7. data/src/behaviors/big_sprite.rb +2 -1
  8. data/src/behaviors/bounding_box_collidable.rb +2 -0
  9. data/src/behaviors/camera_anchored_drawable.rb +1 -1
  10. data/src/behaviors/cardinal_movable.rb +18 -11
  11. data/src/behaviors/clickable.rb +1 -0
  12. data/src/behaviors/countable.rb +1 -0
  13. data/src/behaviors/debug_physical.rb +25 -13
  14. data/src/behaviors/debug_tangible.rb +1 -0
  15. data/src/behaviors/drawable.rb +1 -0
  16. data/src/behaviors/drawable_shape.rb +4 -3
  17. data/src/behaviors/fading_image_trail_emittable.rb +4 -0
  18. data/src/behaviors/game_object_emittable.rb +1 -0
  19. data/src/behaviors/gravity_source.rb +3 -2
  20. data/src/behaviors/inertial.rb +3 -0
  21. data/src/behaviors/movable2d.rb +1 -0
  22. data/src/behaviors/multi_animated_sprite.rb +7 -1
  23. data/src/behaviors/physical.rb +123 -77
  24. data/src/behaviors/physical_cardinal_movable.rb +14 -13
  25. data/src/behaviors/physical_sprite.rb +25 -9
  26. data/src/behaviors/platformer_controllable.rb +9 -2
  27. data/src/behaviors/pointer.rb +2 -0
  28. data/src/behaviors/pressable.rb +4 -0
  29. data/src/behaviors/receives_events.rb +3 -0
  30. data/src/behaviors/regional.rb +5 -0
  31. data/src/behaviors/repulsive.rb +28 -0
  32. data/src/behaviors/rotates_to_point.rb +2 -1
  33. data/src/behaviors/spatial.rb +4 -4
  34. data/src/behaviors/sprite.rb +17 -6
  35. data/src/behaviors/stateful.rb +2 -0
  36. data/src/behaviors/taggable.rb +1 -0
  37. data/src/behaviors/tangible.rb +8 -0
  38. data/src/behaviors/timeable.rb +9 -0
  39. data/src/behaviors/top_down_vehicle.rb +1 -0
  40. data/src/behaviors/triangle_trail_emittable.rb +5 -0
  41. data/src/behaviors/updates.rb +1 -0
  42. data/src/behaviors/updates_at_consistant_rate.rb +1 -0
  43. data/src/behaviors/vectored_movement.rb +1 -0
  44. data/src/behaviors/world_collidable.rb +1 -0
  45. data/src/game_objects/static_sprite.rb +3 -2
  46. data/src/game_objects/triangle_trail.rb +1 -1
  47. data/src/gemini_version.rb +1 -1
  48. data/src/managers/input_support/input_mapping.rb +10 -10
  49. data/src/managers/scrolling_render_manager.rb +8 -6
  50. data/src/project_generator.rb +6 -0
  51. data/src/vector.rb +19 -7
  52. metadata +4 -3
@@ -1,6 +1,5 @@
1
- # A CardinalMovable can move north, east, south and west.
2
- # Movement along certain axis can be constrained, for example, pong has horizonal (north/east)
3
- # movement constrained
1
+ # A CardinalMovable that interacts with the physics manager.
2
+ # See the API for CardinalMovable for method descriptions.
4
3
  # TODO: Allow disabling of diaganol easily
5
4
  # TODO: Allow disabling of directions
6
5
  # TODO: Allow speed limit per axis
@@ -14,8 +13,8 @@ class PhysicalCardinalMovable < Gemini::Behavior
14
13
  SOUTH_EAST = :south_east
15
14
  SOUTH_WEST = :south_west
16
15
  NORTH_WEST = :north_west
17
- DIAGONOL_DIRECTIONS = [NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST]
18
- CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] + DIAGONOL_DIRECTIONS
16
+ DIAGONAL_DIRECTIONS = [NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST]
17
+ CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] + DIAGONAL_DIRECTIONS
19
18
  DIRECTION_TRANSLATION_IN_DEGREES = []
20
19
 
21
20
  depends_on :Physical
@@ -35,12 +34,12 @@ class PhysicalCardinalMovable < Gemini::Behavior
35
34
  def facing_direction=(direction)
36
35
  if @allowed_directions.include? direction
37
36
  if @moving && orthogonal_directions?(@facing_direction, direction)
38
- diagonol_direction = begin
37
+ diagonal_direction = begin
39
38
  "#{self.class}::#{@facing_direction.to_s.upcase}_#{direction.to_s.upcase}".constantize
40
39
  rescue
41
40
  "#{self.class}::#{direction.to_s.upcase}_#{@facing_direction.to_s.upcase}".constantize
42
41
  end
43
- @facing_direction = diagonol_direction
42
+ @facing_direction = diagonal_direction
44
43
  else
45
44
  @facing_direction = direction
46
45
  end
@@ -64,7 +63,7 @@ class PhysicalCardinalMovable < Gemini::Behavior
64
63
 
65
64
  def end_cardinal_movement(message)
66
65
  direction = message.value
67
- @facing_direction = other_direction(@facing_direction, direction) if diagonol_direction? @facing_direction
66
+ @facing_direction = other_direction(@facing_direction, direction) if diagonal_direction? @facing_direction
68
67
  if @facing_direction == direction
69
68
  @cardinal_velocity = Vector.new(0,0)
70
69
  @moving = false
@@ -72,7 +71,9 @@ class PhysicalCardinalMovable < Gemini::Behavior
72
71
  @cardinal_velocity = direction_to_polar_vector(@facing_direction)
73
72
  end
74
73
  end
75
-
74
+
75
+ private
76
+
76
77
  def direction_to_polar_vector(direction)
77
78
  angle = case direction
78
79
  when NORTH
@@ -95,12 +96,12 @@ class PhysicalCardinalMovable < Gemini::Behavior
95
96
  Vector.from_polar_vector(@cardinal_speed, angle)
96
97
  end
97
98
 
98
- def diagonol_direction?(direction)
99
- DIAGONOL_DIRECTIONS.include? direction
99
+ def diagonal_direction?(direction)
100
+ DIAGONAL_DIRECTIONS.include? direction
100
101
  end
101
102
 
102
- def other_direction(diagonol_direction, direction)
103
- diagonol_direction.to_s.sub(direction.to_s, '').sub('_', '').to_sym
103
+ def other_direction(diagonal_direction, direction)
104
+ diagonal_direction.to_s.sub(direction.to_s, '').sub('_', '').to_sym
104
105
  end
105
106
 
106
107
  def orthogonal_directions?(direction_a, direction_b)
@@ -1,3 +1,4 @@
1
+ #Gives an object a bitmap that responds appropriately to the physics engine.
1
2
  class PhysicalSprite < Gemini::Behavior
2
3
  depends_on :Sprite
3
4
  depends_on :Physical
@@ -5,15 +6,9 @@ class PhysicalSprite < Gemini::Behavior
5
6
  alias_method :set_tiled_to_bounds, :tiled_to_bounds=
6
7
 
7
8
  def load
8
- #TODO: This should call a method that does the same thing for performance
9
- @target.on_before_draw do
10
- @target.image_rotation = @target.physical_rotation unless @target.image.nil?
11
- #TODO: Only execute this if the shape is bound to the image.
12
- #TODO: Call raw_move instead of x= and y=
13
- position = @target.body_position
14
- @target.x = position.x
15
- @target.y = position.y
16
- end
9
+ @target.on_before_draw :draw_physical_sprite
10
+ @offset_position = Vector.new
11
+ @offset_rotation = 0.0
17
12
  end
18
13
 
19
14
  def bounded_image=(new_image)
@@ -26,4 +21,25 @@ class PhysicalSprite < Gemini::Behavior
26
21
  @tiled_to_bounds = state
27
22
  @target.image_size = @target.box_size
28
23
  end
24
+
25
+ def physical_sprite_position_offset=(offset)
26
+ @offset_position = offset.dup
27
+ end
28
+ alias_method :set_physical_sprite_position_offset, :physical_sprite_position_offset=
29
+
30
+ def physical_sprite_rotation_offset=(offset)
31
+ @offset_rotation = offset
32
+ end
33
+ alias_method :set_physical_sprite_rotation_offset, :physical_sprite_rotation_offset=
34
+
35
+ def draw_physical_sprite(graphics)
36
+ @target.image_rotation = @target.physical_rotation unless @target.image.nil?
37
+ #TODO: Only execute this if the shape is bound to the image.
38
+ #TODO: Call raw_move instead of x= and y=
39
+ position = @target.body_position
40
+
41
+ offset = Vector::ORIGIN.pivot_around_degrees(@offset_position, @target.physical_rotation + @offset_rotation)
42
+ @target.x = position.x + offset.x
43
+ @target.y = position.y + offset.y
44
+ end
29
45
  end
@@ -61,14 +61,18 @@ class PlatformerControllable < Gemini::Behavior
61
61
  # end
62
62
  end
63
63
 
64
+ #Refers to the cached result of detect_grounded to indicate whether the object is touching the ground.
64
65
  def grounded?
65
66
  @grounded
66
67
  end
67
68
 
69
+ #Cardinal direction the object is facing, :east or :west.
68
70
  def facing_direction
69
71
  @facing_right ? :east : :west
70
72
  end
71
-
73
+
74
+ #Move the object, setting the appropriate animation and flipping its bitmap in the appropriate direction.
75
+ #Takes a message with :left or :right as its value.
72
76
  def start_move(message)
73
77
  @moving = true
74
78
  if grounded?
@@ -89,6 +93,8 @@ class PlatformerControllable < Gemini::Behavior
89
93
  end
90
94
  end
91
95
 
96
+ #Halt the object, setting the appropriate animation and flipping its bitmap in the appropriate direction.
97
+ #Takes a message with :left or :right as its value.
92
98
  def stop_move(message)
93
99
  if grounded?
94
100
  @target.animate :stand
@@ -103,10 +109,10 @@ class PlatformerControllable < Gemini::Behavior
103
109
  end
104
110
  end
105
111
 
112
+ #Add vertical velocity to an object, but only if it's on the ground.
106
113
  def jump(message)
107
114
  detect_grounded
108
115
  if grounded?
109
- puts "jump!"
110
116
  @target.animate :jump
111
117
  # This should help us get the object unstuck if it's sunk a little into another body
112
118
  # Although, this might also get us stuck too
@@ -119,6 +125,7 @@ class PlatformerControllable < Gemini::Behavior
119
125
  end
120
126
  end
121
127
 
128
+ #Determine if the object is touching the ground.
122
129
  def detect_grounded
123
130
  @target.get_collision_events.each do |collision_event|
124
131
  # shameless rip/port from Kevin Glass's platformer example, with his comments
@@ -1,3 +1,4 @@
1
+ #Makes an object move with the mouse.
1
2
  class Pointer < Gemini::Behavior
2
3
  depends_on :Sprite
3
4
  # depends_on :Movable2d
@@ -21,6 +22,7 @@ class Pointer < Gemini::Behavior
21
22
  # end
22
23
  end
23
24
 
25
+ #Takes a message with a Vector indicating the x/y coordinates to move the object to.
24
26
  def mouse_movement(message)
25
27
  vector = message.value
26
28
  @target.move(vector.location.x, vector.location.y)
@@ -1,3 +1,4 @@
1
+ #Makes an object change appearance when clicked.
1
2
  class Pressable < Gemini::Behavior
2
3
  depends_on :Sprite
3
4
  depends_on :Clickable
@@ -6,6 +7,9 @@ class Pressable < Gemini::Behavior
6
7
  add_tag :button, :gui, :ui
7
8
  end
8
9
 
10
+ #Takes a hash with the following keys and values:
11
+ #[:normal] The path for the image to display when the object is not pressed.
12
+ #[:pressed] The path for the image to display when the object is pressed.
9
13
  def images=(image_path_hash)
10
14
  @normal_image = image_path_hash[:normal]
11
15
  @pressed_image = image_path_hash[:pressed]
@@ -1,9 +1,11 @@
1
+ #Enables an object to respond to events.
1
2
  class ReceivesEvents < Gemini::Behavior
2
3
 
3
4
  def load
4
5
  @handled_events = []
5
6
  end
6
7
 
8
+ #Add a handler for a given event type. Subsequent events of the given type will be passed to the given block or the given method name.
7
9
  def handle_event(type, method_name=nil, &block)
8
10
  if !method_name.nil?
9
11
  @target.game_state.manager(:message_queue).add_listener(type, self, @target.send(:method, method_name).to_proc)
@@ -15,6 +17,7 @@ class ReceivesEvents < Gemini::Behavior
15
17
  @handled_events << type
16
18
  end
17
19
 
20
+ #Unregisters all message handlers for this object.
18
21
  def unload
19
22
  @handled_events.each {|e| @target.game_state.manager(:message_queue).remove_listener(e, self)}
20
23
  end
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
 
3
+ #Indicates that the receiver has entered/exited an area.
3
4
  class RegionalTransitionEvent
4
5
  attr_accessor :region, :spatial
5
6
  def initialize(region, spatial)
@@ -8,6 +9,7 @@ class RegionalTransitionEvent
8
9
  end
9
10
  end
10
11
 
12
+ #Makes an object receive a RegionalTransitionEvent whenever it enters/exits a given area.
11
13
  class Regional < Gemini::Behavior
12
14
  depends_on :Spatial
13
15
  attr_accessor :dimensions, :region_shape
@@ -42,6 +44,7 @@ class Regional < Gemini::Behavior
42
44
  # end
43
45
  end
44
46
 
47
+ #Indicates whether the given point is within the region's boundaries.
45
48
  def within_region?(spatial)
46
49
  half_width = dimensions.x / 2.0
47
50
  half_height = dimensions.y / 2.0
@@ -49,6 +52,8 @@ class Regional < Gemini::Behavior
49
52
  ((@target.y - half_height) < spatial.y) && ((@target.y + half_height) > spatial.y)
50
53
  end
51
54
 
55
+ #Indicates whether the given object existed on the previous world update.
56
+ #If not, it should not be considered for collision events on this update.
52
57
  def existed_last_update?(game_object)
53
58
  @last_spatials.find {|previous_game_object| game_object == previous_game_object}
54
59
  end
@@ -0,0 +1,28 @@
1
+ #Makes an object attract other Physical game objects towards it.
2
+ class Repulsive < Gemini::Behavior
3
+ depends_on :Physical
4
+
5
+ #The force to exert. 0 means no push.
6
+ attr_accessor :push
7
+ alias_method :set_push, :push=
8
+
9
+ #There is no effect on targets that are beyond this distance.
10
+ attr_accessor :effect_radius
11
+ alias_method :set_effect_radius, :effect_radius=
12
+
13
+ def load
14
+ @push = 0
15
+ @effect_radius = 0
16
+ @target.on_update do |delta|
17
+ physicals = @target.game_state.manager(:game_object).game_objects.select {|game_object| game_object.kind_of? Physical}.compact
18
+ physicals.each do |physical|
19
+ next if @target == physical
20
+ distance = @target.position.distance_from physical
21
+ next if distance > @effect_radius
22
+ force = delta * @push / (distance * Gemini::Math::SQUARE_ROOT_OF_TWO)
23
+ repulsion = Vector.from_polar_vector(force, physical.position.angle_from(@target))
24
+ physical.add_force repulsion
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,4 +1,4 @@
1
- # This behavior rotates to face the tracking point or game object
1
+ # This behavior rotates to face the tracking point or game object.
2
2
  class RotatesToPoint < Gemini::Behavior
3
3
  #TODO: Add offset rotation
4
4
  depends_on :Physical
@@ -11,6 +11,7 @@ class RotatesToPoint < Gemini::Behavior
11
11
  end
12
12
  end
13
13
 
14
+ #The angle that the object must turn to face the rotation_target.
14
15
  def rotation_to_face_target
15
16
  #TODO: Use vector
16
17
  diff_angle = Gemini::Math.radians_to_degrees Math.atan2(@target.y - @rotation_target.y, @target.x - @rotation_target.x)
@@ -1,3 +1,4 @@
1
+ #Gives an object x/y coordinates.
1
2
  class Spatial < Gemini::Behavior
2
3
  attr_accessor :position
3
4
  alias_method :set_position, :position=
@@ -23,16 +24,15 @@ class Spatial < Gemini::Behavior
23
24
  @position.y = y
24
25
  end
25
26
 
27
+ #Takes a Vector or x/y coordinates to move to.
26
28
  def move(x_or_other, y=nil)
27
29
  if y.nil?
28
- if x_or_other.kind_of? Vector
29
- @position = Vector.new(x_or_other.x, x_or_other.y)
30
30
  #TODO: Determine if Spatial should really suppor this behavior.
31
31
  # use case: Pipe a mouse move event directly to move
32
- elsif x_or_other.kind_of? Gemini::Message
32
+ if x_or_other.kind_of? Gemini::Message
33
33
  @position = Vector.new(x_or_other.value.x, x_or_other.value.y)
34
34
  else
35
- raise "move does not support #{x_or_other.class}"
35
+ @position = Vector.new(x_or_other.x, x_or_other.y)
36
36
  end
37
37
  else
38
38
  @position = Vector.new(x_or_other, y)
@@ -1,8 +1,6 @@
1
1
  require 'behaviors/drawable'
2
2
 
3
- # WARNING: Using Slick's image for rotation can cause some odd quirks with it
4
- # not quite rotating correctly (especially noticable around 180 degress).
5
- # @rotation stands alone for this reason, instead of using Slick's rotation
3
+ #Makes an object draw itself as a bitmap image.
6
4
  class Sprite < Drawable
7
5
  include_class 'org.newdawn.slick.Image'
8
6
  depends_on :Spatial
@@ -15,7 +13,8 @@ class Sprite < Drawable
15
13
  @texture_coords = [Vector.new(0.0, 0.0), Vector.new(1.0, 1.0)]
16
14
  @rotation = 0.0
17
15
  end
18
-
16
+
17
+ #Assign an Image to the sprite. Takes an Image or the name of a file in the data directory.
19
18
  def image=(sprite_name)
20
19
  if sprite_name.kind_of? Image
21
20
  @image = sprite_name
@@ -25,18 +24,21 @@ class Sprite < Drawable
25
24
  set_image_size(Vector.new(@image.width, @image.height))
26
25
  end
27
26
  alias_method :set_image, :image=
28
-
27
+
28
+ #Assign a Color to the sprite.
29
29
  def color=(color)
30
30
  @color = color
31
31
  end
32
32
  alias_method :set_color, :color=
33
-
33
+
34
+ #Increase or decrease horizontal and vertical scale for the image. 1.0 is identical to the current scale. If y_scale is omitted, x_scale is used for both axes.
34
35
  #TODO: Take vectors for first args as well
35
36
  def image_scaling(x_scale, y_scale = nil)
36
37
  y_scale = x_scale if y_scale.nil?
37
38
  set_image @image.get_scaled_copy(x_scale.to_f * image_size.x, y_scale.to_f * image_size.y)
38
39
  end
39
40
 
41
+ #Set horizontal and vertical scale for the image relative to the original. 1.0 is original scale. If y_scale is omitted, x_scale is used for both axes.
40
42
  #TODO: Take vectors for first args as well
41
43
  def scale_image_from_original(x_scale, y_scale = nil)
42
44
  y_scale = x_scale if y_scale.nil?
@@ -44,6 +46,9 @@ class Sprite < Drawable
44
46
  set_image @original_image.get_scaled_copy(x_scale.to_f * @original_image.width, y_scale.to_f * @original_image.height)
45
47
  end
46
48
 
49
+ # WARNING: Using Slick's image for rotation can cause some odd quirks with it
50
+ # not quite rotating correctly (especially noticable around 180 degress).
51
+ # @rotation stands alone for this reason, instead of using Slick's rotation
47
52
  def image_rotation
48
53
  @rotation
49
54
  end
@@ -55,23 +60,28 @@ class Sprite < Drawable
55
60
  end
56
61
  alias_method :set_image_rotation, :image_rotation=
57
62
 
63
+ #Increment the image rotation.
58
64
  def add_rotation(rotation)
59
65
  @rotation += rotation
60
66
  end
61
67
 
68
+ #Flip the texture horizontally.
62
69
  def flip_horizontally
63
70
  @texture_coords[1].x, @texture_coords[0].x = @texture_coords[0].x, @texture_coords[1].x
64
71
  end
65
72
 
73
+ #Flip the texture vertically.
66
74
  def flip_vertically
67
75
  @texture_coords[1].y, @texture_coords[0].y = @texture_coords[0].y, @texture_coords[1].y
68
76
  end
69
77
 
78
+ #Returns a Vector with the x/y coordinates of the sprite Image's top left corner.
70
79
  def top_left_position
71
80
  #Vector.new(center_position.x - image_size.x / 2.0, center_position.y - image_size.y / 2.0)
72
81
  Vector.new(@target.x - image_size.x / 2.0, @target.y - image_size.y / 2.0)
73
82
  end
74
83
 
84
+ #Takes either a single Vector or the x/y coordinates to move the sprite Image's top left corner to.
75
85
  def move_by_top_left(move_x_or_vector, move_y = nil)
76
86
  half_width = image_size.x / 2.0
77
87
  half_height = image_size.y / 2.0
@@ -82,6 +92,7 @@ class Sprite < Drawable
82
92
  end
83
93
  end
84
94
 
95
+ #Draw the sprite to the given graphic context.
85
96
  def draw(graphics)
86
97
  return if @image.nil? || @image_size.nil?
87
98
  half_width = image_size.x / 2.0
@@ -1,3 +1,5 @@
1
+
2
+ # Makes an object transition between states.
1
3
  # Notes from Logan:
2
4
  # I started on this behavior, and realized what I needed was multiple axises of states for my behavior
3
5
  # that I wanted to use Stateful in. This behavior is simple, and seems like it would be useful for some cases.
@@ -1,3 +1,4 @@
1
+ #Allows an object to be categorized.
1
2
  class Taggable < Gemini::Behavior
2
3
 
3
4
  def load
@@ -1,3 +1,4 @@
1
+ #A shape used to determine if one object collides with another.
1
2
  class TangibleBox
2
3
  attr_accessor :size
3
4
  def get_points(top_left_position, rotation)
@@ -9,6 +10,7 @@ class TangibleBox
9
10
  end
10
11
  end
11
12
 
13
+ #Allows an object to indicate when it collides with another.
12
14
  class Tangible < Gemini::Behavior
13
15
  depends_on :Spatial
14
16
  attr_reader :tangible_shape
@@ -26,6 +28,11 @@ class Tangible < Gemini::Behavior
26
28
  end
27
29
  alias_method :set_tangible_debug_mode, :tangible_debug_mode=
28
30
 
31
+ #Set the shape of the object as seen by collision calculations.
32
+ #call-seq:
33
+ #set_shape(:Box, vector)
34
+ #set_shape(:Box, width, height)
35
+ #
29
36
  def set_tangible_shape(name, *args)
30
37
  @tangible_shape = case name
31
38
  when :Box
@@ -39,6 +46,7 @@ class Tangible < Gemini::Behavior
39
46
  end
40
47
  end
41
48
 
49
+ #Indicates whether this object collides with the given object.
42
50
  def tangibly_collides_with?(other_tangible)
43
51
  #TODO: top_left isn't on spatial...
44
52
  other_shape = other_tangible.tangible_shape