cyberarm_engine 0.13.0 → 0.17.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.
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