cyberarm_engine 0.13.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -8
  3. data/.rubocop.yml +8 -0
  4. data/.travis.yml +5 -5
  5. data/Gemfile +6 -6
  6. data/LICENSE.txt +21 -21
  7. data/README.md +73 -43
  8. data/Rakefile +10 -10
  9. data/assets/textures/default.png +0 -0
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/cyberarm_engine.gemspec +39 -36
  13. data/lib/cyberarm_engine.rb +64 -47
  14. data/lib/cyberarm_engine/animator.rb +56 -54
  15. data/lib/cyberarm_engine/background.rb +179 -175
  16. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  17. data/lib/cyberarm_engine/bounding_box.rb +150 -150
  18. data/lib/cyberarm_engine/cache.rb +4 -0
  19. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  20. data/lib/cyberarm_engine/common.rb +96 -96
  21. data/lib/cyberarm_engine/config_file.rb +46 -0
  22. data/lib/cyberarm_engine/game_object.rb +248 -257
  23. data/lib/cyberarm_engine/game_state.rb +92 -89
  24. data/lib/cyberarm_engine/model.rb +207 -0
  25. data/lib/cyberarm_engine/model/material.rb +21 -0
  26. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  27. data/lib/cyberarm_engine/model/parser.rb +74 -0
  28. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  29. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  30. data/lib/cyberarm_engine/model_cache.rb +31 -0
  31. data/lib/cyberarm_engine/opengl.rb +28 -0
  32. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  33. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  34. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  35. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  36. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  37. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  38. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  39. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  40. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  41. data/lib/cyberarm_engine/ray.rb +56 -56
  42. data/lib/cyberarm_engine/stats.rb +21 -0
  43. data/lib/cyberarm_engine/text.rb +160 -146
  44. data/lib/cyberarm_engine/timer.rb +23 -23
  45. data/lib/cyberarm_engine/transform.rb +296 -273
  46. data/lib/cyberarm_engine/ui/border_canvas.rb +102 -101
  47. data/lib/cyberarm_engine/ui/dsl.rb +138 -99
  48. data/lib/cyberarm_engine/ui/element.rb +315 -276
  49. data/lib/cyberarm_engine/ui/elements/button.rb +160 -67
  50. data/lib/cyberarm_engine/ui/elements/check_box.rb +51 -59
  51. data/lib/cyberarm_engine/ui/elements/container.rb +256 -176
  52. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  53. data/lib/cyberarm_engine/ui/elements/edit_line.rb +262 -172
  54. data/lib/cyberarm_engine/ui/elements/flow.rb +15 -17
  55. data/lib/cyberarm_engine/ui/elements/image.rb +72 -52
  56. data/lib/cyberarm_engine/ui/elements/label.rb +156 -50
  57. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -0
  58. data/lib/cyberarm_engine/ui/elements/progress.rb +51 -50
  59. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  60. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  61. data/lib/cyberarm_engine/ui/elements/stack.rb +11 -13
  62. data/lib/cyberarm_engine/ui/elements/text_block.rb +156 -0
  63. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +65 -56
  64. data/lib/cyberarm_engine/ui/event.rb +47 -47
  65. data/lib/cyberarm_engine/ui/gui_state.rb +226 -135
  66. data/lib/cyberarm_engine/ui/style.rb +38 -37
  67. data/lib/cyberarm_engine/ui/theme.rb +182 -120
  68. data/lib/cyberarm_engine/vector.rb +293 -203
  69. data/lib/cyberarm_engine/version.rb +4 -4
  70. data/lib/cyberarm_engine/{engine.rb → window.rb} +114 -101
  71. metadata +88 -18
  72. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
  73. data/lib/cyberarm_engine/shader.rb +0 -262
