jemini 2009.10.27

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 (127) hide show
  1. data/README.txt +9 -0
  2. data/bin/jemini +26 -0
  3. data/lib/ibxm.jar +0 -0
  4. data/lib/jinput.jar +0 -0
  5. data/lib/jogg-0.0.7.jar +0 -0
  6. data/lib/jorbis-0.0.15.jar +0 -0
  7. data/lib/jruby-complete.jar +0 -0
  8. data/lib/lwjgl.jar +0 -0
  9. data/lib/lwjgl_util_applet.jar +0 -0
  10. data/lib/native_files/OpenAL32.dll +0 -0
  11. data/lib/native_files/jinput-dx8.dll +0 -0
  12. data/lib/native_files/jinput-dx8_64.dll +0 -0
  13. data/lib/native_files/jinput-raw.dll +0 -0
  14. data/lib/native_files/jinput-raw_64.dll +0 -0
  15. data/lib/native_files/jinput-wintab.dll +0 -0
  16. data/lib/native_files/libjinput-linux.so +0 -0
  17. data/lib/native_files/libjinput-linux64.so +0 -0
  18. data/lib/native_files/libjinput-osx.jnilib +0 -0
  19. data/lib/native_files/liblwjgl.jnilib +0 -0
  20. data/lib/native_files/liblwjgl.so +0 -0
  21. data/lib/native_files/liblwjgl64.so +0 -0
  22. data/lib/native_files/libopenal.so +0 -0
  23. data/lib/native_files/lwjgl.dll +0 -0
  24. data/lib/native_files/openal.dylib +0 -0
  25. data/lib/natives-linux.jar +0 -0
  26. data/lib/natives-mac.jar +0 -0
  27. data/lib/natives-win32.jar +0 -0
  28. data/lib/phys2d.jar +0 -0
  29. data/lib/slick.jar +0 -0
  30. data/package/jar/jemini.jar +0 -0
  31. data/src/behavior.rb +248 -0
  32. data/src/behavior_event.rb +23 -0
  33. data/src/behaviors/animated_image.rb +88 -0
  34. data/src/behaviors/audible.rb +16 -0
  35. data/src/behaviors/axis_stateful.rb +35 -0
  36. data/src/behaviors/bounding_box_collidable.rb +27 -0
  37. data/src/behaviors/cardinal_movable.rb +121 -0
  38. data/src/behaviors/clickable.rb +19 -0
  39. data/src/behaviors/countable.rb +32 -0
  40. data/src/behaviors/debug_physical.rb +43 -0
  41. data/src/behaviors/debug_tangible.rb +31 -0
  42. data/src/behaviors/drawable.rb +7 -0
  43. data/src/behaviors/drawable_image.rb +111 -0
  44. data/src/behaviors/drawable_line.rb +32 -0
  45. data/src/behaviors/drawable_shape.rb +48 -0
  46. data/src/behaviors/fading_image_trail_emittable.rb +32 -0
  47. data/src/behaviors/game_object_emittable.rb +13 -0
  48. data/src/behaviors/grid_bound.rb +108 -0
  49. data/src/behaviors/handles_events.rb +33 -0
  50. data/src/behaviors/inertial.rb +14 -0
  51. data/src/behaviors/magnetic.rb +34 -0
  52. data/src/behaviors/metered.rb +3 -0
  53. data/src/behaviors/movable.rb +81 -0
  54. data/src/behaviors/particle_emitter.rb +27 -0
  55. data/src/behaviors/physical.rb +384 -0
  56. data/src/behaviors/physical_cardinal_movable.rb +111 -0
  57. data/src/behaviors/physical_image.rb +45 -0
  58. data/src/behaviors/pointer.rb +30 -0
  59. data/src/behaviors/pressable.rb +17 -0
  60. data/src/behaviors/regional.rb +76 -0
  61. data/src/behaviors/rotates_to_point.rb +19 -0
  62. data/src/behaviors/spatial.rb +43 -0
  63. data/src/behaviors/stateful.rb +33 -0
  64. data/src/behaviors/taggable.rb +28 -0
  65. data/src/behaviors/tangible.rb +59 -0
  66. data/src/behaviors/timeable.rb +88 -0
  67. data/src/behaviors/top_down_vehicle.rb +42 -0
  68. data/src/behaviors/triangle_trail_emittable.rb +46 -0
  69. data/src/behaviors/unique.rb +0 -0
  70. data/src/behaviors/updates.rb +8 -0
  71. data/src/behaviors/updates_at_consistant_rate.rb +28 -0
  72. data/src/behaviors/vectored_movement.rb +48 -0
  73. data/src/behaviors/world_collidable.rb +9 -0
  74. data/src/color.rb +70 -0
  75. data/src/events/grid_changed_event.rb +8 -0
  76. data/src/events/physical_message.rb +9 -0
  77. data/src/events/tangible_collision_event.rb +8 -0
  78. data/src/file_system.rb +17 -0
  79. data/src/game.rb +110 -0
  80. data/src/game_object.rb +176 -0
  81. data/src/game_objects/background.rb +10 -0
  82. data/src/game_objects/fading_image.rb +23 -0
  83. data/src/game_objects/tangible_object.rb +4 -0
  84. data/src/game_objects/text.rb +71 -0
  85. data/src/game_objects/triangle_trail.rb +85 -0
  86. data/src/game_state.rb +164 -0
  87. data/src/inflector.rb +68 -0
  88. data/src/input_helpers/joystick_dead_zone_filter.rb +9 -0
  89. data/src/jemini.rb +31 -0
  90. data/src/jemini_version.rb +4 -0
  91. data/src/listenable_mixin.rb +15 -0
  92. data/src/logger_mixin.rb +11 -0
  93. data/src/managers/basic_game_object_manager.rb +95 -0
  94. data/src/managers/basic_physics_manager.rb +95 -0
  95. data/src/managers/basic_render_manager.rb +49 -0
  96. data/src/managers/basic_tile_manager.rb +3 -0
  97. data/src/managers/basic_update_manager.rb +30 -0
  98. data/src/managers/diagnostic/diagnostic_input_manager.rb +0 -0
  99. data/src/managers/input_manager.rb +161 -0
  100. data/src/managers/input_support/input_binding.rb +77 -0
  101. data/src/managers/input_support/input_builder.rb +44 -0
  102. data/src/managers/input_support/input_listener.rb +74 -0
  103. data/src/managers/input_support/input_message.rb +5 -0
  104. data/src/managers/input_support/joystick_listener.rb +53 -0
  105. data/src/managers/input_support/key_listener.rb +27 -0
  106. data/src/managers/input_support/mouse_listener.rb +38 -0
  107. data/src/managers/input_support/slick_input_listener.rb +20 -0
  108. data/src/managers/input_support/slick_input_message.rb +11 -0
  109. data/src/managers/input_support/slick_input_translator.rb +15 -0
  110. data/src/managers/message_queue.rb +60 -0
  111. data/src/managers/network_manager.rb +41 -0
  112. data/src/managers/render_support/hardware_cursor.rb +13 -0
  113. data/src/managers/resource_manager.rb +167 -0
  114. data/src/managers/sound_manager.rb +47 -0
  115. data/src/managers/tag_manager.rb +47 -0
  116. data/src/managers/tangible_manager.rb +36 -0
  117. data/src/math.rb +23 -0
  118. data/src/org/rubyforge/rawr/Main.java +67 -0
  119. data/src/platform.rb +28 -0
  120. data/src/proc_enhancement.rb +5 -0
  121. data/src/project_generator.rb +138 -0
  122. data/src/resource.rb +31 -0
  123. data/src/spline.rb +13 -0
  124. data/src/states/input_diagnostic_state.rb +53 -0
  125. data/src/vector.rb +143 -0
  126. data/test/test_state.rb +3 -0
  127. metadata +188 -0
