gosu_extensions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,16 @@
1
+ module Shot
2
+
3
+ #
4
+ #
5
+ attr_accessor :velocity, :originator
6
+
7
+ #
8
+ #
9
+ def shoot_from shooter
10
+ self.position = shooter.muzzle_position
11
+ self.originator = shooter
12
+ self.window.register self
13
+ self
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+ # Any object that is targetable.
2
+ #
3
+ module Targetable
4
+
5
+ # Distance from the potential shooter.
6
+ #
7
+ def distance_from shooter
8
+ (self.position - shooter.position).length
9
+ end
10
+
11
+ end
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ #
2
+ #
3
+ module VectorUtilities
4
+
5
+ # Return a random vector with a given strength.
6
+ #
7
+ def random_vector strength = 1.0
8
+ CP::Vec2.new(rand-0.5, rand-0.5).normalize! * strength
9
+ end
10
+
11
+ 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