lotu 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/VERSION +1 -1
  2. data/examples/{hello_world → 01_hello_world}/moving_portraits.rb +5 -3
  3. data/examples/{screen_cursor → 02_screen_cursor}/mouse_and_keyboard_cursors.rb +6 -26
  4. data/examples/03_collisions/box.rb +83 -0
  5. data/examples/{steering_behaviors → 04_steering_behaviors}/pursuit_and_evade.rb +4 -23
  6. data/examples/{steering_behaviors → 04_steering_behaviors}/pursuit_and_evade_multiple.rb +4 -23
  7. data/lib/lotu/actor.rb +21 -13
  8. data/lib/lotu/behavior.rb +19 -0
  9. data/lib/lotu/behaviors/collidable.rb +28 -7
  10. data/lib/lotu/behaviors/controllable.rb +1 -1
  11. data/lib/lotu/behaviors/eventful.rb +2 -5
  12. data/lib/lotu/behaviors/resource_manager.rb +104 -0
  13. data/lib/lotu/behaviors/system_user.rb +41 -3
  14. data/lib/lotu/game.rb +27 -116
  15. data/lib/lotu/helpers/kernel.rb +8 -0
  16. data/lib/lotu/{misc → helpers}/string.rb +0 -0
  17. data/lib/lotu/{misc → helpers}/vector2d.rb +1 -1
  18. data/lib/lotu/systems/animation_system.rb +3 -3
  19. data/lib/lotu/{system.rb → systems/base_system.rb} +3 -2
  20. data/lib/lotu/systems/collision_system.rb +1 -1
  21. data/lib/lotu/systems/{input_system.rb → input_manager_system.rb} +4 -4
  22. data/lib/lotu/systems/interpolation_system.rb +1 -1
  23. data/lib/lotu/systems/stalker_system.rb +1 -1
  24. data/lib/lotu/systems/steering_system.rb +2 -2
  25. data/lib/lotu/text_box.rb +8 -3
  26. data/lib/lotu.rb +6 -4
  27. data/lotu.gemspec +21 -15
  28. data/spec/lotu/actor_spec.rb +36 -0
  29. data/spec/lotu/game_spec.rb +22 -15
  30. data/spec/lotu/shared_spec.rb +41 -0
  31. metadata +25 -19
  32. data/lib/lotu/systems/fps_system.rb +0 -31
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.15
1
+ 0.1.16
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
2
3
 
3
4
  # Hello world for lotu
4
5
  # Here you will learn about:
@@ -17,7 +18,6 @@ include Lotu
17
18
  # subclass, set the image for all the instances of that class and
18
19
  # define the movement methods
19
20
  class Portrait < Actor
20
-
21
21
  def initialize( opts )
22
22
  # It's important to call super so we take advantage of automatic
23
23
  # management for this object (being added to draw and update queues)
@@ -32,15 +32,15 @@ class Portrait < Actor
32
32
  def move_left; @x -= 1 end
33
33
  def move_up; @y -= 1 end
34
34
  def move_down; @y += 1 end
35
-
36
35
  end
37
36
 
38
37
  # Let's subclass the Game class and write some code!
39
38
  class MyPortraits < Game
39
+ use StalkerSystem, :stalk => [Actor, TextBox, Portrait, Object]
40
40
 
41
41
  def initialize
42
42
  # This will call the hooks:
43
- # load_resources, setup_systems, setup_input and setup_actors
43
+ # load_resources, setup_actors, setup_input and setup_events
44
44
  # declared in the parent class
45
45
  super
46
46
  # When the Escape key is pressed, call the close method on our
@@ -82,6 +82,8 @@ class MyPortraits < Game
82
82
  @info = TextBox.new
83
83
  # Add some text
84
84
  @info.text("Hello world!")
85
+ @info.watch lambda{ "FPS: #{ fps }" }
86
+ @info.watch( @systems[StalkerSystem] )
85
87
  # Add more text, but specify the color and size in pixels
86
88
  @info.text("Move the portraits around with arrow keys", :size => 16, :color => 0xff33ccff)
87
89
  end
@@ -14,26 +14,19 @@ include Lotu
14
14
 
15
15
  # Create a class for displaying something on screen
16
16
  class Lobo < Actor
