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,249 @@
1
+ # Extend this class for your game.
2
+ #
3
+ # Example:
4
+ # class MyGreatGame < GameWindow
5
+ #
6
+ class GameWindow < Gosu::Window
7
+
8
+ attr_reader :space, :font
9
+
10
+ def initialize
11
+ # set to true for fullscreen
12
+ super SCREEN_WIDTH, SCREEN_HEIGHT, true, 16
13
+
14
+ init
15
+ setup_battlefield
16
+ setup_objects
17
+ setup_collisions
18
+ end
19
+
20
+ def init
21
+ self.caption = "Incredible WWII battles!"
22
+
23
+ generate_landscape
24
+
25
+ @font = Gosu::Font.new self, Gosu::default_font_name, 20
26
+ @moveables = []
27
+ @controls = []
28
+ @remove_shapes = []
29
+ @players = []
30
+ @waves = Waves.new self
31
+ @scheduling = Scheduling.new
32
+ @step = 0
33
+ @dt = 1.0 / 60.0
34
+ end
35
+
36
+ def paint_hill
37
+ x, y = uniform_random_position
38
+ [80, 40, 20, 10].each do |radius|
39
+ circle x, y, radius, :fill => true, :color => [5.0/radius, 0.8, 5.0/radius, 1]
40
+ end
41
+ end
42
+
43
+ def uniform_random_position
44
+ [rand(SCREEN_WIDTH), rand(SCREEN_HEIGHT)]
45
+ end
46
+
47
+ def generate_landscape
48
+ @background_image = Gosu::Image.new self, 'media/battlefield.png', true
49
+ @background_image.paint do
50
+ 20.times { paint_hill }
51
+ end
52
+ end
53
+
54
+ def setup_battlefield
55
+ @battlefield = CP::Space.new
56
+ @battlefield.damping = 1.0 # 0.0 # full damping?
57
+ end
58
+
59
+ def imprint &block
60
+ @background_image.paint &block
61
+ end
62
+
63
+ def threaded time, code
64
+ @scheduling.add time, code
65
+ end
66
+
67
+ def randomly_add type
68
+ thing = type.new self
69
+
70
+ thing.warp_to SCREEN_WIDTH, rand*SCREEN_HEIGHT
71
+
72
+ register thing
73
+ end
74
+
75
+ def setup_objects
76
+ wave 10, Enemy, 100
77
+ wave 10, Enemy, 400
78
+ wave 10, Enemy, 700
79
+ wave 10, Enemy, 1000
80
+
81
+ # add_player_units
82
+ end
83
+
84
+ def wave amount, type, time
85
+ @waves.add amount, type, time
86
+ end
87
+
88
+ def small_explosion shape
89
+ explosion = SmallExplosion.new self
90
+ explosion.warp shape.body.p
91
+ remove shape
92
+ register explosion
93
+ end
94
+
95
+ def setup_collisions
96
+ # @space.add_collision_func :projectile, :projectile, &nil
97
+ # @space.add_collision_func :projectile, :enemy do |projectile_shape, enemy_shape|
98
+ # @moveables.each { |projectile| projectile.shape == projectile_shape && projectile.destroy }
99
+ # end
100
+ end
101
+
102
+ # Moveables register themselves here.
103
+ #
104
+ def register moveable
105
+ @moveables << moveable
106
+ moveable.add_to @battlefield
107
+ end
108
+
109
+ # Moveables unregister themselves here.
110
+ #
111
+ # Note: Use as follows in a Moveable.
112
+ #
113
+ # def destroy
114
+ # threaded do
115
+ # 5.times { sleep 0.1; animate_explosion }
116
+ # @window.unregister self
117
+ # end
118
+ # end
119
+ #
120
+ def unregister moveable
121
+ remove moveable.shape
122
+ end
123
+
124
+ # Remove this shape the next turn.
125
+ #
126
+ # Note: Internal use. Use unregister to properly remove a moveable.
127
+ #
128
+ def remove shape
129
+ @remove_shapes << shape
130
+ end
131
+
132
+ # # Adds the first player.
133
+ # #
134
+ # def add_admiral
135
+ # @player1 = Cruiser.new self, 0x99ff0000
136
+ # @player1.warp_to 400, 320
137
+ #
138
+ # @controls << Controls.new(self, @player1,
139
+ # Gosu::Button::KbA => :left,
140
+ # Gosu::Button::KbD => :right,
141
+ # Gosu::Button::KbW => :full_speed_ahead,
142
+ # Gosu::Button::KbS => :reverse,
143
+ # Gosu::Button::Kb1 => :revive
144
+ # )
145
+ #
146
+ # @players << @player1
147
+ #
148
+ # register @player1
149
+ # end
150
+
151
+ def remove_collided
152
+ # This iterator makes an assumption of one Shape per Star making it safe to remove
153
+ # each Shape's Body as it comes up
154
+ # If our Stars had multiple Shapes, as would be required if we were to meticulously
155
+ # define their true boundaries, we couldn't do this as we would remove the Body
156
+ # multiple times
157
+ # We would probably solve this by creating a separate @remove_bodies array to remove the Bodies
158
+ # of the Stars that were gathered by the Player
159
+ #
160
+ @remove_shapes.each do |shape|
161
+ @space.remove_body shape.body
162
+ @space.remove_shape shape
163
+ @moveables.delete_if { |moveable| moveable.shape == shape && moveable.destroy }
164
+ end
165
+ @remove_shapes.clear
166
+ end
167
+
168
+ def handle_input
169
+ @controls.each &:handle
170
+ end
171
+
172
+ def reset_forces
173
+ # When a force or torque is set on a Body, it is cumulative
174
+ # This means that the force you applied last SUBSTEP will compound with the
175
+ # force applied this SUBSTEP; which is probably not the behavior you want
176
+ # We reset the forces on the Player each SUBSTEP for this reason
177
+ #
178
+ # @player1.shape.body.reset_forces
179
+ # @player2.shape.body.reset_forces
180
+ # @player3.shape.body.reset_forces
181
+ # @players.each { |player| player.shape.body.reset_forces }
182
+ end
183
+
184
+ # Checks whether
185
+ #
186
+ def validate
187
+ @moveables.each &:validate_position
188
+ end
189
+
190
+ def step_once
191
+ # Perform the step over @dt period of time
192
+ # For best performance @dt should remain consistent for the game
193
+ @battlefield.step @dt
194
+ end
195
+
196
+ def targeting
197
+ @moveables.select { |m| m.respond_to? :target }.each do |gun|
198
+ gun.target *@moveables.select { |m| m.kind_of? Enemy }
199
+ end
200
+ end
201
+
202
+ # def revive player
203
+ # return if @moveables.find { |moveable| moveable == player }
204
+ # register player
205
+ # end
206
+
207
+ #
208
+ #
209
+ def update
210
+ @step += 1
211
+ # Step the physics environment SUBSTEPS times each update.
212
+ #
213
+ SUBSTEPS.times do
214
+ remove_collided
215
+ reset_forces
216
+ validate
217
+ targeting
218
+ handle_input
219
+ step_once
220
+ end
221
+ @waves.check @step
222
+ @scheduling.step
223
+ end
224
+
225
+ def draw_background
226
+ @background_image.draw 0, 0, ZOrder::Background, 1.5, 1.2
227
+ end
228
+
229
+ def draw_moveables
230
+ @moveables.each &:draw
231
+ end
232
+
233
+ def draw_ui
234
+ # @font.draw "P1 Score: ", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffff0000
235
+ end
236
+
237
+ def draw
238
+ draw_background
239
+ draw_moveables
240
+ draw_ui
241
+ end
242
+
243
+ # Escape exits.
244
+ #
245
+ def button_down id
246
+ close if id == Gosu::Button::KbEscape
247
+ end
248
+
249
+ end
data/lib/resources.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Resources
2
+
3
+ mattr_accessor :root
4
+
5
+ end
data/lib/scheduling.rb ADDED
@@ -0,0 +1,48 @@
1
+ # "Threading"
2
+ # A hash with time => [block, block, block ...]
3
+ #
4
+ # {
5
+ # 100 => [{bla}, {blu}, {bli}],
6
+ # 1 => [{eek}]
7
+ # }
8
+ #
9
+ # When calling threading.step, eek will be executed, the others will be one step closer to zero, 99.
10
+ #
11
+ class Scheduling
12
+
13
+ def initialize
14
+ @threads = []
15
+ end
16
+
17
+ # Adds a code block at time time.
18
+ #
19
+ def add time = 1, &code
20
+ @threads << [time, code]
21
+ end
22
+
23
+ # Does two things:
24
+ # 1. Move one step in time.
25
+ # 2. Execute all blocks with time 0.
26
+ #
27
+ # TODO Rewrite to be faster.
28
+ #
29
+ # FIXME - threads added while threads are handled!
30
+ #
31
+ def step
32
+ @threads.collect! do |time, code|
33
+ if time == 1
34
+ code.call
35
+ nil
36
+ else
37
+ [time-1, code]
38
+ end
39
+ end.compact!
40
+ end
41
+
42
+ # Call all given blocks.
43
+ #
44
+ def execute codes
45
+ codes.each &:[]
46
+ end
47
+
48
+ end
@@ -0,0 +1,12 @@
1
+ # An Attachable can be attached to a Pod.
2
+ #
3
+ module Attachable
4
+
5
+ attr_accessor :relative_position
6
+
7
+ def move_relative pod
8
+ self.position = pod.position + relative_position # pod.rotation
9
+ self.rotation = pod.rotation
10
+ end
11
+
12
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ #
3
+ module Controllable
4
+
5
+ def self.included controllable
6
+ controllable.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ # TODO alternate controls handling!
12
+ #
13
+
14
+ def controls mapping
15
+ attr_accessor :controls_mapping
16
+ hook = lambda do
17
+ if self.controls_mapping
18
+ # primary controls taken, use alternate controls
19
+ self.controls_mapping = self.alternate_controls_mapping if self.respond_to? :alternate_controls_mapping
20
+ else
21
+ self.controls_mapping = mapping
22
+ end
23
+ self.window.add_controls_for self
24
+ end
25
+ InitializerHooks.register self, &hook
26
+ end
27
+
28
+ def alternate_controls mapping
29
+ attr_accessor :alternate_controls_mapping
30
+ hook = lambda do
31
+ if self.controls_mapping
32
+ # primary controls taken, use alternate controls
33
+ self.controls_mapping = self.alternate_controls_mapping if self.respond_to? :alternate_controls_mapping
34
+ else
35
+ self.controls_mapping = mapping
36
+ end
37
+ end
38
+ InitializerHooks.register self, &hook
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,16 @@
1
+ # Use this for a thing that damages.
2
+ #
3
+ # Example:
4
+ # class Rocket < ShortLived
5
+ # it_is Damaging
6
+ # end
7
+ #
8
+ module Damaging
9
+
10
+ mattr_accessor :damage
11
+
12
+ def damage
13
+ @@damage
14
+ end
15
+
16
+ end
@@ -0,0 +1,46 @@
1
+ module Generator
2
+
3
+ def self.included base
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ def generates klass, options = {}
10
+ self.send :include, InstanceMethods
11
+
12
+ rate = options[:every]
13
+ til = options[:until] || 100
14
+ offset = options[:starting_at] || 1
15
+
16
+ InitializerHooks.register self do
17
+ start_generating klass, rate, til, offset
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ module InstanceMethods
25
+
26
+ def start_generating klass, every_rate, til, offset
27
+ return if til <= 0
28
+ threaded offset, &generation(klass, every_rate, til)
29
+ end
30
+
31
+ def generation klass, every_rate, til
32
+ lambda do
33
+ self.generate klass
34
+ self.start_generating klass, every_rate, til - every_rate, every_rate
35
+ end
36
+ end
37
+
38
+ def generate klass
39
+ generated = klass.new self.window
40
+ generated.warp self.position
41
+ self.window.register generated
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,34 @@
1
+ # def initialize
2
+ # after_initialize
3
+ # end
4
+ #
5
+ module InitializerHooks
6
+
7
+ mattr_accessor :hooks # { class => [blocks] }
8
+ self.hooks = {}
9
+
10
+ # Calls the hooks in order of registration.
11
+ #
12
+ def after_initialize
13
+ hooks = InitializerHooks.hooks[self.class]
14
+ hooks && hooks.each do |hook|
15
+ self.instance_eval &hook
16
+ end
17
+ end
18
+
19
+ # Registers a hook for a class.
20
+ #
21
+ def self.register klass, &hook
22
+ self.hooks[klass] ||= []
23
+ self.hooks[klass] << hook
24
+ end
25
+
26
+ def self.prepend klass, &hook
27
+ self.hooks[klass] ||= []
28
+ self.hooks[klass].unshift hook
29
+ end
30
+ def self.append klass, &hook
31
+ self.register klass, &hook
32
+ end
33
+
34
+ end
@@ -0,0 +1,47 @@
1
+ # Include helpers. Multiple Modules (Traits) can be named.
2
+ #
3
+ # Examples:
4
+ # * it_is_a Targetable, Accelerateable
5
+ # * it_is Targeting::Closest
6
+ #
7
+ module ItIsA
8
+
9
+ manual <<-MANUAL
10
+ Defines:
11
+ it_is <some trait>
12
+ it_is_a <some trait>
13
+ it_has <some trait>
14
+
15
+ Example:
16
+ it_is Controllable
17
+ it_is_a Generator
18
+ it_has Lives
19
+ MANUAL
20
+
21
+ def self.included traitable_class
22
+ traitable_class.extend ClassMethods
23
+ end
24
+
25
+ module ClassMethods
26
+
27
+ #
28
+ # Examples:
29
+ # * it_is_a Shooter
30
+ # * it_is_a Shooter do
31
+ # frequency 10
32
+ # …
33
+ # end
34
+ # * it_is Controllable, Turnable do
35
+ # …
36
+ # end
37
+ #
38
+ def it_is *traits, &block
39
+ traits.each { |trait| include trait }
40
+ instance_eval &block if block_given?
41
+ end
42
+ alias it_is_a it_is
43
+ alias it_has it_is
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,59 @@
1
+ # A thing is destroyed if a number of lives has been passed.
2
+ #
3
+ module Lives
4
+
5
+ # TODO def revive!
6
+ #
7
+
8
+ # Prints an amount of information on these capabilities.
9
+ #
10
+ manual <<-MANUAL
11
+ Defines:
12
+ lives <some trait>
13
+
14
+ Example:
15
+ lives 10
16
+
17
+ Call kill! to remove a live. Override killed! to exhibit behaviour.
18
+ MANUAL
19
+
20
+ def self.included target_class
21
+ target_class.extend IncludeMethods
22
+ end
23
+
24
+ module IncludeMethods
25
+
26
+ # Define the amount of lives in the class.
27
+ #
28
+ def lives amount
29
+ include InstanceMethods
30
+ class_inheritable_accessor :prototype_lives
31
+ self.prototype_lives = amount
32
+
33
+ hook = lambda { self.lives = self.class.prototype_lives }
34
+ InitializerHooks.register self, &hook
35
+ end
36
+
37
+ end
38
+
39
+ module InstanceMethods
40
+
41
+ attr_accessor :lives
42
+
43
+ # Does three things:
44
+ # * Deduct 1 live.
45
+ # * Check to see if the amount is 0.
46
+ # * Calls #destroy! if yes.
47
+ #
48
+ def killed!
49
+
50
+ end
51
+ def kill!
52
+ self.lives -= 1
53
+ killed!
54
+ destroy! if self.lives == 0
55
+ end
56
+
57
+ end
58
+
59
+ end
data/lib/traits/pod.rb ADDED
@@ -0,0 +1,95 @@
1
+ # A thing that is a pod can have attachments.
2
+ #
3
+ #
4
+ #
5
+ #
6
+ module Pod
7
+
8
+ manual <<-MANUAL
9
+ Defines:
10
+ attach <class>, x_pos, y_pos
11
+
12
+ Example:
13
+ class Battleship
14
+ attach Cannon, 10, 20
15
+ MANUAL
16
+
17
+ def self.included target_class
18
+ target_class.extend IncludeMethods
19
+ target_class.holds_attachments
20
+ end
21
+
22
+ # TODO is_a Rack
23
+ #
24
+ # TODO Maybe
25
+ # is_a Rack.
26
+ # with(Cannon, 10, 10).
27
+ # with(Cannon, 20, 10)
28
+ #
29
+ module IncludeMethods
30
+
31
+ def holds_attachments
32
+ include InstanceMethods
33
+ alias_method_chain :move, :attachments
34
+
35
+ class_inheritable_accessor :prototype_attachments
36
+ extend ClassMethods
37
+ hook = lambda do
38
+ self.class.prototype_attachments.each do |type, x, y|
39
+ attach type.new(self.window), x, y
40
+ end
41
+ end
42
+ InitializerHooks.append self, &hook
43
+ end
44
+
45
+ end
46
+
47
+ module ClassMethods
48
+
49
+ # Example:
50
+ # class MotherShip
51
+ # is_a Pod
52
+ # attach Cannon, 10, 10
53
+ # attach Cannon, 20, 10
54
+ # attach Cannon, 30, 10
55
+ #
56
+ def attach type, x, y
57
+ self.prototype_attachments ||= []
58
+ self.prototype_attachments << [type, x, y]
59
+ end
60
+
61
+ end
62
+
63
+ module InstanceMethods
64
+
65
+ attr_accessor :attachments
66
+
67
+ #
68
+ #
69
+ def attach attachment, x, y
70
+ self.attachments ||= []
71
+ attachment.extend Attachable # This is where Ruby shines.
72
+ window.register attachment
73
+ # attachment.rotation = self.rotation
74
+ attachment.relative_position = CP::Vec2.new x, y
75
+ self.attachments << attachment
76
+ end
77
+
78
+ #
79
+ #
80
+ def move_attachments
81
+ self.attachments.each do |attachment|
82
+ attachment.move_relative self
83
+ end
84
+ end
85
+
86
+ #
87
+ #
88
+ def move_with_attachments
89
+ move_attachments
90
+ move_without_attachments
91
+ end
92
+
93
+ end
94
+
95
+ end