@@ -1,96 +1,96 @@
1
- module CyberarmEngine
2
- module Common
3
- def push_state(klass, options={})
4
- window.push_state(klass, options)
5
- end
6
-
7
- def current_state
8
- window.current_state
9
- end
10
-
11
- def previous_state
12
- window.previous_state
13
- end
14
-
15
- def pop_state
16
- window.pop_state
17
- end
18
-
19
- def show_cursor
20
- window.show_cursor
21
- end
22
-
23
- def show_cursor=boolean
24
- window.show_cursor = boolean
25
- end
26
-
27
- def draw_rect(x, y, width, height, color, z = 0)
28
- Gosu.draw_rect(x, y, width, height, color, z)
29
- end
30
-
31
- def fill(color, z = 0)
32
- draw_rect(0, 0, window.width, window.height, color, z)
33
- end
34
-
35
- def lighten(color, amount = 25)
36
- if defined?(color.alpha)
37
- return Gosu::Color.rgba(color.red + amount, color.green + amount, color.blue + amount, color.alpha)
38
- else
39
- return Gosu::Color.rgb(color.red + amount, color.green + amount, color.blue + amount)
40
- end
41
- end
42
-
43
- def darken(color, amount = 25)
44
- if defined?(color.alpha)
45
- return Gosu::Color.rgba(color.red - amount, color.green - amount, color.blue - amount, color.alpha)
46
- else
47
- return Gosu::Color.rgb(color.red - amount, color.green - amount, color.blue - amount)
48
- end
49
- end
50
-
51
- def opacity(color, ratio = 1.0)
52
- alpha = 255 * ratio
53
-
54
- return Gosu::Color.rgba(color.red, color.green, color.blue, alpha)
55
- end
56
-
57
- def get_asset(path, hash, klass, retro = false, tileable = false)
58
- asset = nil
59
- hash.detect do |_asset, instance|
60
- if _asset == path
61
- asset = instance
62
- true
63
- end
64
- end
65
-
66
- unless asset
67
- instance = nil
68
- if klass == Gosu::Image
69
- instance = klass.new(path, retro: retro, tileable: tileable)
70
- else
71
- instance = klass.new(path)
72
- end
73
- hash[path] = instance
74
- asset = instance
75
- end
76
-
77
- return asset
78
- end
79
-
80
- def get_image(path, retro: false, tileable: false)
81
- get_asset(path, Engine::IMAGES, Gosu::Image, retro, tileable)
82
- end
83
-
84
- def get_sample(path)
85
- get_asset(path, Engine::SAMPLES, Gosu::Sample)
86
- end
87
-
88
- def get_song(path)
89
- get_asset(path, Engine::SONGS, Gosu::Song)
90
- end
91
-
92
- def window
93
- $window
94
- end
95
- end
96
- end
1
+ module CyberarmEngine
2
+ module Common
3
+ def push_state(klass, options = {})
4
+ window.push_state(klass, options)
5
+ end
6
+
7
+ def current_state
8
+ window.current_state
9
+ end
10
+
11
+ def previous_state
12
+ window.previous_state
13
+ end
14
+
15
+ def pop_state
16
+ window.pop_state
17
+ end
18
+
19
+ def show_cursor
20
+ window.show_cursor
21
+ end
22
+
23
+ def show_cursor=(boolean)
24
+ window.show_cursor = boolean
25
+ end
26
+
27
+ def draw_rect(x, y, width, height, color, z = 0)
28
+ Gosu.draw_rect(x, y, width, height, color, z)
29
+ end
30
+
31
+ def fill(color, z = 0)
32
+ draw_rect(0, 0, window.width, window.height, color, z)
33
+ end
34
+
35
+ def lighten(color, amount = 25)
36
+ if defined?(color.alpha)
37
+ Gosu::Color.rgba(color.red + amount, color.green + amount, color.blue + amount, color.alpha)
38
+ else
39
+ Gosu::Color.rgb(color.red + amount, color.green + amount, color.blue + amount)
40
+ end
41
+ end
42
+
43
+ def darken(color, amount = 25)
44
+ if defined?(color.alpha)
45
+ Gosu::Color.rgba(color.red - amount, color.green - amount, color.blue - amount, color.alpha)
46
+ else
47
+ Gosu::Color.rgb(color.red - amount, color.green - amount, color.blue - amount)
48
+ end
49
+ end
50
+
51
+ def opacity(color, ratio = 1.0)
52
+ alpha = 255 * ratio
53
+
54
+ Gosu::Color.rgba(color.red, color.green, color.blue, alpha)
55
+ end
56
+
57
+ def get_asset(path, hash, klass, retro = false, tileable = false)
58
+ asset = nil
59
+ hash.detect do |_asset, instance|
60
+ if _asset == path
61
+ asset = instance
62
+ true
63
+ end
64
+ end
65
+
66
+ unless asset
67
+ instance = nil
68
+ instance = if klass == Gosu::Image
69
+ klass.new(path, retro: retro, tileable: tileable)
70
+ else
71
+ klass.new(path)
72
+ end
73
+ hash[path] = instance
74
+ asset = instance
75
+ end
76
+
77
+ asset
78
+ end
79
+
80
+ def get_image(path, retro: false, tileable: false)
81
+ get_asset(path, Window::IMAGES, Gosu::Image, retro, tileable)
82
+ end
83
+
84
+ def get_sample(path)
85
+ get_asset(path, Window::SAMPLES, Gosu::Sample)
86
+ end
87
+
88
+ def get_song(path)
89
+ get_asset(path, Window::SONGS, Gosu::Song)
90
+ end
91
+
92
+ def window
93
+ $window
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,46 @@
1
+ module CyberarmEngine
2
+ class ConfigFile
3
+ def initialize(file:)
4
+ @file = file
5
+
6
+ if File.exist?(@file)
7
+ deserialize
8
+ else
9
+ @data = {}
10
+ end
11
+ end
12
+
13
+ def []=(*keys, value)
14
+ last_key = keys.last
15
+
16
+ if keys.size == 1
17
+ hash = @data
18
+ else
19
+ keys.pop
20
+ hash = @data[keys.shift] ||= {}
21
+
22
+ keys.each do |key|
23
+ hash = hash[key] ||= {}
24
+ end
25
+ end
26
+
27
+ hash[last_key] = value
28
+ end
29
+
30
+ def get(*keys)
31
+ @data.dig(*keys)
32
+ end
33
+
34
+ def serialize
35
+ JSON.dump(@data)
36
+ end
37
+
38
+ def deserialize
39
+ @data = JSON.parse(File.read(@file), symbolize_names: true)
40
+ end
41
+
42
+ def save!
43
+ File.open(@file, "w") { |f| f.write(serialize) }
44
+ end
45
+ end
46
+ end
@@ -1,257 +1,248 @@
1
- module CyberarmEngine
2
- class GameObject
3
- include Common
4
-
5
- attr_accessor :image, :angle, :position, :velocity, :center_x, :center_y, :scale_x, :scale_y,
6
- :color, :mode, :options, :paused, :radius, :last_position
7
- attr_reader :alpha
8
- def initialize(options={})
9
- if options[:auto_manage] || options[:auto_manage] == nil
10
- $window.current_state.add_game_object(self)
11
- end
12
-
13
- @options = options
14
- @image = options[:image] ? image(options[:image]) : nil
15
- x = options[:x] ? options[:x] : 0
16
- y = options[:y] ? options[:y] : 0
17
- z = options[:z] ? options[:z] : 0
18
- @position = Vector.new(x, y, z)
19
- @velocity = Vector.new
20
- @last_position = Vector.new
21
- @angle = options[:angle] ? options[:angle] : 0
22
-
23
- @center_x = options[:center_x] ? options[:center_x] : 0.5
24
- @center_y = options[:center_y] ? options[:center_y] : 0.5
25
-
26
- @scale_x = options[:scale_x] ? options[:scale_x] : 1
27
- @scale_y = options[:scale_y] ? options[:scale_y] : 1
28
-
29
- @color = options[:color] ? options[:color] : Gosu::Color.argb(0xff_ffffff)
30
- @alpha = options[:alpha] ? options[:alpha] : 255
31
- @mode = options[:mode] ? options[:mode] : :default
32
-
33
- @paused = false
34
- @speed = 0
35
- @debug_color = Gosu::Color::GREEN
36
- @world_center_point = Vector.new(0,0)
37
-
38
- setup
39
-
40
- @debug_text = Text.new("", color: @debug_color, y: @position.y-(self.height*self.scale), z: 9999)
41
- @debug_text.x = @position.x
42
- if @radius == 0 || @radius == nil
43
- @radius = options[:radius] ? options[:radius] : defined?(@image.width) ? ((@image.width+@image.height)/4)*scale : 1
44
- end
45
- end
46
-
47
- def draw
48
- if @image
49
- @image.draw_rot(@position.x, @position.y, @position.z, @angle, @center_x, @center_y, @scale_x, @scale_y, @color, @mode)
50
- end
51
-
52
- if $debug
53
- show_debug_heading
54
- $window.draw_circle(@position.x, @position.y, radius, 9999, @debug_color)
55
- if @debug_text.text != ""
56
- $window.draw_rect(@debug_text.x-10, (@debug_text.y-10), @debug_text.width+20, @debug_text.height+20, Gosu::Color.rgba(0,0,0,200), 9999)
57
- @debug_text.draw
58
- end
59
- end
60
- end
61
-
62
- def update
63
- end
64
-
65
- def debug_text(text)
66
- @debug_text.text = text
67
- @debug_text.x = @position.x-(@debug_text.width / 2)
68
- @debug_text.y = @position.y-(@debug_text.height + self.radius + self.height)
69
- end
70
-
71
- def scale
72
- if @scale_x == @scale_y
73
- return @scale_x
74
- else
75
- false
76
- # maths?
77
- end
78
- end
79
-
80
- def scale=(int)
81
- self.scale_x = int
82
- self.scale_y = int
83
- self.radius = ((@image.width+@image.height)/4)*self.scale
84
- end
85
-
86
- def visible
87
- true
88
- # if _x_visible
89
- # if _y_visible
90
- # true
91
- # else
92
- # false
93
- # end
94
- # else
95
- # false
96
- # end
97
- end
98
-
99
- def _x_visible
100
- self.x.between?(($window.width/2)-(@world_center_point.x), ($window.width/2)+@world_center_point.x) ||
101
- self.x.between?(((@world_center_point.x)-$window.width/2), ($window.width/2)+@world_center_point.x)
102
- end
103
-
104
- def _y_visible
105
- self.y.between?(($window.height/2)-(@world_center_point.y), ($window.height/2)+@world_center_point.y) ||
106
- self.y.between?((@world_center_point.y)-($window.height/2), ($window.height/2)+@world_center_point.y)
107
- end
108
-
109
- def heading(ahead_by = 100, object = nil, angle_only = false)
110
- direction = Gosu.angle(@last_position.x, @last_position.x, @position.x, position.y).gosu_to_radians
111
-
112
- _x = @position.x + (ahead_by * Math.cos(direction))
113
- _y = @position.y + (ahead_by * Math.sin(direction))
114
-
115
- return direction if angle_only
116
- return Vector.new(_x, _y) unless angle_only
117
- end
118
-
119
- def show_debug_heading
120
- _heading = heading
121
- Gosu.draw_line(@position.x, @position.y, @debug_color, _heading.x, _heading.y, @debug_color, 9999)
122
- end
123
-
124
- def width
125
- @image ? @image.width * self.scale : 0
126
- end
127
-
128
- def height
129
- @image ? @image.height * self.scale : 0
130
- end
131
-
132
- def pause
133
- @paused = true
134
- end
135
-
136
- def unpause
137
- @paused = false
138
- end
139
-
140
- def rotate(int)
141
- self.angle+=int
142
- self.angle%=360
143
- end
144
-
145
- def alpha=(int) # 0-255
146
- @alpha = int
147
- @alpha = 255 if @alpha > 255
148
- @color = Gosu::Color.rgba(@color.red, @color.green, @color.blue, int)
149
- end
150
-
151
- def draw_rect(x, y, width, height, color, z = 0)
152
- $window.draw_rect(x,y,width,height,color,z)
153
- end
154
-
155
- def button_up(id)
156
- end
157
-
158
- def button_down(id)
159
- end
160
-
161
- def find_closest(game_object_class)
162
- best_object = nil
163
- best_distance = 100_000_000_000 # Huge default number
164
-
165
- game_object_class.all.each do |object|
166
- distance = Gosu::distance(self.x, self.y, object.x, object.y)
167
- if distance <= best_distance
168
- best_object = object
169
- best_distance = distance
170
- end
171
- end
172
-
173
- return best_object
174
- end
175
-
176
- def look_at(object)
177
- # TODO: Implement
178
- end
179
-
180
- def circle_collision?(object)
181
- distance = Gosu.distance(self.x, self.y, object.x, object.y)
182
- if distance <= self.radius+object.radius
183
- true
184
- else
185
- false
186
- end
187
- end
188
-
189
- # Duplication... so DRY.
190
- def each_circle_collision(object, resolve_with = :width, &block)
191
- if object.class != Class && object.instance_of?(object.class)
192
- $window.current_state.game_objects.select {|i| i.class == object.class}.each do |o|
193
- distance = Gosu.distance(self.x, self.y, object.x, object.y)
194
- if distance <= self.radius+object.radius
195
- block.call(o, object) if block
196
- end
197
- end
198
- else
199
- list = $window.current_state.game_objects.select {|i| i.class == object}
200
- list.each do |o|
201
- next if self == o
202
- distance = Gosu.distance(self.x, self.y, o.x, o.y)
203
- if distance <= self.radius+o.radius
204
- block.call(self, o) if block
205
- end
206
- end
207
- end
208
- end
209
-
210
- def destroy
211
- if $window.current_state
212
- $window.current_state.game_objects.each do |o|
213
- if o.is_a?(self.class) && o == self
214
- $window.current_state.game_objects.delete(o)
215
- end
216
- end
217
- end
218
- end
219
-
220
- # NOTE: This could be implemented more reliably
221
- def all
222
- INSTANCES.select {|i| i.class == self}
223
- end
224
-
225
- def self.each_circle_collision(object, resolve_with = :width, &block)
226
- if object.class != Class && object.instance_of?(object.class)
227
- $window.current_state.game_objects.select {|i| i.class == self}.each do |o|
228
- distance = Gosu.distance(o.x, o.y, object.x, object.y)
229
- if distance <= o.radius+object.radius
230
- block.call(o, object) if block
231
- end
232
- end
233
- else
234
- lista = $window.current_state.game_objects.select {|i| i.class == self}
235
- listb = $window.current_state.game_objects.select {|i| i.class == object}
236
- lista.product(listb).each do |o, o2|
237
- next if o == o2
238
- distance = Gosu.distance(o.x, o.y, o2.x, o2.y)
239
- if distance <= o.radius+o2.radius
240
- block.call(o, o2) if block
241
- end
242
- end
243
- end
244
- end
245
-
246
- def self.destroy_all
247
- INSTANCES.clear
248
- if $window.current_state
249
- $window.current_state.game_objects.each do |o|
250
- if o.is_a?(self.class)
251
- $window.current_state.game_objects.delete(o)
252
- end
253
- end
254
- end
255
- end
256
- end
257
- end
1
+ module CyberarmEngine
2
+ class GameObject
3
+ include Common
4
+
5
+ attr_accessor :image, :angle, :position, :velocity, :center_x, :center_y, :scale_x, :scale_y,
6
+ :color, :mode, :options, :paused, :radius, :last_position
7
+ attr_reader :alpha
8
+
9
+ def initialize(options = {})
10
+ $window.current_state.add_game_object(self) if options[:auto_manage] || options[:auto_manage].nil?
11
+
12
+ @options = options
13
+ @image = options[:image] ? image(options[:image]) : nil
14
+ x = options[:x] || 0
15
+ y = options[:y] || 0
16
+ z = options[:z] || 0
17
+ @position = Vector.new(x, y, z)
18
+ @velocity = Vector.new
19
+ @last_position = Vector.new
20
+ @angle = options[:angle] || 0
21
+
22
+ @center_x = options[:center_x] || 0.5
23
+ @center_y = options[:center_y] || 0.5
24
+
25
+ @scale_x = options[:scale_x] || 1
26
+ @scale_y = options[:scale_y] || 1
27
+
28
+ @color = options[:color] || Gosu::Color.argb(0xff_ffffff)
29
+ @alpha = options[:alpha] || 255
30
+ @mode = options[:mode] || :default
31
+
32
+ @paused = false
33
+ @speed = 0
34
+ @debug_color = Gosu::Color::GREEN
35
+ @world_center_point = Vector.new(0, 0)
36
+
37
+ setup
38
+
39
+ @debug_text = Text.new("", color: @debug_color, y: @position.y - (height * scale), z: 9999)
40
+ @debug_text.x = @position.x
41
+ if @radius == 0 || @radius.nil?
42
+ @radius = if options[:radius]
43
+ options[:radius]
44
+ else
45
+ defined?(@image.width) ? ((@image.width + @image.height) / 4) * scale : 1
46
+ end
47
+ end
48
+ end
49
+
50
+ def draw
51
+ if @image
52
+ @image.draw_rot(@position.x, @position.y, @position.z, @angle, @center_x, @center_y, @scale_x, @scale_y,
53
+ @color, @mode)
54
+ end
55
+
56
+ if $debug
57
+ show_debug_heading
58
+ $window.draw_circle(@position.x, @position.y, radius, 9999, @debug_color)
59
+ if @debug_text.text != ""
60
+ $window.draw_rect(@debug_text.x - 10, (@debug_text.y - 10), @debug_text.width + 20, @debug_text.height + 20,
61
+ Gosu::Color.rgba(0, 0, 0, 200), 9999)
62
+ @debug_text.draw
63
+ end
64
+ end
65
+ end
66
+
67
+ def update
68
+ end
69
+
70
+ def debug_text(text)
71
+ @debug_text.text = text
72
+ @debug_text.x = @position.x - (@debug_text.width / 2)
73
+ @debug_text.y = @position.y - (@debug_text.height + radius + height)
74
+ end
75
+
76
+ def scale
77
+ if @scale_x == @scale_y
78
+ @scale_x
79
+ else
80
+ false
81
+ # maths?
82
+ end
83
+ end
84
+
85
+ def scale=(int)
86
+ self.scale_x = int
87
+ self.scale_y = int
88
+ self.radius = ((@image.width + @image.height) / 4) * scale
89
+ end
90
+
91
+ def visible
92
+ true
93
+ # if _x_visible
94
+ # if _y_visible
95
+ # true
96
+ # else
97
+ # false
98
+ # end
99
+ # else
100
+ # false
101
+ # end
102
+ end
103
+
104
+ def _x_visible
105
+ x.between?(($window.width / 2) - @world_center_point.x, ($window.width / 2) + @world_center_point.x) ||
106
+ x.between?((@world_center_point.x - $window.width / 2), ($window.width / 2) + @world_center_point.x)
107
+ end
108
+
109
+ def _y_visible
110
+ y.between?(($window.height / 2) - @world_center_point.y, ($window.height / 2) + @world_center_point.y) ||
111
+ y.between?(@world_center_point.y - ($window.height / 2), ($window.height / 2) + @world_center_point.y)
112
+ end
113
+
114
+ def heading(ahead_by = 100, _object = nil, angle_only = false)
115
+ direction = Gosu.angle(@last_position.x, @last_position.x, @position.x, position.y).gosu_to_radians
116
+
117
+ _x = @position.x + (ahead_by * Math.cos(direction))
118
+ _y = @position.y + (ahead_by * Math.sin(direction))
119
+
120
+ return direction if angle_only
121
+ return Vector.new(_x, _y) unless angle_only
122
+ end
123
+
124
+ def show_debug_heading
125
+ _heading = heading
126
+ Gosu.draw_line(@position.x, @position.y, @debug_color, _heading.x, _heading.y, @debug_color, 9999)
127
+ end
128
+
129
+ def width
130
+ @image ? @image.width * scale : 0
131
+ end
132
+
133
+ def height
134
+ @image ? @image.height * scale : 0
135
+ end
136
+
137
+ def pause
138
+ @paused = true
139
+ end
140
+
141
+ def unpause
142
+ @paused = false
143
+ end
144
+
145
+ def rotate(int)
146
+ self.angle += int
147
+ self.angle %= 360
148
+ end
149
+
150
+ def alpha=(int) # 0-255
151
+ @alpha = int
152
+ @alpha = 255 if @alpha > 255
153
+ @color = Gosu::Color.rgba(@color.red, @color.green, @color.blue, int)
154
+ end
155
+
156
+ def draw_rect(x, y, width, height, color, z = 0)
157
+ $window.draw_rect(x, y, width, height, color, z)
158
+ end
159
+
160
+ def button_up(id)
161
+ end
162
+
163
+ def button_down(id)
164
+ end
165
+
166
+ def find_closest(game_object_class)
167
+ best_object = nil
168
+ best_distance = 100_000_000_000 # Huge default number
169
+
170
+ game_object_class.all.each do |object|
171
+ distance = Gosu.distance(x, y, object.x, object.y)
172
+ if distance <= best_distance
173
+ best_object = object
174
+ best_distance = distance
175
+ end
176
+ end
177
+
178
+ best_object
179
+ end
180
+
181
+ def look_at(object)
182
+ # TODO: Implement
183
+ end
184
+
185
+ def circle_collision?(object)
186
+ distance = Gosu.distance(x, y, object.x, object.y)
187
+ distance <= radius + object.radius
188
+ end
189
+
190
+ # Duplication... so DRY.
191
+ def each_circle_collision(object, _resolve_with = :width, &block)
192
+ if object.class != Class && object.instance_of?(object.class)
193
+ $window.current_state.game_objects.select { |i| i.instance_of?(object.class) }.each do |o|
194
+ distance = Gosu.distance(x, y, object.x, object.y)
195
+ block.call(o, object) if distance <= radius + object.radius && block
196
+ end
197
+ else
198
+ list = $window.current_state.game_objects.select { |i| i.instance_of?(object) }
199
+ list.each do |o|
200
+ next if self == o
201
+
202
+ distance = Gosu.distance(x, y, o.x, o.y)
203
+ block.call(self, o) if distance <= radius + o.radius && block
204
+ end
205
+ end
206
+ end
207
+
208
+ def destroy
209
+ if $window.current_state
210
+ $window.current_state.game_objects.each do |o|
211
+ $window.current_state.game_objects.delete(o) if o.is_a?(self.class) && o == self
212
+ end
213
+ end
214
+ end
215
+
216
+ # NOTE: This could be implemented more reliably
217
+ def all
218
+ INSTANCES.select { |i| i.instance_of?(self) }
219
+ end
220
+
221
+ def self.each_circle_collision(object, _resolve_with = :width, &block)
222
+ if object.class != Class && object.instance_of?(object.class)
223
+ $window.current_state.game_objects.select { |i| i.instance_of?(self) }.each do |o|
224
+ distance = Gosu.distance(o.x, o.y, object.x, object.y)
225
+ block.call(o, object) if distance <= o.radius + object.radius && block
226
+ end
227
+ else
228
+ lista = $window.current_state.game_objects.select { |i| i.instance_of?(self) }
229
+ listb = $window.current_state.game_objects.select { |i| i.instance_of?(object) }
230
+ lista.product(listb).each do |o, o2|
231
+ next if o == o2
232
+
233
+ distance = Gosu.distance(o.x, o.y, o2.x, o2.y)
234
+ block.call(o, o2) if distance <= o.radius + o2.radius && block
235
+ end
236
+ end
237
+ end
238
+
239
+ def self.destroy_all
240
+ INSTANCES.clear
241
+ if $window.current_state
242
+ $window.current_state.game_objects.each do |o|
243
+ $window.current_state.game_objects.delete(o) if o.is_a?(self.class)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end