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 +18 -0
- data/VERSION +1 -0
- data/lib/controls.rb +35 -0
- data/lib/extensions/module.rb +15 -0
- data/lib/extensions/numeric.rb +9 -0
- data/lib/game_window.rb +381 -0
- data/lib/gosu_extensions.rb +53 -0
- data/lib/layer.rb +11 -0
- data/lib/old_game_window.rb +249 -0
- data/lib/resources.rb +5 -0
- data/lib/scheduling.rb +48 -0
- data/lib/traits/attachable.rb +12 -0
- data/lib/traits/controllable.rb +43 -0
- data/lib/traits/damaging.rb +16 -0
- data/lib/traits/generator.rb +46 -0
- data/lib/traits/initializer_hooks.rb +34 -0
- data/lib/traits/it_is_a.rb +47 -0
- data/lib/traits/lives.rb +59 -0
- data/lib/traits/pod.rb +95 -0
- data/lib/traits/shooter.rb +113 -0
- data/lib/traits/shot.rb +16 -0
- data/lib/traits/targetable.rb +11 -0
- data/lib/traits/targeting/closest.rb +26 -0
- data/lib/traits/targeting.rb +1 -0
- data/lib/traits/turnable.rb +41 -0
- data/lib/units/moveable.rb +196 -0
- data/lib/units/short_lived.rb +20 -0
- data/lib/units/thing.rb +155 -0
- data/lib/vector_utilities.rb +11 -0
- data/lib/waves.rb +34 -0
- metadata +115 -0
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
|
data/lib/game_window.rb
ADDED
@@ -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