gosu_extensions 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/lib/controls.rb +35 -0
- data/lib/extensions/module.rb +15 -0
- data/lib/extensions/numeric.rb +9 -0
- data/lib/game_window.rb +381 -0
- data/lib/gosu_extensions.rb +53 -0
- data/lib/layer.rb +11 -0
- data/lib/old_game_window.rb +249 -0
- data/lib/resources.rb +5 -0
- data/lib/scheduling.rb +48 -0
- data/lib/traits/attachable.rb +12 -0
- data/lib/traits/controllable.rb +43 -0
- data/lib/traits/damaging.rb +16 -0
- data/lib/traits/generator.rb +46 -0
- data/lib/traits/initializer_hooks.rb +34 -0
- data/lib/traits/it_is_a.rb +47 -0
- data/lib/traits/lives.rb +59 -0
- data/lib/traits/pod.rb +95 -0
- data/lib/traits/shooter.rb +113 -0
- data/lib/traits/shot.rb +16 -0
- data/lib/traits/targetable.rb +11 -0
- data/lib/traits/targeting/closest.rb +26 -0
- data/lib/traits/targeting.rb +1 -0
- data/lib/traits/turnable.rb +41 -0
- data/lib/units/moveable.rb +196 -0
- data/lib/units/short_lived.rb +20 -0
- data/lib/units/thing.rb +155 -0
- data/lib/vector_utilities.rb +11 -0
- data/lib/waves.rb +34 -0
- metadata +115 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
module Shooter
|
2
|
+
|
3
|
+
Shoot = :shoot
|
4
|
+
|
5
|
+
manual <<-MANUAL
|
6
|
+
Defines:
|
7
|
+
range <some range> # This is only needed for targeted shooting, e.g. automatic cannons
|
8
|
+
frequency <some shooting frequency> # TODO block
|
9
|
+
shoots <class:thing>
|
10
|
+
muzzle_position { position calculation } || frontal # a block
|
11
|
+
muzzle_velocity { velocity calculation }
|
12
|
+
muzzle_rotation { rotation calculation }
|
13
|
+
|
14
|
+
Example:
|
15
|
+
frequency 20
|
16
|
+
shoots Bullet
|
17
|
+
muzzle_position { self.position + self.rotation_vector.normalize*self.radius }
|
18
|
+
muzzle_velocity { |_| self.rotation_vector.normalize }
|
19
|
+
muzzle_rotation { |_| self.rotation }
|
20
|
+
MANUAL
|
21
|
+
|
22
|
+
attr_accessor :shot_type
|
23
|
+
attr_writer :shooting_range, :shooting_rate
|
24
|
+
|
25
|
+
def self.included base
|
26
|
+
base.extend ClassMethods
|
27
|
+
end
|
28
|
+
|
29
|
+
def shooting_range
|
30
|
+
@shooting_range || @shooting_range = 300
|
31
|
+
end
|
32
|
+
|
33
|
+
def shooting_rate
|
34
|
+
@shooting_rate || @shooting_rate = (SUBSTEPS**2).to_f/2
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
def range amount
|
39
|
+
InitializerHooks.register self do
|
40
|
+
self.shooting_range = amount
|
41
|
+
end
|
42
|
+
end
|
43
|
+
def frequency amount
|
44
|
+
InitializerHooks.register self do
|
45
|
+
self.shooting_rate = ((SUBSTEPS**2).to_f/amount)/2
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def shoots type
|
49
|
+
InitializerHooks.register self do
|
50
|
+
self.shot_type = type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
def muzzle_position &block
|
54
|
+
InitializerHooks.register self do
|
55
|
+
muzzle_position_func &block
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def muzzle_velocity &block
|
59
|
+
InitializerHooks.register self do
|
60
|
+
muzzle_velocity_func &block
|
61
|
+
end
|
62
|
+
end
|
63
|
+
def muzzle_rotation &block
|
64
|
+
InitializerHooks.register self do
|
65
|
+
muzzle_rotation_func &block
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def shot
|
71
|
+
self.shot_type.new @window
|
72
|
+
end
|
73
|
+
|
74
|
+
def muzzle_position
|
75
|
+
instance_eval &(@muzzle_position || @muzzle_position = lambda { |*| self.position + self.rotation_vector*self.radius })
|
76
|
+
end
|
77
|
+
def muzzle_velocity target = nil
|
78
|
+
instance_eval &(@muzzle_velocity || @muzzle_velocity = lambda { |*| self.rotation_vector })
|
79
|
+
end
|
80
|
+
def muzzle_rotation target = nil
|
81
|
+
instance_eval &(@muzzle_rotation || @muzzle_rotation = lambda { |*| self.rotation })
|
82
|
+
end
|
83
|
+
def muzzle_position_func &position
|
84
|
+
@muzzle_position = position
|
85
|
+
end
|
86
|
+
def muzzle_velocity_func &velocity
|
87
|
+
@muzzle_velocity = velocity
|
88
|
+
end
|
89
|
+
def muzzle_rotation_func &rotation
|
90
|
+
@muzzle_rotation = rotation
|
91
|
+
end
|
92
|
+
def shoot? target = nil
|
93
|
+
target.nil? ? true : target.distance_from(self) < self.range
|
94
|
+
end
|
95
|
+
def shoot target = nil
|
96
|
+
return unless shoot? target
|
97
|
+
|
98
|
+
sometimes :loading, self.shooting_rate do
|
99
|
+
projectile = self.shot.shoot_from self
|
100
|
+
projectile.rotation = self.muzzle_rotation
|
101
|
+
projectile.speed = self.muzzle_velocity * projectile.velocity + self.speed
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if self.kind_of?(::Targeting)
|
106
|
+
def target *targets
|
107
|
+
return if targets.empty?
|
108
|
+
target = acquire *targets
|
109
|
+
shoot target
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
data/lib/traits/shot.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Use this if you need a closest targeting mechanism.
|
2
|
+
#
|
3
|
+
module Targeting
|
4
|
+
|
5
|
+
module Closest
|
6
|
+
|
7
|
+
# Returns the closest target of all targets in the fire arc.
|
8
|
+
#
|
9
|
+
# TODO fire arc
|
10
|
+
#
|
11
|
+
def acquire *targets
|
12
|
+
closest = nil
|
13
|
+
lowest_distance = nil
|
14
|
+
|
15
|
+
targets.each do |target|
|
16
|
+
distance = (target.position - self.position).length
|
17
|
+
next if lowest_distance && distance > lowest_distance
|
18
|
+
lowest_distance = distance
|
19
|
+
closest = target
|
20
|
+
end
|
21
|
+
|
22
|
+
closest
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
module Targeting; end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
module Turnable
|
4
|
+
|
5
|
+
Left = :turn_left
|
6
|
+
Right = :turn_right
|
7
|
+
|
8
|
+
def self.included base
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO meta
|
13
|
+
#
|
14
|
+
module ClassMethods
|
15
|
+
def turn_speed amount
|
16
|
+
amount = amount.to_f / 2
|
17
|
+
define_method :turn_speed do
|
18
|
+
amount
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
#
|
25
|
+
def turn_speed
|
26
|
+
0.5 # Default turn speed
|
27
|
+
end
|
28
|
+
|
29
|
+
# Turns the thing left, depending on turn speed.
|
30
|
+
#
|
31
|
+
def turn_left
|
32
|
+
self.rotation -= self.turn_speed / (SUBSTEPS**2)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Turns the thing right, depending on turn speed.
|
36
|
+
#
|
37
|
+
def turn_right
|
38
|
+
self.rotation += self.turn_speed / (SUBSTEPS**2)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# A moveable has a shape, speed etc.
|
2
|
+
#
|
3
|
+
# TODO moveable should only have active components, like accelerate etc. Positioning etc. should go to Thing.
|
4
|
+
#
|
5
|
+
class Moveable < Thing
|
6
|
+
|
7
|
+
Accelerate = :accelerate
|
8
|
+
Left = :move_left
|
9
|
+
Right = :move_right
|
10
|
+
Up = :move_up
|
11
|
+
Down = :move_down
|
12
|
+
Backwards = :backwards
|
13
|
+
# Jump = :jump
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
# Initial setting.
|
18
|
+
#
|
19
|
+
def friction amount = nil, &block
|
20
|
+
to_execute = block_given? ? block : lambda { amount }
|
21
|
+
InitializerHooks.register self do
|
22
|
+
self.friction = to_execute[]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def velocity amount = nil, &block
|
26
|
+
to_execute = block_given? ? block : lambda { amount }
|
27
|
+
InitializerHooks.register self do
|
28
|
+
self.velocity = to_execute[]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
def rotation amount = nil, &block
|
32
|
+
to_execute = block_given? ? block : lambda { amount }
|
33
|
+
InitializerHooks.register self do
|
34
|
+
self.rotation = to_execute[]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Directly set the position of our Moveable using a vector.
|
41
|
+
#
|
42
|
+
def warp vector
|
43
|
+
@shape.body.p = vector
|
44
|
+
end
|
45
|
+
|
46
|
+
# Directly set the position of our Moveable.
|
47
|
+
#
|
48
|
+
def warp_to x, y
|
49
|
+
@shape.body.p = CP::Vec2.new(x, y)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Directly set the position of our Moveable.
|
53
|
+
#
|
54
|
+
def position= position
|
55
|
+
@shape.body.p = position
|
56
|
+
end
|
57
|
+
def position
|
58
|
+
@shape.body.p
|
59
|
+
end
|
60
|
+
|
61
|
+
# Directly set the torque of our Moveable.
|
62
|
+
#
|
63
|
+
def torque= torque
|
64
|
+
@shape.body.t = torque
|
65
|
+
end
|
66
|
+
def torque
|
67
|
+
@shape.body.t
|
68
|
+
end
|
69
|
+
|
70
|
+
# Directly set the speed of our Moveable.
|
71
|
+
#
|
72
|
+
def speed= v
|
73
|
+
@shape.body.v = v
|
74
|
+
end
|
75
|
+
def speed
|
76
|
+
@shape.body.v
|
77
|
+
end
|
78
|
+
def current_speed
|
79
|
+
# TODO use built-in function
|
80
|
+
#
|
81
|
+
Math.sqrt(speed.x**2 + speed.y**2)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Directly set the rotation of our Moveable.
|
85
|
+
#
|
86
|
+
def rotation= rotation
|
87
|
+
@shape.body.a = rotation % (2*Math::PI)
|
88
|
+
end
|
89
|
+
def rotation
|
90
|
+
@shape.body.a
|
91
|
+
end
|
92
|
+
def drawing_rotation
|
93
|
+
self.rotation.radians_to_gosu
|
94
|
+
end
|
95
|
+
def rotation_vector
|
96
|
+
@shape.body.a.radians_to_vec2
|
97
|
+
end
|
98
|
+
|
99
|
+
def friction= friction
|
100
|
+
@shape.u = friction
|
101
|
+
end
|
102
|
+
def friction
|
103
|
+
@shape.u
|
104
|
+
end
|
105
|
+
|
106
|
+
# Length is the vector length you want.
|
107
|
+
#
|
108
|
+
# Note: radians_to_vec2
|
109
|
+
#
|
110
|
+
def rotation_as_vector length
|
111
|
+
rotation = -self.rotation + Math::PI / 2
|
112
|
+
x = Math.sin rotation
|
113
|
+
y = Math.cos rotation
|
114
|
+
total_length = Math.sqrt(x**2 + y**2)
|
115
|
+
multiplier = length / total_length
|
116
|
+
CP::Vec2.new(x * multiplier, y * multiplier)
|
117
|
+
end
|
118
|
+
|
119
|
+
def move
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
# Methods for controls.
|
124
|
+
#
|
125
|
+
def accelerate strength = 1
|
126
|
+
self.speed += self.rotation_vector * strength/SUBSTEPS
|
127
|
+
end
|
128
|
+
def backwards strength = 1
|
129
|
+
accelerate -0.5*strength
|
130
|
+
end
|
131
|
+
def move_left strength = 1
|
132
|
+
self.speed += CP::Vec2.new(-strength.to_f/SUBSTEPS, 0)
|
133
|
+
end
|
134
|
+
def move_right strength = 1
|
135
|
+
self.speed += CP::Vec2.new(strength.to_f/SUBSTEPS, 0)
|
136
|
+
end
|
137
|
+
def move_up strength = 1
|
138
|
+
self.speed += CP::Vec2.new(0, -strength.to_f/SUBSTEPS)
|
139
|
+
end
|
140
|
+
def move_down strength = 1
|
141
|
+
self.speed += CP::Vec2.new(0, strength.to_f/SUBSTEPS)
|
142
|
+
end
|
143
|
+
# def jump strength = 100
|
144
|
+
# self.speed += CP::Vec2.new(0, -strength.to_f/SUBSTEPS) if self.current_speed <= 1
|
145
|
+
# end
|
146
|
+
|
147
|
+
# Movement rules
|
148
|
+
#
|
149
|
+
# Note: Call in method move.
|
150
|
+
#
|
151
|
+
def bounce_off_border_x elasticity = 1.0
|
152
|
+
if position.x > window.screen_width || position.x < 0
|
153
|
+
shape.body.v.x = -shape.body.v.x.to_f*elasticity
|
154
|
+
end
|
155
|
+
end
|
156
|
+
def bounce_off_border_y elasticity = 1.0
|
157
|
+
if position.y > window.screen_height || position.y < 0
|
158
|
+
shape.body.v.y = -shape.body.v.y.to_f*elasticity
|
159
|
+
end
|
160
|
+
end
|
161
|
+
def bounce_off_border elasticity = 1.0
|
162
|
+
bounce_off_border_x elasticity
|
163
|
+
bounce_off_border_y elasticity
|
164
|
+
end
|
165
|
+
def wrap_around_border_x
|
166
|
+
if position.x > window.screen_width
|
167
|
+
position.x -= window.screen_width
|
168
|
+
elsif position.x < 0
|
169
|
+
position.x += window.screen_width
|
170
|
+
end
|
171
|
+
end
|
172
|
+
def wrap_around_border_y
|
173
|
+
if position.y > window.screen_height
|
174
|
+
position.y -= window.screen_height
|
175
|
+
elsif position.y < 0
|
176
|
+
position.y += window.screen_height
|
177
|
+
end
|
178
|
+
end
|
179
|
+
def wrap_around_border
|
180
|
+
wrap_around_border_x
|
181
|
+
wrap_around_border_y
|
182
|
+
end
|
183
|
+
def obey_gravity
|
184
|
+
self.speed += window.gravity_vector
|
185
|
+
end
|
186
|
+
def on_hitting_x
|
187
|
+
yield if block_given? && position.x > window.screen_width || position.x < 0
|
188
|
+
end
|
189
|
+
def on_hitting_y
|
190
|
+
yield if block_given? && position.y > window.screen_height || position.y < 0
|
191
|
+
end
|
192
|
+
def rotate_towards_velocity
|
193
|
+
self.rotation = self.speed.to_angle
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ShortLived < Moveable
|
2
|
+
|
3
|
+
def initialize window
|
4
|
+
super window
|
5
|
+
|
6
|
+
threaded self.lifetime do
|
7
|
+
self.destroy!
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def lifetime lifetime = nil, &block
|
14
|
+
block = lambda { lifetime } unless block_given?
|
15
|
+
define_method :lifetime, &block
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/units/thing.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
class Thing
|
2
|
+
|
3
|
+
include VectorUtilities
|
4
|
+
include InitializerHooks
|
5
|
+
include ItIsA
|
6
|
+
|
7
|
+
attr_writer :layer
|
8
|
+
attr_reader :window, :shape
|
9
|
+
|
10
|
+
# Every thing knows the window it is attached to.
|
11
|
+
#
|
12
|
+
def initialize window
|
13
|
+
@window = window
|
14
|
+
self.destroyed = false
|
15
|
+
after_initialize
|
16
|
+
end
|
17
|
+
|
18
|
+
# Default layer is Layer::Players.
|
19
|
+
#
|
20
|
+
def layer
|
21
|
+
@layer || Layer::Players
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# TODO Move to module.
|
27
|
+
#
|
28
|
+
def image path, *args
|
29
|
+
InitializerHooks.register self do
|
30
|
+
@image = Gosu::Image.new self.window, File.join(Resources.root, path), *args
|
31
|
+
end
|
32
|
+
define_method :image do
|
33
|
+
@image
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def sequenced_image path, width, height, frequency = 10, &block
|
37
|
+
InitializerHooks.register self do
|
38
|
+
@image_sequence_started = Time.now
|
39
|
+
@image = Gosu::Image::load_tiles self.window, File.join(Resources.root, path), width, height, false
|
40
|
+
end
|
41
|
+
# divider = 1000 / frequency
|
42
|
+
define_method :image do
|
43
|
+
@image[(block ? block : lambda { (Time.now - @image_sequence_started)*frequency % @image.size })[]]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@@form_shape_class_mapping = { :circle => CP::Shape::Circle }
|
47
|
+
def shape form
|
48
|
+
form_shape_class_mapping = @@form_shape_class_mapping
|
49
|
+
InitializerHooks.append self do
|
50
|
+
shape_class = form_shape_class_mapping[form]
|
51
|
+
raise "Shape #{form} does not exist." unless shape_class
|
52
|
+
@shape = shape_class.new(CP::Body.new(self.mass, self.moment), self.radius, CP::Vec2.new(0.0, 0.0))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def mass amount
|
56
|
+
define_method :mass do
|
57
|
+
amount || 1.0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def moment amount
|
61
|
+
define_method :moment do
|
62
|
+
amount || 1.0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
def radius amount
|
66
|
+
define_method :radius do
|
67
|
+
amount || 10.0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def collision_type type
|
72
|
+
to_execute = lambda do |shape|
|
73
|
+
shape.collision_type = type
|
74
|
+
end
|
75
|
+
InitializerHooks.append self do
|
76
|
+
# Ensure @shape exists
|
77
|
+
#
|
78
|
+
InitializerHooks.append self.class, &to_execute unless @shape
|
79
|
+
to_execute[@shape]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def layer layer
|
84
|
+
InitializerHooks.register self do
|
85
|
+
self.layer = layer
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Plays a random sound of the given sounds.
|
90
|
+
#
|
91
|
+
def plays *paths
|
92
|
+
InitializerHooks.register self do
|
93
|
+
sound = Gosu::Sample.new self.window, File.join(Resources.root, paths[rand(paths.size)])
|
94
|
+
sound.play
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# Do something threaded.
|
101
|
+
#
|
102
|
+
# Default is: Instantly, in the next step.
|
103
|
+
#
|
104
|
+
def threaded time = 1, &code
|
105
|
+
self.window.threaded time, &code
|
106
|
+
end
|
107
|
+
|
108
|
+
# Destroy this thing.
|
109
|
+
#
|
110
|
+
attr_writer :destroyed
|
111
|
+
def destroyed?
|
112
|
+
@destroyed
|
113
|
+
end
|
114
|
+
def destroy!
|
115
|
+
return if self.destroyed?
|
116
|
+
self.window.unregister self
|
117
|
+
self.destroyed = true
|
118
|
+
end
|
119
|
+
|
120
|
+
# Some things you can only do every x time units.
|
121
|
+
#
|
122
|
+
# Example:
|
123
|
+
# sometimes :loading, self.frequency do
|
124
|
+
# projectile = self.shot.shoot_from self
|
125
|
+
# projectile.rotation = self.muzzle_rotation[target]
|
126
|
+
# projectile.speed = self.muzzle_velocity[target] * projectile.velocity
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
def sometimes variable, units = 1, &block
|
130
|
+
name = :"@#{variable}"
|
131
|
+
return if instance_variable_get(name)
|
132
|
+
instance_variable_set name, true
|
133
|
+
result = block.call
|
134
|
+
threaded units.to_i do
|
135
|
+
self.instance_variable_set name, false
|
136
|
+
end
|
137
|
+
result
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add this thing to a space.
|
141
|
+
#
|
142
|
+
# Note: Adds the body and the shape.
|
143
|
+
#
|
144
|
+
def add_to space
|
145
|
+
space.add_body @shape.body
|
146
|
+
space.add_shape @shape
|
147
|
+
end
|
148
|
+
|
149
|
+
# TODO
|
150
|
+
#
|
151
|
+
def draw
|
152
|
+
self.image.draw_rot self.position.x, self.position.y, self.layer, self.drawing_rotation
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
data/lib/waves.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
class Waves
|
4
|
+
|
5
|
+
#
|
6
|
+
#
|
7
|
+
def initialize window
|
8
|
+
@window = window
|
9
|
+
@waves = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def add amount, type, time
|
15
|
+
@waves[time] ||= []
|
16
|
+
@waves[time] << [amount, type]
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
#
|
21
|
+
def check time
|
22
|
+
if wave? time
|
23
|
+
types = @waves[time]
|
24
|
+
types.each { |amount, type| amount.times { @window.randomly_add type } }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def wave? time
|
31
|
+
!@waves[time].nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|