17
- def initialize(opts={})
17
+ def initialize( opts={} )
18
18
  super
19
19
  set_image 'lobo_tuerto.png', :width => 100
20
20
  end
21
21
 
22
22
  # Define a method for teleporting our instances around
23
- def teleport(x, y)
23
+ def teleport( x, y )
24
24
  @x, @y = x, y
25
25
  end
26
26
  end
27
27
 
28
28
  class Cursors < Game
29
- def initialize
30
- # This will call the hooks:
31
- # load_resources, setup_systems, setup_input and setup_actors
32
- # declared in the parent class
33
- super
34
- # Custom setup methods for this class
35
- setup_events
36
- end
29
+ use StalkerSystem, :stalk => [Actor, Cursor, TextBox, Lobo, Object]
37
30
 
38
31
  def load_resources
39
32
  with_path_from_file(__FILE__) do
@@ -41,19 +34,6 @@ class Cursors < Game
41
34
  end
42
35
  end
43
36
 
44
- def setup_systems
45
- # It's important to call super here to setup the InputSystem in
46
- # the parent class
47
- super
48
- # To use the systems lotu provides, you just "use" them
49
- # Let's activate the FPS system, so we can track the frames per
50
- #second our app is pushing out
51
- use(FpsSystem)
52
- # Activate the stalker system to track how many objects of these
53
- # classes are around
54
- use(StalkerSystem, :stalk => [Actor, Cursor, TextBox, Lobo, Object])
55
- end
56
-
57
37
  # Setup some input handling for our Cursors app
58
38
  def setup_input
59
39
  set_keys(KbEscape => :close,
@@ -123,10 +103,10 @@ class Cursors < Game
123
103
  @info = TextBox.new(:size => 15)
124
104
  @info.text("Press F1 to hide this text", :size => 24)
125
105
  # Watch the FPS, so we get a nice FPS report on the screen
126
- @info.watch(@systems[FpsSystem], :size => 20)
106
+ @info.watch(lambda{ "FPS: #{ fps }" }, :size => 20)
127
107
  # Watch the Stalker system, so we know how many objects of the
128
- # classes we specified up in setup_systems are around
129
- @info.watch(@systems[StalkerSystem], :color => 0xff3ffccf)
108
+ # classes we specified up in "use StalkerSystem" are around
109
+ @info.watch( @systems[StalkerSystem], :color => 0xff3ffccf )
130
110
  # We can change the size for a specific line of text
131
111
  @info.text("@cursor1 data:", :size => 20)
132
112
  @info.text("move with Mouse | click with LeftMouseButton")
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ LIB_PATH = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'lotu.rb')
4
+ require File.expand_path(LIB_PATH)
5
+
6
+ include Lotu
7
+ include Gosu
8
+
9
+ class Box < Actor
10
+ collides_as :box
11
+
12
+ def initialize( opts )
13
+ # It's important to call super so we take advantage of automatic
14
+ # management for this object (being added to draw and update queues)
15
+ super
16
+ # Use the image which filename is "lobo_tuerto.png", and scale
17
+ # it's size to half width and half height
18
+ set_image 'lobo_tuerto.png', :factor_x => 0.5, :factor_y => 0.5
19
+ calc_radius
20
+ end
21
+
22
+ # Let's define some basic movement methods
23
+ def move_right; @x += 1 end
24
+ def move_left; @x -= 1 end
25
+ def move_up; @y -= 1 end
26
+ def move_down; @y += 1 end
27
+ end
28
+
29
+ class Example < Game
30
+ use CollisionSystem
31
+ use StalkerSystem, :stalk => [Game, Box, Actor, BaseSystem, Object]
32
+
33
+ def initialize
34
+ super
35
+ set_keys(KbEscape => :close,
36
+ KbD => [:debug!, false])
37
+ end
38
+
39
+ # This method is called when we call super inside initialize
40
+ def load_resources
41
+ # From this file,
42
+ with_path_from_file(__FILE__) do
43
+ # go back one dir and search inside media/images
44
+ load_images '../media/images'
45
+ end
46
+ end
47
+
48
+ def setup_events
49
+ when_colliding( :box, :box ) do |b1, b2|
50
+ b1.color = Gosu::Color.from_hsv(rand(360), 1, 1)
51
+ b2.color = Gosu::Color.from_hsv(rand(360), 1, 1)
52
+ end
53
+ end
54
+
55
+ # This method is called when we call super inside initialize
56
+ def setup_actors
57
+ # Create a portrait in the middle of the screen
58
+ @lobo1 = Box.new(:x => width/2 - 100, :y => height/2)
59
+ # Map keys to some methods
60
+ @lobo1.set_keys(KbRight => :move_right,
61
+ KbLeft => :move_left,
62
+ KbUp => :move_up,
63
+ KbDown => :move_down)
64
+
65
+ # Rinse and repeat... but invert some keys
66
+ @lobo2 = Box.new(:x => width/2 + 100, :y => height/2)
67
+ @lobo2.set_keys(KbRight => :move_left,
68
+ KbLeft => :move_right,
69
+ KbUp => :move_up,
70
+ KbDown => :move_down)
71
+
72
+ # Create a TextBox so we can display a message on screen
73
+ @info = TextBox.new
74
+ # Add some text
75
+ @info.text("Hello world!")
76
+ @info.watch lambda{ "FPS: #{ fps }" }
77
+ @info.watch( @systems[StalkerSystem] )
78
+ # Add more text, but specify the color and size in pixels
79
+ @info.text("Move the portraits around with arrow keys", :size => 16, :color => 0xff33ccff)
80
+ end
81
+ end
82
+
83
+ Example.new.show
@@ -14,12 +14,7 @@ include Lotu
14
14
  # Let's define a Missile class that will use a Steering system to
