jemini 2009.10.27
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +9 -0
- data/bin/jemini +26 -0
- data/lib/ibxm.jar +0 -0
- data/lib/jinput.jar +0 -0
- data/lib/jogg-0.0.7.jar +0 -0
- data/lib/jorbis-0.0.15.jar +0 -0
- data/lib/jruby-complete.jar +0 -0
- data/lib/lwjgl.jar +0 -0
- data/lib/lwjgl_util_applet.jar +0 -0
- data/lib/native_files/OpenAL32.dll +0 -0
- data/lib/native_files/jinput-dx8.dll +0 -0
- data/lib/native_files/jinput-dx8_64.dll +0 -0
- data/lib/native_files/jinput-raw.dll +0 -0
- data/lib/native_files/jinput-raw_64.dll +0 -0
- data/lib/native_files/jinput-wintab.dll +0 -0
- data/lib/native_files/libjinput-linux.so +0 -0
- data/lib/native_files/libjinput-linux64.so +0 -0
- data/lib/native_files/libjinput-osx.jnilib +0 -0
- data/lib/native_files/liblwjgl.jnilib +0 -0
- data/lib/native_files/liblwjgl.so +0 -0
- data/lib/native_files/liblwjgl64.so +0 -0
- data/lib/native_files/libopenal.so +0 -0
- data/lib/native_files/lwjgl.dll +0 -0
- data/lib/native_files/openal.dylib +0 -0
- data/lib/natives-linux.jar +0 -0
- data/lib/natives-mac.jar +0 -0
- data/lib/natives-win32.jar +0 -0
- data/lib/phys2d.jar +0 -0
- data/lib/slick.jar +0 -0
- data/package/jar/jemini.jar +0 -0
- data/src/behavior.rb +248 -0
- data/src/behavior_event.rb +23 -0
- data/src/behaviors/animated_image.rb +88 -0
- data/src/behaviors/audible.rb +16 -0
- data/src/behaviors/axis_stateful.rb +35 -0
- data/src/behaviors/bounding_box_collidable.rb +27 -0
- data/src/behaviors/cardinal_movable.rb +121 -0
- data/src/behaviors/clickable.rb +19 -0
- data/src/behaviors/countable.rb +32 -0
- data/src/behaviors/debug_physical.rb +43 -0
- data/src/behaviors/debug_tangible.rb +31 -0
- data/src/behaviors/drawable.rb +7 -0
- data/src/behaviors/drawable_image.rb +111 -0
- data/src/behaviors/drawable_line.rb +32 -0
- data/src/behaviors/drawable_shape.rb +48 -0
- data/src/behaviors/fading_image_trail_emittable.rb +32 -0
- data/src/behaviors/game_object_emittable.rb +13 -0
- data/src/behaviors/grid_bound.rb +108 -0
- data/src/behaviors/handles_events.rb +33 -0
- data/src/behaviors/inertial.rb +14 -0
- data/src/behaviors/magnetic.rb +34 -0
- data/src/behaviors/metered.rb +3 -0
- data/src/behaviors/movable.rb +81 -0
- data/src/behaviors/particle_emitter.rb +27 -0
- data/src/behaviors/physical.rb +384 -0
- data/src/behaviors/physical_cardinal_movable.rb +111 -0
- data/src/behaviors/physical_image.rb +45 -0
- data/src/behaviors/pointer.rb +30 -0
- data/src/behaviors/pressable.rb +17 -0
- data/src/behaviors/regional.rb +76 -0
- data/src/behaviors/rotates_to_point.rb +19 -0
- data/src/behaviors/spatial.rb +43 -0
- data/src/behaviors/stateful.rb +33 -0
- data/src/behaviors/taggable.rb +28 -0
- data/src/behaviors/tangible.rb +59 -0
- data/src/behaviors/timeable.rb +88 -0
- data/src/behaviors/top_down_vehicle.rb +42 -0
- data/src/behaviors/triangle_trail_emittable.rb +46 -0
- data/src/behaviors/unique.rb +0 -0
- data/src/behaviors/updates.rb +8 -0
- data/src/behaviors/updates_at_consistant_rate.rb +28 -0
- data/src/behaviors/vectored_movement.rb +48 -0
- data/src/behaviors/world_collidable.rb +9 -0
- data/src/color.rb +70 -0
- data/src/events/grid_changed_event.rb +8 -0
- data/src/events/physical_message.rb +9 -0
- data/src/events/tangible_collision_event.rb +8 -0
- data/src/file_system.rb +17 -0
- data/src/game.rb +110 -0
- data/src/game_object.rb +176 -0
- data/src/game_objects/background.rb +10 -0
- data/src/game_objects/fading_image.rb +23 -0
- data/src/game_objects/tangible_object.rb +4 -0
- data/src/game_objects/text.rb +71 -0
- data/src/game_objects/triangle_trail.rb +85 -0
- data/src/game_state.rb +164 -0
- data/src/inflector.rb +68 -0
- data/src/input_helpers/joystick_dead_zone_filter.rb +9 -0
- data/src/jemini.rb +31 -0
- data/src/jemini_version.rb +4 -0
- data/src/listenable_mixin.rb +15 -0
- data/src/logger_mixin.rb +11 -0
- data/src/managers/basic_game_object_manager.rb +95 -0
- data/src/managers/basic_physics_manager.rb +95 -0
- data/src/managers/basic_render_manager.rb +49 -0
- data/src/managers/basic_tile_manager.rb +3 -0
- data/src/managers/basic_update_manager.rb +30 -0
- data/src/managers/diagnostic/diagnostic_input_manager.rb +0 -0
- data/src/managers/input_manager.rb +161 -0
- data/src/managers/input_support/input_binding.rb +77 -0
- data/src/managers/input_support/input_builder.rb +44 -0
- data/src/managers/input_support/input_listener.rb +74 -0
- data/src/managers/input_support/input_message.rb +5 -0
- data/src/managers/input_support/joystick_listener.rb +53 -0
- data/src/managers/input_support/key_listener.rb +27 -0
- data/src/managers/input_support/mouse_listener.rb +38 -0
- data/src/managers/input_support/slick_input_listener.rb +20 -0
- data/src/managers/input_support/slick_input_message.rb +11 -0
- data/src/managers/input_support/slick_input_translator.rb +15 -0
- data/src/managers/message_queue.rb +60 -0
- data/src/managers/network_manager.rb +41 -0
- data/src/managers/render_support/hardware_cursor.rb +13 -0
- data/src/managers/resource_manager.rb +167 -0
- data/src/managers/sound_manager.rb +47 -0
- data/src/managers/tag_manager.rb +47 -0
- data/src/managers/tangible_manager.rb +36 -0
- data/src/math.rb +23 -0
- data/src/org/rubyforge/rawr/Main.java +67 -0
- data/src/platform.rb +28 -0
- data/src/proc_enhancement.rb +5 -0
- data/src/project_generator.rb +138 -0
- data/src/resource.rb +31 -0
- data/src/spline.rb +13 -0
- data/src/states/input_diagnostic_state.rb +53 -0
- data/src/vector.rb +143 -0
- data/test/test_state.rb +3 -0
- metadata +188 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# A CardinalMovable that interacts with the physics manager.
|
2
|
+
# See the API for CardinalMovable for method descriptions.
|
3
|
+
# TODO: Allow disabling of diaganol easily
|
4
|
+
# TODO: Allow disabling of directions
|
5
|
+
# TODO: Allow speed limit per axis
|
6
|
+
# TODO: Allow speed limit per direction
|
7
|
+
class PhysicalCardinalMovable < Jemini::Behavior
|
8
|
+
NORTH = :north
|
9
|
+
EAST = :east
|
10
|
+
SOUTH = :south
|
11
|
+
WEST = :west
|
12
|
+
NORTH_EAST = :north_east
|
13
|
+
SOUTH_EAST = :south_east
|
14
|
+
SOUTH_WEST = :south_west
|
15
|
+
NORTH_WEST = :north_west
|
16
|
+
DIAGONAL_DIRECTIONS = [NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST]
|
17
|
+
CARDINAL_DIRECTIONS = [NORTH, EAST, SOUTH, WEST] + DIAGONAL_DIRECTIONS
|
18
|
+
DIRECTION_TRANSLATION_IN_DEGREES = []
|
19
|
+
|
20
|
+
depends_on :Physical
|
21
|
+
depends_on :HandlesEvents
|
22
|
+
attr_accessor :facing_direction
|
23
|
+
wrap_with_callbacks :facing_direction=, :set_facing_drection
|
24
|
+
|
25
|
+
def load
|
26
|
+
@facing_direction = NORTH
|
27
|
+
@allowed_directions = CARDINAL_DIRECTIONS.dup
|
28
|
+
@cardinal_speed = 25
|
29
|
+
@game_object.on_update do
|
30
|
+
@game_object.add_velocity @cardinal_velocity if @moving
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def facing_direction=(direction)
|
35
|
+
if @allowed_directions.include? direction
|
36
|
+
if @moving && orthogonal_directions?(@facing_direction, direction)
|
37
|
+
diagonal_direction = begin
|
38
|
+
"#{self.class}::#{@facing_direction.to_s.upcase}_#{direction.to_s.upcase}".constantize
|
39
|
+
rescue
|
40
|
+
"#{self.class}::#{direction.to_s.upcase}_#{@facing_direction.to_s.upcase}".constantize
|
41
|
+
end
|
42
|
+
@facing_direction = diagonal_direction
|
43
|
+
else
|
44
|
+
@facing_direction = direction
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias_method :set_facing_direction, :facing_direction=
|
49
|
+
|
50
|
+
def constrain_direction(directions)
|
51
|
+
directions = [directions] unless directions.kind_of? Array
|
52
|
+
directions.each
|
53
|
+
directions.each { |direction| @allowed_directions.delete direction }
|
54
|
+
end
|
55
|
+
|
56
|
+
def begin_cardinal_movement(message)
|
57
|
+
direction = message.value
|
58
|
+
return unless @allowed_directions.include? direction
|
59
|
+
set_facing_direction direction
|
60
|
+
@moving = true
|
61
|
+
@cardinal_velocity = direction_to_polar_vector(@facing_direction)
|
62
|
+
end
|
63
|
+
|
64
|
+
def end_cardinal_movement(message)
|
65
|
+
direction = message.value
|
66
|
+
@facing_direction = other_direction(@facing_direction, direction) if diagonal_direction? @facing_direction
|
67
|
+
if @facing_direction == direction
|
68
|
+
@cardinal_velocity = Vector.new(0,0)
|
69
|
+
@moving = false
|
70
|
+
else
|
71
|
+
@cardinal_velocity = direction_to_polar_vector(@facing_direction)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def direction_to_polar_vector(direction)
|
78
|
+
angle = case direction
|
79
|
+
when NORTH
|
80
|
+
0
|
81
|
+
when NORTH_EAST
|
82
|
+
45
|
83
|
+
when EAST
|
84
|
+
90
|
85
|
+
when SOUTH_EAST
|
86
|
+
135
|
87
|
+
when SOUTH
|
88
|
+
180
|
89
|
+
when SOUTH_WEST
|
90
|
+
225
|
91
|
+
when WEST
|
92
|
+
270
|
93
|
+
when NORTH_WEST
|
94
|
+
315
|
95
|
+
end
|
96
|
+
Vector.from_polar_vector(@cardinal_speed, angle)
|
97
|
+
end
|
98
|
+
|
99
|
+
def diagonal_direction?(direction)
|
100
|
+
DIAGONAL_DIRECTIONS.include? direction
|
101
|
+
end
|
102
|
+
|
103
|
+
def other_direction(diagonal_direction, direction)
|
104
|
+
diagonal_direction.to_s.sub(direction.to_s, '').sub('_', '').to_sym
|
105
|
+
end
|
106
|
+
|
107
|
+
def orthogonal_directions?(direction_a, direction_b)
|
108
|
+
([NORTH, SOUTH].include?(direction_a) && [EAST, WEST].include?(direction_b)) ||
|
109
|
+
([NORTH, SOUTH].include?(direction_b) && [EAST, WEST].include?(direction_a))
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#Gives an object a bitmap that responds appropriately to the physics engine.
|
2
|
+
class PhysicalImage < Jemini::Behavior
|
3
|
+
depends_on :DrawableImage
|
4
|
+
depends_on :Physical
|
5
|
+
attr_accessor :tiled_to_bounds
|
6
|
+
alias_method :set_tiled_to_bounds, :tiled_to_bounds=
|
7
|
+
|
8
|
+
def load
|
9
|
+
@game_object.on_before_draw :draw_physical_image
|
10
|
+
@offset_position = Vector.new
|
11
|
+
@offset_rotation = 0.0
|
12
|
+
end
|
13
|
+
|
14
|
+
def bounded_image=(new_image)
|
15
|
+
@game_object.set_image new_image
|
16
|
+
@game_object.set_shape(:Box, @game_object.image_size.x, @game_object.image_size.y)
|
17
|
+
end
|
18
|
+
alias_method :set_bounded_image, :bounded_image=
|
19
|
+
|
20
|
+
def tiled_to_bounds=(state)
|
21
|
+
@tiled_to_bounds = state
|
22
|
+
@game_object.image_size = @game_object.box_size
|
23
|
+
end
|
24
|
+
|
25
|
+
def physical_image_position_offset=(offset)
|
26
|
+
@offset_position = offset.dup
|
27
|
+
end
|
28
|
+
alias_method :set_physical_image_position_offset, :physical_image_position_offset=
|
29
|
+
|
30
|
+
def physical_image_rotation_offset=(offset)
|
31
|
+
@offset_rotation = offset
|
32
|
+
end
|
33
|
+
alias_method :set_physical_image_rotation_offset, :physical_image_rotation_offset=
|
34
|
+
|
35
|
+
def draw_physical_image(graphics)
|
36
|
+
@game_object.image_rotation = @game_object.physical_rotation unless @game_object.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 = @game_object.body_position
|
40
|
+
|
41
|
+
offset = Vector::ORIGIN.pivot_around_degrees(@offset_position, @game_object.physical_rotation + @offset_rotation)
|
42
|
+
@game_object.x = position.x + offset.x
|
43
|
+
@game_object.y = position.y + offset.y
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#Makes an object move with the mouse.
|
2
|
+
class Pointer < Jemini::Behavior
|
3
|
+
depends_on :DrawableImage
|
4
|
+
# depends_on :Movable2d
|
5
|
+
# depends_on :CollisionPoolAlgorithmTaggable
|
6
|
+
# depends_on :WorldCollidable
|
7
|
+
depends_on :HandlesEvents
|
8
|
+
|
9
|
+
|
10
|
+
def load
|
11
|
+
# add_tag :ui, :gui, :pointer
|
12
|
+
# collides_with_tags :gui, :clickable
|
13
|
+
#
|
14
|
+
@game_object.handle_event :mouse_move do |message|
|
15
|
+
mouse_movement(message)
|
16
|
+
end
|
17
|
+
# handle_event :start_click, :start_click
|
18
|
+
# handle_event :stop_click, :stop_click
|
19
|
+
|
20
|
+
# listen_for(:collided) do |event, continue|
|
21
|
+
# event.collided_object.click if event.collided_object.respond_to? :click
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
|
25
|
+
#Takes a message with a Vector indicating the x/y coordinates to move the object to.
|
26
|
+
def mouse_movement(message)
|
27
|
+
vector = message.value
|
28
|
+
@game_object.move(vector.location.x, vector.location.y)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#Makes an object change appearance when clicked.
|
2
|
+
class Pressable < Jemini::Behavior
|
3
|
+
depends_on :DrawableImage
|
4
|
+
depends_on :Clickable
|
5
|
+
|
6
|
+
def load
|
7
|
+
add_tag :button, :gui, :ui
|
8
|
+
end
|
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.
|
13
|
+
def images=(image_path_hash)
|
14
|
+
@normal_image = image_path_hash[:normal]
|
15
|
+
@pressed_image = image_path_hash[:pressed]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
#Indicates that the receiver has entered/exited an area.
|
4
|
+
class RegionalTransitionEvent
|
5
|
+
attr_accessor :region, :spatial
|
6
|
+
def initialize(region, spatial)
|
7
|
+
@region = region
|
8
|
+
@spatial = spatial
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
#Makes an object receive a RegionalTransitionEvent whenever it enters/exits a given area.
|
13
|
+
class Regional < Jemini::Behavior
|
14
|
+
depends_on :Spatial
|
15
|
+
attr_accessor :dimensions, :region_shape
|
16
|
+
alias_method :set_dimensions, :dimensions=
|
17
|
+
alias_method :set_region_shape, :region_shape=
|
18
|
+
|
19
|
+
# This is bad. We need a real collision system
|
20
|
+
def load
|
21
|
+
@game_object.enable_listeners_for :entered_region, :exited_region
|
22
|
+
@game_object.move(0,0)
|
23
|
+
@dimensions = Vector.new(1,1)
|
24
|
+
@last_spatials_within = []
|
25
|
+
@last_spatials_without = nil
|
26
|
+
@last_spatials = []
|
27
|
+
#TODO: SAVE: This should be turned on when requested.
|
28
|
+
# game_state.manager(:update).on_update do
|
29
|
+
# spatials = game_state.manager(:game_object).game_objects.select {|game_object| game_object.kind_of? Tangible}.compact
|
30
|
+
#
|
31
|
+
# spatials_within, spatials_without = spatials.partition {|spatial| within_region?(spatial)}
|
32
|
+
# (spatials_within - @last_spatials_within).each do |spatial_within|
|
33
|
+
# @game_object.notify :entered_region, RegionalTransitionEvent.new(self, spatial_within) if existed_last_update? spatial_within
|
34
|
+
# end
|
35
|
+
# @last_spatials_within = spatials_within
|
36
|
+
#
|
37
|
+
# unless @last_spatials_without.nil?
|
38
|
+
# (spatials_without - @last_spatials_without).each do |spatial_without|
|
39
|
+
# @game_object.notify :exited_region, RegionalTransitionEvent.new(self, spatial_without) if existed_last_update? spatial_without
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# @last_spatials_without = spatials_without
|
43
|
+
# @last_spatials = spatials
|
44
|
+
# end
|
45
|
+
end
|
46
|
+
|
47
|
+
#Indicates whether the given point is within the region's boundaries.
|
48
|
+
def within_region?(spatial)
|
49
|
+
half_width = dimensions.x / 2.0
|
50
|
+
half_height = dimensions.y / 2.0
|
51
|
+
((@game_object.x - half_width) < spatial.x) && ((@game_object.x + half_width) > spatial.x) &&
|
52
|
+
((@game_object.y - half_height) < spatial.y) && ((@game_object.y + half_height) > spatial.y)
|
53
|
+
end
|
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.
|
57
|
+
def existed_last_update?(game_object)
|
58
|
+
@last_spatials.find {|previous_game_object| game_object == previous_game_object}
|
59
|
+
end
|
60
|
+
|
61
|
+
def toggle_debug_mode
|
62
|
+
@debug_mode = !@debug_mode
|
63
|
+
if @debug_mode
|
64
|
+
game_state.manager(:render).on_before_render do |graphics|
|
65
|
+
old_color = graphics.color
|
66
|
+
graphics.color = Color.new(0.0, 1.0, 0.0, 0.3).native_color
|
67
|
+
half_width = dimensions.x / 2.0
|
68
|
+
half_height = dimensions.y / 2.0
|
69
|
+
graphics.fill_rect(@game_object.x - half_width, @game_object.y - half_height, dimensions.x, dimensions.y)
|
70
|
+
graphics.color = old_color
|
71
|
+
end
|
72
|
+
else
|
73
|
+
game_state.manager(:render).remove_before_draw self
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This behavior rotates to face the tracking point or game object.
|
2
|
+
class RotatesToPoint < Jemini::Behavior
|
3
|
+
#TODO: Add offset rotation
|
4
|
+
depends_on :Physical
|
5
|
+
attr_accessor :rotation_target
|
6
|
+
alias_method :set_rotation_target, :rotation_target=
|
7
|
+
|
8
|
+
def load
|
9
|
+
@game_object.on_update do
|
10
|
+
@game_object.set_rotation rotation_to_face_target if @rotation_target
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
#The angle that the object must turn to face the rotation_target.
|
15
|
+
def rotation_to_face_target
|
16
|
+
#TODO: Use vector
|
17
|
+
diff_angle = Jemini::Math.radians_to_degrees Math.atan2(@game_object.y - @rotation_target.y, @game_object.x - @rotation_target.x)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#Gives an object x/y coordinates.
|
2
|
+
class Spatial < Jemini::Behavior
|
3
|
+
wrap_with_callbacks :position=
|
4
|
+
attr_accessor :position
|
5
|
+
|
6
|
+
alias_method :set_position, :position=
|
7
|
+
|
8
|
+
|
9
|
+
def load
|
10
|
+
@position = Vector.new(0,0)
|
11
|
+
end
|
12
|
+
|
13
|
+
def x
|
14
|
+
@position.x
|
15
|
+
end
|
16
|
+
|
17
|
+
def y
|
18
|
+
@position.y
|
19
|
+
end
|
20
|
+
|
21
|
+
def x=(x)
|
22
|
+
@position.x = x
|
23
|
+
end
|
24
|
+
|
25
|
+
def y=(y)
|
26
|
+
@position.y = y
|
27
|
+
end
|
28
|
+
|
29
|
+
#Takes a Vector or x/y coordinates to move to.
|
30
|
+
# def move(x_or_other, y=nil)
|
31
|
+
# if y.nil?
|
32
|
+
# #TODO: Determine if Spatial should really suppor this behavior.
|
33
|
+
# # use case: Pipe a mouse move event directly to move
|
34
|
+
# if x_or_other.kind_of? Jemini::Message
|
35
|
+
# @position = Vector.new(x_or_other.value.x, x_or_other.value.y)
|
36
|
+
# else
|
37
|
+
# @position = Vector.new(x_or_other.x, x_or_other.y)
|
38
|
+
# end
|
39
|
+
# else
|
40
|
+
# @position = Vector.new(x_or_other, y)
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
# Makes an object transition between states.
|
3
|
+
# Notes from Logan:
|
4
|
+
# I started on this behavior, and realized what I needed was multiple axises of states for my behavior
|
5
|
+
# that I wanted to use Stateful in. This behavior is simple, and seems like it would be useful for some cases.
|
6
|
+
# Thus, the behavior remains, but it is not tested or used at the time of this comment.
|
7
|
+
class Stateful < Jemini::Behavior
|
8
|
+
class StateTransferEvent
|
9
|
+
attr_accessor :before_state, :after_state
|
10
|
+
def initialize(before_state, after_state)
|
11
|
+
@before_state = before_state
|
12
|
+
@after_state = after_state
|
13
|
+
end
|
14
|
+
end
|
15
|
+
attr_accessor :default_state, :current_state
|
16
|
+
wrap_with_callbacks :transfer_state
|
17
|
+
|
18
|
+
def load
|
19
|
+
@state_transitions = {}
|
20
|
+
@game_object.enable_listeners_for :state_transfer_accepted
|
21
|
+
@game_object.enable_listeners_for :state_transfer_rejected
|
22
|
+
end
|
23
|
+
|
24
|
+
def transfer_state(state)
|
25
|
+
event = StateTransferEvent.new(@current_state, state)
|
26
|
+
if @state_transitions[@current_state].include? state
|
27
|
+
@current_state = state
|
28
|
+
notify :state_transfer_accepted, event
|
29
|
+
else
|
30
|
+
notify :state_transfer_rejected, event
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#Allows an object to be categorized.
|
2
|
+
class Taggable < Jemini::Behavior
|
3
|
+
|
4
|
+
def load
|
5
|
+
@tags = []
|
6
|
+
@game_object.enable_listeners_for :tag_added, :tag_removed
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_tag(*tags)
|
10
|
+
new_tags = tags - @tags
|
11
|
+
@tags.concat new_tags
|
12
|
+
new_tags.each { |tag| @game_object.notify :tag_added, tag }
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_tag(*tags)
|
16
|
+
tags_to_remove = @tags & tags
|
17
|
+
@tags -= tags_to_remove
|
18
|
+
tags_to_remove.each {|tag| @game_object.notify :tag_removed, tag}
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_tag?(*tags)
|
22
|
+
tags.any? {|tag| @tags.member?(tag) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def tags
|
26
|
+
@tags.clone
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#A shape used to determine if one object collides with another.
|
2
|
+
class TangibleBox
|
3
|
+
attr_accessor :size
|
4
|
+
def get_points(top_left_position, rotation)
|
5
|
+
top_left = top_left_position
|
6
|
+
top_right = Vector.new(top_left_position.x + size.x, top_left_position.y)
|
7
|
+
bottom_left = Vector.new(top_left_position.x, top_left_position.y + size.y)
|
8
|
+
bottom_right = Vector.new(top_left_position.x + size.x, top_left_position.y + size.y)
|
9
|
+
[top_left, top_right, bottom_right, bottom_left]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#Allows an object to indicate when it collides with another.
|
14
|
+
class Tangible < Jemini::Behavior
|
15
|
+
depends_on :Spatial
|
16
|
+
attr_reader :tangible_shape
|
17
|
+
|
18
|
+
def load
|
19
|
+
@game_object.enable_listeners_for :tangible_collision
|
20
|
+
end
|
21
|
+
|
22
|
+
def tangible_debug_mode=(mode)
|
23
|
+
if mode
|
24
|
+
@game_object.add_behavior :DebugTangible
|
25
|
+
else
|
26
|
+
@game_object.remove_behavior :DebugTangible
|
27
|
+
end
|
28
|
+
end
|
29
|
+
alias_method :set_tangible_debug_mode, :tangible_debug_mode=
|
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
|
+
#
|
36
|
+
def set_tangible_shape(name, *args)
|
37
|
+
@tangible_shape = case name
|
38
|
+
when :Box
|
39
|
+
box = TangibleBox.new
|
40
|
+
if args.size == 1
|
41
|
+
box.size = args[0]
|
42
|
+
else
|
43
|
+
box.size = Vector.new(args[0], args[1])
|
44
|
+
end
|
45
|
+
box
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#Indicates whether this object collides with the given object.
|
50
|
+
def tangibly_collides_with?(other_tangible)
|
51
|
+
#TODO: top_left isn't on spatial...
|
52
|
+
other_shape = other_tangible.tangible_shape
|
53
|
+
|
54
|
+
((@game_object.x <= other_tangible.x && (@game_object.x + @tangible_shape.size.x) >= other_tangible.x) ||
|
55
|
+
( @game_object.x >= other_tangible.x && @game_object.x <= (other_tangible.x + other_shape.size.x))) &&
|
56
|
+
((@game_object.y <= other_tangible.y && (@game_object.y + @tangible_shape.size.y) >= other_tangible.y) ||
|
57
|
+
( @game_object.y >= other_tangible.y && @game_object.y <= (other_tangible.y + other_shape.size.y)))
|
58
|
+
end
|
59
|
+
end
|