@@ -0,0 +1,32 @@
1
+ # draws a line on the screen
2
+ # TODO: Enable color
3
+ class DrawableLine < Jemini::Behavior
4
+ java_import 'org.newdawn.slick.geom.Line'
5
+ depends_on :Spatial
6
+ wrap_with_callbacks :draw
7
+
8
+ attr_reader :line_end_position
9
+ attr_accessor :color
10
+
11
+ def load
12
+ @line_end_position = Vector.new(0.0, 0.0)
13
+ @line = Line.new @game_object.position.to_slick_vector, @line_end_position.to_slick_vector
14
+ @game_object.on_after_position_changes { set_line }
15
+ #@color = Color.white
16
+ end
17
+
18
+ def line_end_position=(end_point)
19
+ @line_end_position = end_point
20
+ set_line
21
+ @line_end_position
22
+ end
23
+
24
+ def draw(graphics)
25
+ graphics.draw @line
26
+ end
27
+
28
+ private
29
+ def set_line
30
+ @line.set @game_object.position.to_slick_vector, @line_end_position.to_slick_vector
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ require 'behaviors/drawable'
2
+
3
+ #Makes an object draw itself on the screen as a polygon.
4
+ class DrawableShape < Jemini::Behavior
5
+ java_import 'org.newdawn.slick.geom.Vector2f'
6
+ java_import 'org.newdawn.slick.geom.Polygon'
7
+ java_import 'org.newdawn.slick.geom.Circle'
8
+
9
+ depends_on :Spatial
10
+ wrap_with_callbacks :draw
11
+
12
+ attr_accessor :color, :image
13
+ attr_reader :visual_shape
14
+
15
+
16
+ #Set the shape to draw.
17
+ #Accepts :Polygon or the name of a class in the DrawableShape namespace.
18
+ #TODO: There are no DrawableShape::* classes yet!
19
+ def set_visual_shape(shape, *params)
20
+ case shape
21
+ when :polygon, :Polygon
22
+ @visual_shape = "#{self.class}::#{shape}".constantize.new
23
+ params.each do |vector|
24
+ @visual_shape.add_point vector.x, vector.y
25
+ end
26
+ when :circle, :Circle
27
+ @visual_shape = DrawableShape::Circle.new(position.x, position.y, params)
28
+ else
29
+ @visual_shape = ("DrawableShape::"+ shape.to_s).constantize.new(params)
30
+ end
31
+ end
32
+
33
+ #Takes a reference to an image loaded via the resource manager, and sets the bitmap.
34
+ def image=(reference)
35
+ @image = game_state.manager(:resource).get_image(reference)
36
+ end
37
+ alias_method :set_image, :image=
38
+
39
+ def draw(graphics)
40
+ if @visual_shape.kind_of? Polygon
41
+ #TODO: Tweak these values!!!!
42
+ #@image.width.to_f / game_state.screen_width.to_f, @image.height.to_f / game_state.screen_height.to_f
43
+ graphics.texture @visual_shape, @image, 1.0 / @image.width.to_f, 1.0 / @image.height.to_f
44
+ else
45
+ raise "#{@visual_shape.class} is not supported"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ #Makes an object leave a trail of images behind it that fade over time.
2
+ class FadingImageTrailEmittable < Jemini::Behavior
3
+ depends_on :Updates
4
+
5
+ def load
6
+ @fading_image_offset = Vector.new(0,0)
7
+ @move_threshold = 4
8
+ @move_count = 0
9
+ @seconds_to_fade_away = 2
10
+ @game_object.on_update do
11
+ @move_count += 1
12
+ if @move_count >= @move_threshold
13
+ fading_image = game_state.create :FadingImage, @image, Color.new(:white), @seconds_to_fade_away
14
+ #center_position = @game_object.center_position
15
+ fading_image.position = Vector.new(@fading_image_offset.x + @game_object.x, @fading_image_offset.y + @game_object.y)
16
+ @move_count = 0
17
+ end
18
+ end
19
+ end
20
+
21
+ #Sets the Image to emit.
22
+ def emit_fading_image(image)
23
+ @image = image
24
+ end
25
+
26
+ #Causes fading images to appear at some distance away from object.
27
+ #Takes a Vector with the x/y offset at which to emit the images.
28
+ def emit_fading_image_trail_from_offset(offset)
29
+ @fading_image_offset = offset
30
+ end
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ #Makes an object create other objects.
2
+ class GameObjectEmittable < Jemini::Behavior
3
+ attr_accessor :emitting_game_object_name
4
+ alias_method :set_emitting_game_object_name, :emitting_game_object_name=
5
+ def load
6
+ @game_object.enable_listeners_for :emit_game_object
7
+ end
8
+
9
+ def emit_game_object(message=nil)
10
+ game_object = game_state.create_game_object @emitting_game_object_name
11
+ @game_object.notify :emit_game_object, game_object
12
+ end
13
+ end
@@ -0,0 +1,108 @@
1
+ require 'events/grid_changed_event'
2
+
3
+ class GridBound < Jemini::Behavior
4
+ DEFAULT_GRID_WIDTH = 32
5
+ DEFAULT_GRID_HEIGHT = 32
6
+ DEFAULT_GRID_SIZE = Vector.new(DEFAULT_GRID_WIDTH, DEFAULT_GRID_HEIGHT)
7
+ depends_on :Spatial
8
+ depends_on :Movable
9
+
10
+ listen_for :grid_changed
11
+
12
+ attr_accessor :grid_size, :grid_position
13
+
14
+ def load
15
+ @grid_size = DEFAULT_GRID_SIZE
16
+ @game_object.on_movement { notify_grid_changed }
17
+ @grid_position = detect_grid_position
18
+ end
19
+
20
+ def grid_size=(vector)
21
+ @grid_size = Vector.new(vector.x.to_i, vector.y.to_i)
22
+ end
23
+
24
+ def grid_position
25
+ detect_grid_position
26
+ end
27
+ # position is a vector
28
+ # setting grid position calculates at the center of the grid
29
+ def grid_position=(grid_position)
30
+ @game_object.position = position_at(grid_position)
31
+ @grid_position = grid_position
32
+ end
33
+
34
+ def adjacent_grid(direction)
35
+ adjacent_grid = @grid_position.dup
36
+ case direction
37
+ when :top, :north, :up
38
+ adjacent_grid.y += 1
39
+ when :north_east
40
+ adjacent_grid.x += 1
41
+ adjacent_grid.y += 1
42
+ when :right, :east
43
+ adjacent_grid.x += 1
44
+ when :south_east
45
+ adjacent_grid.x += 1
46
+ adjacent_grid.y -= 1
47
+ when :bottom, :south, :down
48
+ adjacent_grid.y -= 1
49
+ when :south_west
50
+ adjacent_grid.x -= 1
51
+ adjacent_grid.y -= 1
52
+ when :left, :west
53
+ adjacent_grid.x -= 1
54
+ when :north_west
55
+ adjacent_grid.x -= 1
56
+ adjacent_grid.y += 1
57
+ end
58
+ adjacent_grid
59
+ end
60
+
61
+ def move_to_adjacent_grid(direction)
62
+ move_to_grid(adjacent_grid(direction))
63
+ end
64
+
65
+ def move_to_grid(grid)
66
+ @game_object.move_to position_at(grid)
67
+ end
68
+
69
+ def snap_to_grid
70
+ grids = adjacent_grids
71
+ grids << grid_position
72
+ nearest_grid = grids.min_by { |g| position_at(g).distance_from(@game_object.position) }
73
+ self.grid_position = nearest_grid
74
+ end
75
+
76
+ def adjacent_grids
77
+ [:north, :north_east, :east, :south_east, :south, :south_west, :west, :north_west].map do |direction|
78
+ adjacent_grid direction
79
+ end
80
+ end
81
+
82
+ private
83
+ def position_at(grid_position)
84
+ top_left_of_grid = Vector.new(grid_position.x.to_i * grid_size.x.to_i, grid_position.y.to_i * grid_size.y.to_i)
85
+ center_offset = grid_size.half
86
+ top_left_of_grid + center_offset
87
+ end
88
+
89
+ def grid_at(position)
90
+ x = position.x.to_i / grid_size.x.to_i
91
+ y = position.y.to_i / grid_size.y.to_i
92
+ Vector.new(x, y)
93
+ end
94
+
95
+ def notify_grid_changed
96
+ old_position = @grid_position
97
+ new_position = detect_grid_position
98
+
99
+ unless old_position == new_position
100
+ @grid_position = new_position
101
+ @game_object.notify(:grid_changed, GridChangedEvent.new(new_position, old_position))
102
+ end
103
+ end
104
+
105
+ def detect_grid_position
106
+ grid_at @game_object.position
107
+ end
108
+ end
@@ -0,0 +1,33 @@
1
+ #Enables an object to respond to events.
2
+ class HandlesEvents < Jemini::Behavior
3
+
4
+ def load
5
+ @handled_events = []
6
+ @event_types = []
7
+ end
8
+
9
+ #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.
10
+ def handle_event(event_name, method_name=nil, &block)
11
+ if !method_name.nil?
12
+ game_state.manager(:message_queue).add_listener(event_name, self, @game_object.send(:method, method_name).to_proc)
13
+ elsif block_given?
14
+ game_state.manager(:message_queue).add_listener(event_name, self, &block)
15
+ else
16
+ raise "Either a method name or a block must be provided"
17
+ end
18
+ @handled_events << event_name
19
+ end
20
+
21
+ def handles_events_for(event_type)
22
+ @event_types << event_type
23
+ end
24
+
25
+ def handles_events_for?(event_type)
26
+ @event_types.include? event_type
27
+ end
28
+
29
+ #Unregisters all message handlers for this object.
30
+ def unload
31
+ @handled_events.each {|e| game_state.manager(:message_queue).remove_listener(e, self)}
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ # deprecated
2
+ class Inertial < Jemini::Behavior
3
+ depends_on :UpdatesAtConsistantRate
4
+
5
+ #A 2-element array with x/y inertial values. 0 means no resistance to acceleration.
6
+ attr_accessor :inertia
7
+
8
+ def load
9
+ @inertia = [0,0]
10
+ @game_object.on_update do
11
+ move(@inertia[0] + x, @inertia[1] + y)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ #Makes an object attract other Physical game objects towards it.
2
+ class Magnetic < Jemini::Behavior
3
+ depends_on :Physical
4
+
5
+ #The force to exert. 0 means no push.
6
+ attr_accessor :magnetism
7
+ alias_method :set_magnetism, :magnetism=
8
+
9
+ #There is no effect on targets that are beyond this distance.
10
+ attr_accessor :magnetism_max_radius
11
+ alias_method :set_magnetism_max_radius, :magnetism_max_radius=
12
+
13
+ #The distance where force progression is cut off. Distances closer than this will be treated as if it were the distance provided
14
+ attr_accessor :magnetism_min_radius
15
+ alias_method :set_magnetism_min_radius, :magnetism_min_radius=
16
+
17
+ def load
18
+ @magnetism = 1.0
19
+ @magnetism_max_radius = 1000.0
20
+ @magnetism_min_radius = 10.0
21
+ @game_object.on_update do |delta|
22
+ physicals = game_state.manager(:game_object).game_objects.select {|game_object| game_object.has_behavior? :Physical}
23
+ physicals.each do |physical|
24
+ next if @game_object == physical
25
+ distance = @game_object.body_position.distance_from physical
26
+ next if distance > @magnetism_max_radius
27
+ distance = @magnetism_min_radius if distance < @magnetism_min_radius
28
+ force = delta * @magnetism / (distance * Jemini::Math::SQUARE_ROOT_OF_TWO)
29
+ magnetism = Vector.from_polar_vector(force, @game_object.body_position.angle_from(physical.body_position))
30
+ physical.add_force magnetism
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ class Metered < Jemini::Behavior
2
+
3
+ end
@@ -0,0 +1,81 @@
1
+ # Enables simple movement
2
+ # This movement is not coupled to any physics behavior
3
+ # TODO: Consider adding #move_with(spline) method
4
+ class Movable < Jemini::Behavior
5
+ DEFAULT_DESTINATION_ACCURACY = 0.1
6
+
7
+ depends_on :Spatial
8
+ depends_on :Updates
9
+ wrap_with_callbacks :move, :moveable_speed=
10
+ listen_for :movement, :movable_arrival
11
+
12
+ attr_accessor :movable_speed, :movable_destination_accuracy
13
+ alias_method :set_movable_speed, :movable_speed
14
+
15
+ def load
16
+ @movable_speed = 0.01
17
+ @movable_destination_accuracy = DEFAULT_DESTINATION_ACCURACY
18
+ @game_object.on_update {|delta| update_movement delta }
19
+ end
20
+
21
+ # move at a direction indefinitely
22
+ def move(angle_or_polar, speed=nil)
23
+ @destination = nil
24
+ if angle_or_polar.kind_of? Numeric
25
+ self.movable_speed = speed unless speed.nil? # force the callbacks
26
+ @movement = Vector.from_polar_vector(movable_speed, angle_or_polar)
27
+ else
28
+ angle = angle_or_polar.angle_from(@game_object.position)
29
+ @movement = Vector.from_polar_vector(movable_speed, angle)
30
+ self.movable_speed = @movement.magnitude
31
+ warn "Vector given in first arg means speed of #{speed} is ignored." if speed
32
+ end
33
+ end
34
+
35
+ # move to a destination
36
+ def move_to(desired_location, speed=nil)
37
+ self.movable_speed = speed if speed
38
+ move desired_location.angle_from(@game_object.position), movable_speed
39
+ @destination = desired_location
40
+ end
41
+
42
+ def movement_direction
43
+ return nil if @movement.nil?
44
+ @movement.angle_from(Vector.new(0.0, 0.0))
45
+ end
46
+
47
+ private
48
+ def update_movement(delta)
49
+ return unless @movement
50
+ new_position = @game_object.position + Vector.new(@movement.x * delta, @movement.y * delta)
51
+
52
+ if @destination
53
+ move_to_destination new_position
54
+ else
55
+ move_normally_with new_position
56
+ end
57
+ end
58
+
59
+ def move_to_destination(new_position)
60
+ new_position = @destination if overshot_destination?(new_position)
61
+ move_normally_with new_position # may seem like a possible no-op from above, but will kick off callbacks for Movable and Spatial
62
+
63
+ if @game_object.position.near?(@destination, @movable_destination_accuracy)
64
+ @destination = nil
65
+ @movement = nil
66
+ @game_object.notify :movable_arrival
67
+ end
68
+ end
69
+
70
+ def move_normally_with(new_position)
71
+ @game_object.position = new_position
72
+ @game_object.notify :movement
73
+ end
74
+
75
+ def overshot_destination?(new_position)
76
+ previous_distance = @game_object.position.distance_from(@destination).abs
77
+ new_distance = new_position.distance_from(@destination).abs
78
+
79
+ new_distance > previous_distance
80
+ end
81
+ end
@@ -0,0 +1,27 @@
1
+ class ParticleEmitter < Jemini::Behavior
2
+ has_behavior :Updates
3
+
4
+ java_import 'org.newdawn.slick.particles.ConfigurableEmitter'
5
+ java_import 'org.newdawn.slick.particles.ParticleSystem'
6
+
7
+ def load
8
+ on_update {|delta| update_particle(delta)}
9
+ end
10
+
11
+ def load_particle_data(resource)
12
+ @system = ParticleSystem.new(resource)
13
+
14
+ end
15
+
16
+ def draw(graphics)
17
+ # use ConfigurableEmitter's angularOffset public field to apply rotation
18
+ # this may be a poor system considering it may not respect the openGL graphics/transform stack
19
+
20
+ end
21
+
22
+ private
23
+ def update_particle(delta)
24
+ return if @system.nil?
25
+
26
+ end
27
+ end
@@ -0,0 +1,384 @@
1
+ require 'behaviors/spatial'
2
+ require 'events/physical_message'
3
+
4
+ #Makes an object interact with the physics engine.
5
+ class Physical < Jemini::Behavior
6
+
7
+ INFINITE_MASS = Java::net::phys2d::raw::Body::INFINITE_MASS
8
+
9
+ include_class "net.phys2d.raw.Body"
10
+ include_class "net.phys2d.raw.shapes.Box"
11
+ include_class "net.phys2d.raw.shapes.Circle"
12
+ include_class "net.phys2d.raw.shapes.Line"
13
+ include_class "net.phys2d.raw.shapes.Polygon"
14
+ include_class "net.phys2d.raw.shapes.ConvexPolygon"
15
+ include_class "net.phys2d.math.Vector2f"
16
+ include_class 'net.phys2d.raw.AngleJoint'
17
+ include_class 'net.phys2d.raw.BasicJoint'
18
+ include_class 'net.phys2d.raw.SpringJoint'
19
+ include_class 'net.phys2d.raw.FixedAngleJoint'
20
+ include_class 'net.phys2d.raw.DistanceJoint'
21
+
22
+ attr_reader :mass, :name, :shape
23
+ depends_on :Spatial
24
+ depends_on :Updates
25
+ wrap_with_callbacks :mass=, :add_to_world, :body_position=, :set_body_position
26
+
27
+ def load
28
+ @mass = 1
29
+ @shape = Box.new(1,1)
30
+ setup_body
31
+ @body.restitution = 0.0
32
+ @body.user_data = @game_object
33
+ @angular_damping = 0.0
34
+ @game_object.enable_listeners_for :physical_collided
35
+ @game_object.on_after_position_changes { move_body @game_object.position }
36
+ # Manual angular damping. New Phys2D may support this? If not, file a bug.
37
+ @game_object.on_update do |delta|
38
+ next if @angular_damping.zero? || angular_velocity.zero?
39
+ decay = (delta * @angular_damping * angular_velocity) / (mass * shape.surface_factor)
40
+ set_angular_velocity(angular_velocity - decay) unless decay.nan?
41
+ end
42
+ end
43
+
44
+ def add_excluded_physical(physical_game_object)
45
+ @body.add_excluded_body physical_game_object.instance_variable_get(:@__behaviors)[:Physical].instance_variable_get(:@body)
46
+ end
47
+
48
+ # prevent collision and interaction with all physicals now and in the future
49
+ def exclude_all_physicals
50
+ game_state.manager(:game_object).game_objects.select {|game_object| game_object.has_behavior? :Physical }.each {|physical| add_excluded_physical physical }
51
+
52
+ game_state.manager(:game_object).on_before_add_game_object do |game_object, event|
53
+ add_excluded_physical game_object if game_object.has_behavior? :Physical
54
+ end
55
+ end
56
+
57
+ def physics_bitmask
58
+ @body.bitmask
59
+ end
60
+
61
+ def physics_bitmask=(bitmask)
62
+ @body.bitmask = bitmask
63
+ end
64
+ alias_method :set_physics_bitmask, :physics_bitmask=
65
+
66
+
67
+ def body_position
68
+ @body.position.to_vector
69
+ end
70
+
71
+ #Takes a Vector with the x/y coordinates to move the object to.
72
+ def body_position=(vector)
73
+ # set_position doesn't take a vector, only x/y
74
+ @body.set_position(vector.x, vector.y)
75
+ end
76
+ alias_method :set_body_position, :body_position=
77
+
78
+ # See about Phys2D joints here: http://www.cokeandcode.com/phys2d/source/javadoc/net/phys2d/raw/Joint.html
79
+ # TODO: Make the joint a game object and/or behavior
80
+ def join_to_physical(physical_game_object, options={})
81
+ other_body = physical_game_object.instance_variable_get(:@__behaviors)[:Physical].instance_variable_get(:@body)
82
+
83
+ self_body_point = (options[:self_body_point] || Vector::ORIGIN).to_phys2d_vector
84
+ other_body_point = (options[:other_body_point] || Vector::ORIGIN).to_phys2d_vector
85
+ joint = case options[:joint]
86
+ when :distance
87
+ DistanceJoint.new(@body, other_body, self_body_point, other_body_point, options[:distance])
88
+ when :fixed_angle
89
+ FixedAngleJoint.new(@body, other_body, self_body_point, other_body_point, options[:angle])
90
+ when :angle
91
+ AngleJoint.new(@body, other_body, self_body_point, other_body_point, options[:self_body_angle], options[:other_body_angle])
92
+ when :basic
93
+ joint = BasicJoint.new(@body, other_body, options[:anchor].to_phys2d_vector)
94
+ joint.relaxation = options[:relaxation] if options[:relaxation]
95
+ joint
96
+ when :spring
97
+ joint = SpringJoint.new(@body, other_body, options[:self_anchor].to_phys2d_vector, options[:other_anchor].to_phys2d_vector)
98
+ joint
99
+ else
100
+ raise "Joint type #{options[:joint].inspect} not supported."
101
+ end
102
+ @world.add joint
103
+ end
104
+
105
+ #The maximum speed the object is allowed to travel. Takes either a Vector with the x/y limits or the numeric value to assign to both x and y.
106
+ def speed_limit=(vector_or_shared_value)
107
+ if vector_or_shared_value.kind_of? Numeric
108
+ axis_limit_x = axis_limit_y = vector_or_shared_value / Jemini::Math::SQUARE_ROOT_OF_TWO
109
+ else
110
+ axis_limit_x = vector_or_shared_value.x
111
+ axis_limit_y = vector_or_shared_value.y
112
+ end
113
+ @body.set_max_velocity(axis_limit_x, axis_limit_y)
114
+ end
115
+ alias_method :set_speed_limit, :speed_limit=
116
+
117
+ def width
118
+ @body.shape.bounds.width
119
+ end
120
+
121
+ def height
122
+ @body.shape.bounds.height
123
+ end
124
+
125
+ def radius
126
+ @body.shape.radius
127
+ end
128
+
129
+ def box_size
130
+ @body.shape.size
131
+ end
132
+
133
+ #Set the absolute rotation.
134
+ def physical_rotation=(degrees)
135
+ @body.rotation = Jemini::Math.degrees_to_radians(degrees)
136
+ end
137
+ alias_method :set_physical_rotation, :physical_rotation=
138
+
139
+ #Set the rotation relative to the current rotation.
140
+ def rotate_physical(degrees)
141
+ @body.adjust_rotation Jemini::Math.degrees_to_radians(degrees)
142
+ end
143
+
144
+ def physical_rotation
145
+ Jemini::Math.radians_to_degrees(@body.rotation)
146
+ end
147
+
148
+ def physical_rotatable?
149
+ @body.rotatable?
150
+ end
151
+
152
+ #Set whether the object is allowed to rotate.
153
+ def physical_rotatable=(rotatable)
154
+ @body.rotatable = rotatable
155
+ end
156
+ alias_method :set_physical_rotatable, :physical_rotatable=
157
+
158
+ #Push on the object.
159
+ #Takes either a Vector or x/y values representing the force to apply.
160
+ def add_force(x_or_vector, y = nil)
161
+ if y.nil?
162
+ @body.add_force(x_or_vector.to_phys2d_vector)
163
+ else
164
+ @body.add_force(Vector2f.new(x_or_vector, y))
165
+ end
166
+ end
167
+
168
+ #Set the force being applied to the object. Disregards all other forces.
169
+ def set_force(x, y)
170
+ @body.set_force(x, y)
171
+ end
172
+
173
+ def force
174
+ @body.force
175
+ end
176
+
177
+ #Adjust the object's velocity.
178
+ #Takes either a Vector or x/y values representing the adjustment to make.
179
+ def add_velocity(x_or_vector, y = nil)
180
+ if x_or_vector.kind_of? Vector
181
+ @body.adjust_velocity(x_or_vector.to_phys2d_vector)
182
+ else
183
+ @body.adjust_velocity(Java::net::phys2d::math::Vector2f.new(x_or_vector, y))
184
+ end
185
+ end
186
+
187
+ def velocity
188
+ @body.velocity.to_vector
189
+ end
190
+
191
+ def velocity=(vector)
192
+ @body.adjust_velocity(Java::net::phys2d::math::Vector2f.new(vector.x - @body.velocity.x, vector.y - @body.velocity.y))
193
+ end
194
+ alias_method :set_velocity, :velocity=
195
+
196
+ def angular_velocity
197
+ @body.angular_velocity
198
+ end
199
+
200
+ def angular_velocity=(delta)
201
+ @body.adjust_angular_velocity(delta - angular_velocity)
202
+ end
203
+ alias_method :set_angular_velocity, :angular_velocity=
204
+
205
+ def apply_angular_velocity(velocity)
206
+ @body.adjust_angular_velocity velocity
207
+ end
208
+
209
+ #Immediately halt movement.
210
+ def come_to_rest
211
+ current_velocity = @body.velocity
212
+ add_velocity(-current_velocity.x, -current_velocity.y)
213
+ set_angular_velocity(0)
214
+ @body.is_resting = true
215
+ end
216
+
217
+ #Takes the following values:
218
+ #[:infinite] Object has infinite mass.
219
+ #[number] Value to set the mass to.
220
+ def mass=(mass)
221
+ @mass = mass
222
+ @mass = @mass.kind_of?(Symbol) ? INFINITE_MASS : @mass
223
+ @body.set(@shape, @mass)
224
+ # TODO: Consider moving to set_shape
225
+ # A body's position is lost when it moves, reset the position to where it was
226
+ @body.move @game_object.position.x, @game_object.position.y
227
+ end
228
+ alias_method :set_mass, :mass=
229
+
230
+ def restitution
231
+ @body.restitution
232
+ end
233
+
234
+ def restitution=(restitution)
235
+ @body.restitution = restitution
236
+ end
237
+ alias_method :set_restitution, :restitution=
238
+
239
+ #Set the shape of the object as seen by the physics engine.
240
+ #call-seq:
241
+ #set_shape(:Box, width, height)
242
+ #set_shape(:Circle, radius)
243
+ #set_shape(:Polygon, *vectors)
244
+ #
245
+ def set_shape(shape, *params)
246
+ # Save off data that is destroyed when @body.set is called
247
+ saved_damping = damping
248
+ saved_angular_damping = angular_damping
249
+ saved_body_position = body_position
250
+ saved_friction = friction
251
+ saved_position = @game_object.position
252
+ saved_x, saved_y = @game_object.x, @game_object.y
253
+ if shape.respond_to?(:to_str) || shape.kind_of?(Symbol)
254
+ case shape.to_s
255
+ when 'Polygon', 'ConvexPolygon'
256
+ params = [params.map {|vector| vector.to_phys2d_vector }.to_java(Vector2f)]
257
+ end
258
+ @shape = ("Physical::" + shape.to_s).constantize.new(*params)
259
+ else
260
+ @shape = shape
261
+ end
262
+
263
+ @body.set(@shape, @mass)
264
+ set_body_position saved_body_position
265
+ @game_object.set_position saved_position
266
+ set_friction saved_friction
267
+ @game_object.position = Vector.new(saved_x, saved_y)
268
+ self.damping = saved_damping
269
+ self.angular_damping = saved_angular_damping
270
+ end
271
+
272
+ def name=(name)
273
+ @name = name
274
+ setup_body
275
+ end
276
+
277
+ #Place this object under the control of the physics engine.
278
+ def add_to_world(world)
279
+ world.add @body
280
+ @world = world
281
+ end
282
+
283
+ #Remove this object from the control of the physics engine.
284
+ def remove_from_world(world)
285
+ world.remove @body
286
+ @world = nil
287
+ end
288
+
289
+ #Turn debug mode on or off for this object.
290
+ def physical_debug_mode=(flag)
291
+ if flag
292
+ @game_object.add_behavior :DebugPhysical
293
+ else
294
+ @game_object.remove_behavior :DebugPhysical
295
+ end
296
+ end
297
+ alias_method :set_physical_debug_mode, :physical_debug_mode=
298
+
299
+ def movable=(flag)
300
+ @body.moveable = flag
301
+ end
302
+ alias_method :set_movable, :movable=
303
+
304
+ def movable?
305
+ @body.moveable?
306
+ end
307
+
308
+ #The amount of air friction slowing the object's movement.
309
+ def damping
310
+ @body.damping
311
+ end
312
+
313
+ def damping=(damping)
314
+ @body.damping = damping
315
+ end
316
+ alias_method :set_damping, :damping=
317
+
318
+ def angular_damping
319
+ @angular_damping
320
+ #@body.rot_damping
321
+ end
322
+
323
+ def angular_damping=(damping)
324
+ @angular_damping = damping
325
+ #@body.rot_damping = damping
326
+ end
327
+ alias_method :set_angular_damping, :angular_damping=
328
+
329
+ #Set this object as immobile.
330
+ def set_static_body
331
+ come_to_rest
332
+ @body.moveable = false
333
+ @body.rotatable = false
334
+ end
335
+
336
+ #Set whether gravity affects this object.
337
+ def gravity_effected=(flag)
338
+ @body.gravity_effected = flag
339
+ end
340
+ alias_method :set_gravity_effected, :gravity_effected=
341
+
342
+ #The amount of friction slowing the object's movement.
343
+ def friction
344
+ @body.friction
345
+ end
346
+
347
+ def friction=(friction)
348
+ @body.friction = friction
349
+ end
350
+ alias_method :set_friction, :friction=
351
+
352
+ # def get_colliding_game_objects(tangible_game_object)
353
+ # # TODO: Tangibles only?
354
+ # tangible_game_object
355
+ # end
356
+
357
+ #Get a list of CollisionEvents for objects currently colliding with this one.
358
+ def get_collision_events
359
+ return [] if @world.nil? # a worthwhile check in some cases. Maybe because the physical is removed but the game object is not?
360
+ @world.get_contacts(@body).map do |event|
361
+ body_method = event.body_a == @body ? :body_b : :body_a
362
+ PhysicsMessage.new(event, event.send(body_method).user_data)
363
+ end
364
+ end
365
+
366
+ private
367
+ def setup_body
368
+ if @name
369
+ @body = Body.new(@name, @shape, @mass)
370
+ else
371
+ @body = Body.new(@shape, @mass)
372
+ end
373
+
374
+ x = @body.position.x
375
+ y = @body.position.y
376
+
377
+ @body.set(@shape, @mass)
378
+ @body.move(x, y)
379
+ end
380
+
381
+ def move_body(vector)
382
+ @body.move(vector.x, vector.y)
383
+ end
384
+ end