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.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "gosu_extensions"
5
+ gemspec.summary = ""
6
+ gemspec.email = "florian.hanke@gmail.com"
7
+ gemspec.homepage = "http://www.github.com/floere/gosu_extensions"
8
+ gemspec.description = ""
9
+ gemspec.authors = ["Florian Hanke"]
10
+ gemspec.rdoc_options = ["--inline-source", "--charset=UTF-8"]
11
+ gemspec.files = FileList["[A-Z]*", "{generators,lib,rails,spec}/**/*"]
12
+ gemspec.add_dependency 'gosu'
13
+ gemspec.add_dependency 'chipmunk'
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError => e
17
+ puts "Jeweler not available (#{e}). Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/controls.rb ADDED
@@ -0,0 +1,35 @@
1
+ # Controls for a controllable.
2
+ # Example:
3
+ # # Note: left, right, full_speed_ahead, reverse, revive are
4
+ # # methods on the @player.
5
+ # #
6
+ # @controls << Controls.new(self, @player,
7
+ # Gosu::Button::KbA => :left,
8
+ # Gosu::Button::KbD => :right,
9
+ # Gosu::Button::KbW => :full_speed_ahead,
10
+ # Gosu::Button::KbS => :reverse,
11
+ # Gosu::Button::Kb1 => :revive
12
+ # )
13
+ #
14
+ #
15
+ #
16
+ class Controls
17
+
18
+ #
19
+ #
20
+ def initialize window, controllable
21
+ @window = window
22
+ @controllable = controllable
23
+ @mapping = controllable.controls_mapping
24
+ end
25
+
26
+ #
27
+ #
28
+ def handle
29
+ return if @controllable.destroyed?
30
+ @mapping.each do |key, command|
31
+ @controllable.send(command) if @window.button_down? key
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,15 @@
1
+ class Module
2
+
3
+ def manual text
4
+ metaclass.send :define_method, :manual! do
5
+ puts <<-MANUAL
6
+ MANUAL FOR #{self}
7
+ #{text}
8
+ Change #{self}.manual! -> #{self}, to not show the manual anymore.
9
+
10
+ MANUAL
11
+ self
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,9 @@
1
+ class Numeric
2
+
3
+ # Convenience method for converting from radians to a Vec2 vector.
4
+ #
5
+ def radians_to_vec2
6
+ CP::Vec2.new Math::cos(self), Math::sin(self)
7
+ end
8
+
9
+ end
@@ -0,0 +1,381 @@
1
+ # Extend this class for your game.
2
+ #
3
+ # Example:
4
+ # class MyGreatGame < GameWindow
5
+ #
6
+ class GameWindow < Gosu::Window
7
+
8
+ include InitializerHooks
9
+
10
+ attr_writer :full_screen,
11
+ :font_name,
12
+ :font_size,
13
+ :damping,
14
+ :caption,
15
+ :screen_width,
16
+ :screen_height,
17
+ :gravity
18
+ attr_reader :environment,
19
+ :moveables,
20
+ :font
21
+ attr_accessor :background_path,
22
+ :background_hard_borders
23
+
24
+ def initialize
25
+ after_initialize
26
+
27
+ super self.screen_width, self.screen_height, self.full_screen, 16
28
+
29
+ setup_window
30
+ setup_background
31
+ setup_containers
32
+ setup_steps
33
+ setup_waves
34
+ setup_scheduling
35
+ setup_font
36
+
37
+ setup_environment
38
+ setup_enemies
39
+ setup_players
40
+ setup_collisions
41
+ end
42
+
43
+ def media_path
44
+ @media_path || 'media'
45
+ end
46
+ def full_screen
47
+ @full_screen || false
48
+ end
49
+ def font_name
50
+ @font_name || Gosu::default_font_name
51
+ end
52
+ def font_size
53
+ @font_size || 20
54
+ end
55
+ def damping
56
+ @damping || 0.001
57
+ end
58
+ def caption
59
+ @caption || ""
60
+ end
61
+ def screen_width
62
+ @screen_width || DEFAULT_SCREEN_WIDTH
63
+ end
64
+ def screen_height
65
+ @screen_height || DEFAULT_SCREEN_HEIGHT
66
+ end
67
+ def gravity_vector
68
+ @gravity || @gravity = CP::Vec2.new(0, 0.98/SUBSTEPS)
69
+ end
70
+
71
+ class << self
72
+ def gravity amount = 0.98
73
+ InitializerHooks.register self do
74
+ self.gravity = CP::Vec2.new 0, amount.to_f/SUBSTEPS
75
+ end
76
+ end
77
+ def width value = DEFAULT_SCREEN_WIDTH
78
+ InitializerHooks.register self do
79
+ self.screen_width = value
80
+ end
81
+ end
82
+ def height value = DEFAULT_SCREEN_HEIGHT
83
+ InitializerHooks.register self do
84
+ self.screen_height = value
85
+ end
86
+ end
87
+ def caption text = ""
88
+ InitializerHooks.register self do
89
+ self.caption = text
90
+ end
91
+ end
92
+ def damping amount = 0.0
93
+ InitializerHooks.register self do
94
+ self.damping = amount
95
+ end
96
+ end
97
+ def font name = Gosu::default_font_name, size = 20
98
+ InitializerHooks.register self do
99
+ self.font_name = name
100
+ self.font_size = size
101
+ end
102
+ end
103
+ def background path, options = {}
104
+ InitializerHooks.register self do
105
+ self.background_path = path
106
+ self.background_hard_borders = options[:hard_borders] || false
107
+ end
108
+ end
109
+ def full_screen
110
+ InitializerHooks.register self do
111
+ self.full_screen = true
112
+ end
113
+ end
114
+ def collisions &block
115
+ raise "collisions are defined in a block" unless block_given?
116
+ InitializerHooks.register self do
117
+ @collision_definitions = block
118
+ end
119
+ end
120
+ end
121
+
122
+ def setup_window
123
+ self.caption = self.class.caption || ""
124
+ end
125
+ def setup_background
126
+ @background_image = Gosu::Image.new self, File.join(Resources.root, self.background_path), self.background_hard_borders
127
+ end
128
+ def setup_containers
129
+ @moveables = []
130
+ @controls = []
131
+ @remove_shapes = []
132
+ @players = []
133
+ end
134
+ def setup_steps
135
+ @step = 0
136
+ @dt = 1.0 / 60.0
137
+ end
138
+ def setup_waves
139
+ @waves = Waves.new self
140
+ end
141
+ def setup_scheduling
142
+ @scheduling = Scheduling.new
143
+ end
144
+ def setup_font
145
+ @font = Gosu::Font.new self, self.font_name, self.font_size
146
+ end
147
+ def setup_environment
148
+ @environment = CP::Space.new
149
+ class << @environment
150
+ attr_accessor :window
151
+ end
152
+ @environment.window = self
153
+ @environment.damping = -self.damping + 1 # recalculate the damping such that 0.0 has no damping.
154
+ end
155
+
156
+ # Override.
157
+ #
158
+ def setup_players; end
159
+ def setup_enemies; end
160
+
161
+ #
162
+ #
163
+ # Example:
164
+ # collisions do
165
+ # add_collision_func ...
166
+ #
167
+ def setup_collisions
168
+ @environment.instance_eval &@collision_definitions
169
+ end
170
+
171
+ # Add controls for a player.
172
+ #
173
+ # Example:
174
+ # add_controls_for @player1, Gosu::Button::KbA => :left,
175
+ # Gosu::Button::KbD => :right,
176
+ # Gosu::Button::KbW => :full_speed_ahead,
177
+ # Gosu::Button::KbS => :reverse,
178
+ # Gosu::Button::Kb1 => :revive
179
+ #
180
+ def add_controls_for object
181
+ @controls << Controls.new(self, object)
182
+ end
183
+
184
+
185
+ # Core methods used by the extensions "framework"
186
+ #
187
+
188
+ # The main loop.
189
+ #
190
+ # TODO implement hooks.
191
+ #
192
+ def update
193
+ @step += 1
194
+ # Step the physics environment SUBSTEPS times each update.
195
+ #
196
+ SUBSTEPS.times do
197
+ remove_shapes!
198
+ reset_forces
199
+ move_all
200
+ targeting
201
+ handle_input
202
+ step_once
203
+ end
204
+ @waves.check @step
205
+ @scheduling.step
206
+ end
207
+ # Each step, this is called to handle any input.
208
+ #
209
+ def handle_input
210
+ @controls.each &:handle
211
+ end
212
+ # Does a single step.
213
+ #
214
+ def step_once
215
+ # Perform the step over @dt period of time
216
+ # For best performance @dt should remain consistent for the game
217
+ @environment.step @dt
218
+ end
219
+
220
+ # Things unregister themselves here.
221
+ #
222
+ # Note: Use as follows in a Thing.
223
+ #
224
+ # def destroy
225
+ # threaded do
226
+ # 5.times { sleep 0.1; animate_explosion }
227
+ # @window.unregister self
228
+ # end
229
+ # end
230
+ #
231
+ def unregister thing
232
+ remove thing.shape
233
+ end
234
+
235
+ # Remove this shape the next turn.
236
+ #
237
+ # Note: Internal use. Use unregister to properly remove a moveable.
238
+ #
239
+ def remove shape
240
+ @remove_shapes << shape
241
+ end
242
+
243
+ # Run some code at relative time <time>.
244
+ #
245
+ # Example:
246
+ # # Will destroy the object that calls this method
247
+ # # in 20 steps.
248
+ # #
249
+ # window.threaded 20 do
250
+ # destroy!
251
+ # end
252
+ #
253
+ def threaded time = 1, &code
254
+ @scheduling.add time, &code
255
+ end
256
+
257
+ # Moves each moveable.
258
+ #
259
+ def move_all
260
+ @moveables.each &:move
261
+ end
262
+
263
+ # Handles the targeting process.
264
+ #
265
+ def targeting
266
+ @moveables.select { |m| m.respond_to? :target }.each do |gun|
267
+ gun.target *@moveables.select { |m| m.kind_of? Enemy }
268
+ end
269
+ end
270
+
271
+
272
+
273
+
274
+ # Utility Methods
275
+ #
276
+
277
+ #
278
+ #
279
+ # Example:
280
+ # * x, y = uniform_random_position
281
+ #
282
+ def uniform_random_position
283
+ [rand(self.width), rand(self.height)]
284
+ end
285
+
286
+ #
287
+ #
288
+ # Example:
289
+ # imprint do
290
+ # circle x, y, radius, :fill => true, :color => :black
291
+ # end
292
+ #
293
+ def imprint &block
294
+ @background_image.paint &block
295
+ end
296
+
297
+ # Randomly adds a Thing to a uniform random position.
298
+ #
299
+ def randomly_add type
300
+ thing = type.new self
301
+ thing.warp_to *uniform_random_position
302
+ register thing
303
+ end
304
+
305
+ # Moveables register themselves here.
306
+ #
307
+ def register moveable
308
+ @moveables << moveable
309
+ moveable.add_to @environment
310
+ end
311
+
312
+ def remove_shapes!
313
+ # This iterator makes an assumption of one Shape per Star making it safe to remove
314
+ # each Shape's Body as it comes up
315
+ # If our Stars had multiple Shapes, as would be required if we were to meticulously
316
+ # define their true boundaries, we couldn't do this as we would remove the Body
317
+ # multiple times
318
+ # We would probably solve this by creating a separate @remove_bodies array to remove the Bodies
319
+ # of the Stars that were gathered by the Player
320
+ #
321
+ # p @remove_shapes unless @remove_shapes.empty?
322
+ @remove_shapes.each do |shape|
323
+ @environment.remove_body shape.body
324
+ @environment.remove_shape shape
325
+ @moveables.delete_if { |moveable| moveable.shape == shape }
326
+ end
327
+ @remove_shapes.clear
328
+ end
329
+
330
+ def reset_forces
331
+ # When a force or torque is set on a Body, it is cumulative
332
+ # This means that the force you applied last SUBSTEP will compound with the
333
+ # force applied this SUBSTEP; which is probably not the behavior you want
334
+ # We reset the forces on the Player each SUBSTEP for this reason
335
+ #
336
+ # @player1.shape.body.reset_forces
337
+ # @player2.shape.body.reset_forces
338
+ # @player3.shape.body.reset_forces
339
+ # @players.each { |player| player.shape.body.reset_forces }
340
+ end
341
+
342
+ # def revive player
343
+ # return if @moveables.find { |moveable| moveable == player }
344
+ # register player
345
+ # end
346
+
347
+ # Drawing methods
348
+ #
349
+
350
+ def draw
351
+ draw_background
352
+ draw_ambient
353
+ draw_moveables
354
+ draw_ui
355
+ end
356
+ def draw_background
357
+ @background_image.draw 0, 0, Layer::Background, 1.0, 1.0 if @background_image
358
+ end
359
+ def draw_ambient
360
+
361
+ end
362
+ def draw_moveables
363
+ @moveables.each &:draw
364
+ end
365
+ def draw_ui
366
+ # @font.draw "P1 Score: ", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffff0000
367
+ end
368
+
369
+ # Escape exits by default.
370
+ #
371
+ def button_down id
372
+ close if exit?(id)
373
+ end
374
+
375
+ # Override exit? if you want to define another exit rule.
376
+ #
377
+ def exit? id = nil
378
+ id == Gosu::Button::KbEscape
379
+ end
380
+
381
+ end
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ # require 'texplay'
4
+ begin
5
+ require 'gosu'
6
+ rescue LoadError => e
7
+ puts "Couldn't find gem gosu. Install using:\n\nsudo gem install gosu.\n\n"
8
+ raise e
9
+ end
10
+ begin
11
+ require 'chipmunk' # A physics framework.
12
+ rescue LoadError => e
13
+ puts "Couldn't find gem chipmunk. Install using:\n\nsudo gem install chipmunk.\n\n"
14
+ raise e
15
+ end
16
+
17
+ require 'resources'
18
+ require 'vector_utilities'
19
+
20
+ $:.unshift File.join(File.dirname(__FILE__), '/extensions')
21
+ require 'module'
22
+ require 'numeric'
23
+
24
+ $:.unshift File.join(File.dirname(__FILE__), '/traits')
25
+ require 'it_is_a'
26
+ require 'pod'
27
+ require 'attachable'
28
+ require 'damaging'
29
+ require 'generator'
30
+ require 'initializer_hooks'
31
+ require 'lives'
32
+ require 'targeting'
33
+ require 'targeting/closest'
34
+ require 'shooter'
35
+ require 'shot'
36
+ require 'targetable'
37
+ require 'turnable'
38
+ require 'controllable'
39
+
40
+ $:.unshift File.join(File.dirname(__FILE__), '/units')
41
+ require 'thing'
42
+ require 'moveable'
43
+ require 'short_lived'
44
+
45
+ require 'controls'
46
+ require 'game_window'
47
+ require 'scheduling'
48
+ require 'waves'
49
+ require 'layer'
50
+
51
+ DEFAULT_SCREEN_WIDTH = 1200
52
+ DEFAULT_SCREEN_HEIGHT = 700
53
+ SUBSTEPS = 10
data/lib/layer.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Layering of sprites.
2
+ #
3
+ # 4 Layers:
4
+ # * UI
5
+ # * Players (Controlled and AI Elements)
6
+ # * Ambient (Moveable Background)
7
+ # * Background (Fixed, true Background)
8
+ #
9
+ module Layer
10
+ Background, Ambient, Players, UI = 0, 1, 2, 3
11
+ end