gosu_extensions 0.1.0

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.
@@ -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