15
15
  # control it's movement
16
16
  class Missile < Actor
17
- def initialize(opts={})
18
- super
19
- # Activate the steering system and pass the opts, since they might
20
- # have some config info for the system
21
- use(SteeringSystem, opts)
22
- end
17
+ use SteeringSystem
23
18
 
24
19
  def teleport(x, y)
25
20
  @pos.x, @pos.y = x, y
@@ -28,14 +23,7 @@ end
28
23
 
29
24
  # The main app class
30
25
  class SteeringMissiles < Game
31
- def initialize
32
- # This will call the hooks:
33
- # load_resources, setup_systems, setup_input and setup_actors
34
- # declared in the parent class
35
- super
36
- # Custom setup methods for this class
37
- setup_events
38
- end
26
+ use StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object]
39
27
 
40
28
  # Let's load some images and animations, check out the animations
41
29
  # directory, the animation there was created with:
@@ -54,13 +42,6 @@ class SteeringMissiles < Game
54
42
  KbF1 => [:toggle_missile_info, false])
55
43
  end
56
44
 
57
- def setup_systems
58
- # It's important to call super here to setup the InputSystem
59
- super
60
- use(FpsSystem)
61
- use(StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object])
62
- end
63
-
64
45
  def setup_actors
65
46
  @big_missile = Missile.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
66
47
  @big_missile.teleport(width/2, height/2)
@@ -77,8 +58,8 @@ class SteeringMissiles < Game
77
58
 
78
59
  @window_info = TextBox.new(:size => 15)
79
60
  @window_info.text("Press F1 to hide this text", :size => 24)
80
- @window_info.watch(@systems[FpsSystem], :size => 20)
81
- @window_info.watch(@systems[StalkerSystem], :color => 0xff33ccff)
61
+ @window_info.watch(lambda{ "FPS: #{fps}" }, :size => 20)
62
+ @window_info.watch( @systems[StalkerSystem], :color => 0xff33ccff)
82
63
  @window_info.watch(@cursor, :color => @cursor.color)
83
64
  @window_info.text("Click to start the simulation", :color => 0xffffff00)
84
65
  @window_info.text("One will pursuit while the other evades, right click to center evader on screen")
@@ -14,12 +14,7 @@ include Lotu
14
14
  # Let's define a Missile class that will use a Steering system to
15
15
  # control it's movement
16
16
  class Missile < Actor
17
- def initialize(opts={})
18
- super
19
- # Activate the steering system and pass the opts, since they might
20
- # have some config info for the system
21
- use(SteeringSystem, opts)
22
- end
17
+ use SteeringSystem
23
18
 
24
19
  def teleport(x, y)
25
20
  @pos.x, @pos.y = x, y
