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
@@ -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
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,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,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
|
data/lib/traits/lives.rb
ADDED
@@ -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
|