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,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