@@ -28,14 +23,7 @@ end
28
23
 
29
24
  # The main app class
30
25
  class EvadeMultiple < Game
31
- def initialize
32
- # This will call the hooks:
33
- # load_resources, setup_systems, setup_input and setup_actors
34
- # declared in the parent class
35
- super
36
- # Custom setup methods for this class
37
- setup_events
38
- end
26
+ use StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object]
39
27
 
40
28
  # Let's load some images and animations, check out the animations
41
29
  # directory, the animation there was created with:
@@ -55,13 +43,6 @@ class EvadeMultiple < Game
55
43
  KbSpace => [:pause!, false])
56
44
  end
57
45
 
58
- def setup_systems
59
- # It's important to call super here to setup the InputSystem
60
- super
61
- use(FpsSystem)
62
- use(StalkerSystem, :stalk => [Actor, Missile, Vector2d, Object])
63
- end
64
-
65
46
  def setup_actors
66
47
  @big_missile = Missile.new(:mass => 0.3, :max_speed => 100, :max_turn_rate => 140)
67
48
  @big_missile.teleport(width/2, height/2)
@@ -81,8 +62,8 @@ class EvadeMultiple < Game
81
62
 
82
63
  @window_info = TextBox.new(:size => 15)
83
64
  @window_info.text("Press F1 to hide this text", :size => 24)
84
- @window_info.watch(@systems[FpsSystem], :size => 20)
85
- @window_info.watch(@systems[StalkerSystem], :color => 0xff33ccff)
65
+ @window_info.watch(lambda{ "FPS: #{fps}" }, :size => 20)
66
+ @window_info.watch( @systems[StalkerSystem], :color => 0xff33ccff)
86
67
  @window_info.watch(@cursor, :color => @cursor.color)
87
68
  @window_info.text("Click to start the simulation", :color => 0xffffff00)
88
69
  @window_info.text("Little missiles will pursuit while the big one evades, right click to center big one on screen")
data/lib/lotu/actor.rb CHANGED
@@ -1,13 +1,20 @@
1
1
  module Lotu
2
2
  class Actor
3
- attr_accessor :parent, :x, :y, :systems,
3
+ extend Behavior
4
+
5
+ behave_like SystemUser
6
+ use AnimationSystem
7
+ use InterpolationSystem
8
+
9
+ behave_like Eventful
10
+ behave_like Collidable
11
+ behave_like Controllable
12
+
13
+ attr_accessor :parent, :x, :y,
4
14
  :z, :angle, :center_x, :center_y,
5
15
  :factor_x, :factor_y, :color, :mode, :image,
6
16
  :width, :height
7
17
 
8
- include SystemUser
9
- include Controllable
10
-
11
18
  def initialize(opts={})
12
19
  default_opts = {
13
20
  :x => 0,
@@ -22,20 +29,17 @@ module Lotu
22
29
  :mode => :default,
23
30
  :parent => $lotu
24
31
  }
32
+
25
33
  opts = default_opts.merge!(opts)
26
34
  @parent = opts[:parent]
27
35
  @parent.manage_me(self)
28
36
  set_image(opts[:image], opts) if opts[:image]
29
37
  parse_options(opts)
30
38
  @color = rand_color if opts[:rand_color]
31
-
32
39
  set_keys(opts[:keys]) unless opts[:keys].nil?
33
40
 
34
- # Add extra functionality
35
- self.extend Eventful
36
- self.extend Collidable
37
- use(AnimationSystem)
38
- use(InterpolationSystem)
41
+ # so it can start behaving
42
+ init_behavior opts
39
43
  end
40
44
 
41
45
  # Easy access to delta-time
@@ -118,12 +122,16 @@ module Lotu
118
122
  end
119
123
 
120
124
  def update
121
- @systems.each_pair do |klass, system|
122
- system.update
123
- end
125
+ # to call update on behaviors (that in turn wil call
126
+ # update on systems, for example)
127
+ super
124
128
  end
125
129
 
126
130
  def draw
131
+ # to call draw on behaviors (that in turn will call
132
+ # draw on systems, for example)
133
+ super
134
+
127
135
  unless @image.nil?
128
136
  @image.draw_rot(@x, @y, @z, @angle, @center_x, @center_y, @factor_x*@zoom_x, @factor_y*@zoom_y, @color, @mode)
129
137
  draw_debug if $lotu.debug?
@@ -0,0 +1,19 @@
1
+ module Lotu
2
+ module Behavior
3
+
4
+ def behave_like something
5
+ class << self
6
+ attr_accessor :behavior_options
7
+ end
8
+ include something
9
+
10
+ @behavior_options ||= Hash.new{ |h,k| h[k] = {} }
11
+ end
12
+
13
+ def inherited subclass
14
+ subclass.behavior_options =
15
+ behavior_options.inject({}){ |hash, opts| hash[opts[0]] = opts[1].deep_copy; hash }
16
+ end
17
+
18
+ end
19
+ end
@@ -1,20 +1,35 @@
1
1
  module Lotu
2
2
  module Collidable
3
3
 
4
- def self.extended(instance)
5
- instance.init_behavior
4
+ def self.included base
5
+ base.extend ClassMethods
6
6
  end
7
7
 
8
- def init_behavior
9
- @collision_tag = nil
8
+ def calc_radius
9
+ if @width
10
+ @collision_radius = @width/2.0 * @factor_x
11
+ elsif @height
12
+ @collision_radius = @height/2.0 * @factor_y
13
+ else
14
+ @collision_radius = 100
15
+ end
10
16
  end
11
17
 
12
- def collides_as(tag)
13
- @collision_tag = tag
14
- @parent.systems[CollisionSystem].add_entity(self, tag)
18
+ def init_behavior opts
19
+ super if defined? super
20
+
21
+ calc_radius
22
+ class << self
23
+ attr_accessor :collision_radius
24
+ end
25
+
26
+ @collision_tag = self.class.behavior_options[Collidable]
27
+ # TODO: Change @parent for @manager (could be a Game or a Scene)
28
+ @parent.systems[CollisionSystem].add_entity(self, @collision_tag) if @parent.systems[CollisionSystem]
15
29
  end
16
30
 
17
31
  def collides_with(other)
32
+ return false if self.equal? other
18
33
  Gosu.distance(@x, @y, other.x, other.y) < @collision_radius + other.collision_radius
19
34
  end
20
35
 
@@ -23,5 +38,11 @@ module Lotu
23
38
  @parent.systems[CollisionSystem].remove_entity(self, @collision_tag) if @parent.systems[CollisionSystem]
24
39
  end
25
40
 
41
+ module ClassMethods
42
+ def collides_as tag
43
+ behavior_options[Collidable] = tag
44
+ end
45
+ end
46
+
26
47
  end
27
48
  end
@@ -13,7 +13,7 @@ module Lotu
13
13
  # This will call #go_up every 50ms
14
14
  # Gosu::Button::KbUp => [:go_up, 50]
15
15
  def set_keys(keys)
16
- @parent.systems[InputSystem].set_keys(self, keys)
16
+ @parent.systems[InputManagerSystem].set_keys(self, keys)
17
17
  end
18
18
 
19
19
  end
@@ -18,11 +18,8 @@
18
18
  module Lotu
19
19
  module Eventful
20
20
 
21
- def self.extended(instance)
22
- instance.init_behavior
23
- end
24
-
25
- def init_behavior
21
+ def init_behavior opts
22
+ super if defined? super
26
23
  @events = {}
27
24
  end
28
25
 
@@ -0,0 +1,104 @@
1
+ module Lotu
2
+ module ResourceManager
3
+
4
+ attr_accessor :images, :sounds, :songs, :animations, :default_font
5
+
6
+ def init_behavior opts
7
+ super if defined? super
8
+ @images = {}
9
+ @sounds = {}
10
+ @songs = {}
11
+ @animations = Hash.new{ |h,k| h[k] = [] }
12
+ @default_font = Hash.new{ |h,k| h[k] = Gosu::Font.new( self, Gosu::default_font_name, k ) }
13
+ end
14
+
15
+ def image( name )
16
+ @images[name]
17
+ end
18
+
19
+ def sound( name )
20
+ @sounds[name]
21
+ end
22
+
23
+ def song( name )
24
+ @songs[name]
25
+ end
26
+
27
+ def animation(name)
28
+ @animations[name]
29
+ end
30
+
31
+ def with_path_from_file(path, &blk)
32
+ @path = File.expand_path(File.dirname path)
33
+ instance_eval &blk
34
+ end
35
+
36
+ protected
37
+ def load_images( path )
38
+ count = 0
39
+ with_files(/\.png$|\.jpg$|\.bmp$/, path) do |file_name, file_path|
40
+ @images[file_name] = Gosu::Image.new($lotu, file_path)
41
+ count += 1
42
+ end
43
+ puts "\n#{count} image(s) loaded."
44
+ end
45
+
46
+ def load_sounds(path)
47
+ count = 0
48
+ with_files(/\.ogg$|\.mp3$|\.wav$/, path) do |file_name, file_path|
49
+ @sounds[file_name] = Gosu::Sample.new($lotu, file_path)
50
+ count += 1
51
+ end
52
+ puts "\n#{count} sounds(s) loaded."
53
+ end
54
+
55
+ def load_songs(path)
56
+ count = 0
57
+ with_files(/\.ogg$|\.mp3$|\.wav$/, path) do |file_name, file_path|
58
+ @songs[file_name] = Gosu::Song.new($lotu, file_path)
59
+ count += 1
60
+ end
61
+ puts "\n#{count} song(s) loaded."
62
+ end
63
+
64
+ def load_animations(path)
65
+ count = 0
66
+ coords = Hash.new{|h,k| h[k] = []}
67
+
68
+ with_files(/\.txt$/, path) do |file_name, file_path|
69
+ name = File.basename(file_name, '.txt')
70
+ File.open(file_path) do |file|
71
+ file.lines.each do |line|
72
+ coords[name] << line.scan(/\d+/).map!(&:to_i)
73
+ end
74
+ end
75
+ false
76
+ end
77
+
78
+ with_files(/\.png$|\.jpg$|\.bmp$/, path) do |file_name, file_path|
79
+ name, extension = file_name.split('.')
80
+ coords[name].each do |index, x, y, width, height|
81
+ @animations[file_name] << Gosu::Image.new($lotu, file_path, true, x, y, width, height)
82
+ end
83
+ count += 1 if coords[name]
84
+ end
85
+ puts "\n#{count} animation(s) loaded."
86
+ end
87
+
88
+ def with_files(regexp, path)
89
+ path = File.expand_path(File.join(@path, path))
90
+ puts "\nLoading from: #{path}".blue if @debug
91
+
92
+ Dir.entries(path).grep(regexp).each do |entry|
93
+ begin
94
+ report = yield(entry, File.join(path, entry))
95
+ print '.'.green if report
96
+ rescue Exception => e
97
+ print '.'.red
98
+ puts e, File.join(path, entry) if @debug
99
+ end
100
+ end
101
+ end
102
+
103
+ end
104
+ end
@@ -1,10 +1,48 @@
1
1
  module Lotu
2
2
  module SystemUser
3
3
 
4
- # Allows to activate a system in the host
5
- def use( klass, opts={} )
4
+ attr_accessor :systems
5
+
6
+ def self.included base
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def init_behavior user_opts
11
+ super if defined? super
6
12
  @systems ||= Hash.new
7
- @systems[klass] = klass.new( self, opts )
13
+
14
+ options_for_me = self.class.behavior_options[SystemUser]
15
+
16
+ options_for_me && options_for_me.each do |klass, options|
17
+ # add the behavior options to the user_opts hash
18
+ # in case we need access to some level class config param
19
+ user_opts.merge!(options)
20
+ @systems[klass] = klass.new( self, user_opts )
21
+ end
22
+ end
23
+
24
+ # Need to call this inside update
25
+ def update
26
+ super if defined? super
27
+ @systems.each_value do |system|
28
+ system.update
29
+ end
30
+ end
31
+
32
+ # Need to call this inside draw
33
+ def draw
34
+ super if defined? super
35
+ # Systems may report interesting stuff
36
+ @systems.each_value do |system|
37
+ system.draw
38
+ end
39
+ end
40
+
41
+ # Allows to activate a system in the host
42
+ module ClassMethods
43
+ def use( klass, opts={} )
44
+ behavior_options[SystemUser][klass] = opts
45
+ end
8
46
  end
9
47
 
10
48
  end