cyberarm_engine 0.25.1 → 0.30.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +20 -8
  4. data/README.md +1 -1
  5. data/assets/textures/ui/circle.png +0 -0
  6. data/assets/textures/ui/circle_small.png +0 -0
  7. data/assets/textures/ui/rounded.png +0 -0
  8. data/assets/textures/ui/rounded_bottom.png +0 -0
  9. data/assets/textures/ui/rounded_left.png +0 -0
  10. data/assets/textures/ui/rounded_left_bottom.png +0 -0
  11. data/assets/textures/ui/rounded_left_top.png +0 -0
  12. data/assets/textures/ui/rounded_right.png +0 -0
  13. data/assets/textures/ui/rounded_right_bottom.png +0 -0
  14. data/assets/textures/ui/rounded_right_top.png +0 -0
  15. data/assets/textures/ui/rounded_small.png +0 -0
  16. data/assets/textures/ui/rounded_tiny.png +0 -0
  17. data/assets/textures/ui/rounded_top.png +0 -0
  18. data/assets/textures/ui/square.png +0 -0
  19. data/cyberarm_engine.gemspec +1 -2
  20. data/lib/cyberarm_engine/background.rb +16 -18
  21. data/lib/cyberarm_engine/background_nine_slice.rb +62 -22
  22. data/lib/cyberarm_engine/common.rb +2 -0
  23. data/lib/cyberarm_engine/event_bus.rb +39 -0
  24. data/lib/cyberarm_engine/game_state.rb +11 -1
  25. data/lib/cyberarm_engine/pathfinding/graph.rb +64 -0
  26. data/lib/cyberarm_engine/pathfinding/graph_search.rb +6 -0
  27. data/lib/cyberarm_engine/pathfinding/graph_searches/astar_search.rb +6 -0
  28. data/lib/cyberarm_engine/pathfinding/graph_searches/breadth_first_search.rb +27 -0
  29. data/lib/cyberarm_engine/pathfinding/graph_searches/dijkstra_search.rb +6 -0
  30. data/lib/cyberarm_engine/pathfinding/graphs/grid.rb +52 -0
  31. data/lib/cyberarm_engine/result.rb +1 -2
  32. data/lib/cyberarm_engine/text.rb +54 -26
  33. data/lib/cyberarm_engine/timer.rb +4 -0
  34. data/lib/cyberarm_engine/ui/border_canvas.rb +16 -16
  35. data/lib/cyberarm_engine/ui/dialog.rb +24 -0
  36. data/lib/cyberarm_engine/ui/dsl.rb +29 -1
  37. data/lib/cyberarm_engine/ui/element.rb +132 -130
  38. data/lib/cyberarm_engine/ui/elements/button.rb +12 -13
  39. data/lib/cyberarm_engine/ui/elements/check_box.rb +5 -1
  40. data/lib/cyberarm_engine/ui/elements/container.rb +52 -46
  41. data/lib/cyberarm_engine/ui/elements/edit_box.rb +30 -9
  42. data/lib/cyberarm_engine/ui/elements/edit_line.rb +4 -2
  43. data/lib/cyberarm_engine/ui/elements/image.rb +9 -3
  44. data/lib/cyberarm_engine/ui/elements/list_box.rb +3 -3
  45. data/lib/cyberarm_engine/ui/elements/menu.rb +3 -3
  46. data/lib/cyberarm_engine/ui/elements/progress.rb +8 -6
  47. data/lib/cyberarm_engine/ui/elements/slider.rb +7 -3
  48. data/lib/cyberarm_engine/ui/elements/text_block.rb +31 -17
  49. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +5 -1
  50. data/lib/cyberarm_engine/ui/elements/widget.rb +45 -0
  51. data/lib/cyberarm_engine/ui/event.rb +1 -1
  52. data/lib/cyberarm_engine/ui/gui_state.rb +86 -10
  53. data/lib/cyberarm_engine/ui/page.rb +39 -0
  54. data/lib/cyberarm_engine/ui/style.rb +48 -15
  55. data/lib/cyberarm_engine/ui/theme.rb +17 -5
  56. data/lib/cyberarm_engine/version.rb +1 -1
  57. data/lib/cyberarm_engine/window.rb +40 -1
  58. data/lib/cyberarm_engine.rb +11 -0
  59. metadata +28 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 684e4ba3ba113566003f54546d230b73a2e114de9ffb37a61c82459399fed368
4
- data.tar.gz: ed6d16a4e889f8d7c744aff3c75b3688adeaae6ae63c670d43fc2416af3cc5c3
3
+ metadata.gz: adf885ec549b6b0550708d2b037ebdf15f4ad45a90a6b600bbffb6ca0b2dc9b1
4
+ data.tar.gz: d5f249516e9979e54dbbcbee37e78a5993559e4322f3a18bc734f02c44b7944b
5
5
  SHA512:
6
- metadata.gz: 9210c849bb6c757930f887f4af71c4782e199d3b46583da9152d3daead65887c34286fc7ebdce52226cebf342d23fd9434f6f99f441ecad1e8cfed7c37d64093
7
- data.tar.gz: fa421d854aec6c8ef906b7e6882ec443d070517daf40650cd11abc98513db9ae7d412492de15467516ac8bb9abe3838f3bf5434577092e419195128311734301
6
+ metadata.gz: 92337be7d3ffe70bd5b3ea87ea7ec4232c92ab60afcf5beb8e3dd9bb62c201afdac06c9da8f1bb21e673b9160a18ac41bdd5b01172c317f86a561d3d62343a6b
7
+ data.tar.gz: 9ca3a2377f3faa7555bb3a8c49cee3b652c356a2ac6c725815e0b9526ab0021f139da24c5cff313a00c01596ea4f98bd35dd781513c71c6cf9de535a35d21bc3
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ /.idea
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
data/Gemfile.lock CHANGED
@@ -1,25 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cyberarm_engine (0.24.5)
4
+ cyberarm_engine (0.30.0)
5
5
  gosu (~> 1.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- gosu (1.4.6)
11
- minitest (5.25.5)
12
- rake (13.2.1)
10
+ ffi (1.17.4-x86_64-linux-gnu)
11
+ ffi (1.17.4-x86_64-linux-musl)
12
+ gosu (1.5.0-x86_64-linux)
13
+ ffi (~> 1.12)
14
+ minitest (5.27.0)
15
+ rake (13.4.2)
13
16
 
14
17
  PLATFORMS
15
- x64-mingw-ucrt
16
- x86_64-linux
18
+ x86_64-linux-gnu
19
+ x86_64-linux-musl
17
20
 
18
21
  DEPENDENCIES
19
- bundler (~> 2.2)
22
+ bundler (~> 4)
20
23
  cyberarm_engine!
21
24
  minitest (~> 5.0)
22
25
  rake (~> 13.0)
23
26
 
27
+ CHECKSUMS
28
+ bundler (4.1.0.beta1) sha256=8a2dac8ca276ff4eff5efc2c6457b0044080fc1e431534ad7c5cc6187232b800
29
+ cyberarm_engine (0.30.0)
30
+ ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
31
+ ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
32
+ gosu (1.5.0-x86_64-linux)
33
+ minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
34
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
35
+
24
36
  BUNDLED WITH
25
- 2.6.8
37
+ 4.1.0.beta1
data/README.md CHANGED
@@ -35,7 +35,7 @@ class Hello < CyberarmEngine::GuiState
35
35
  background Gosu::Color::GRAY
36
36
 
37
37
  stack do
38
- label "Hello World!"
38
+ banner "Hello World!"
39
39
 
40
40
  button "close" do
41
41
  window.close
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -28,9 +28,8 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = %w[lib assets]
29
29
 
30
30
  spec.add_dependency "gosu", "~> 1.1"
31
- # spec.add_dependency "ffi", :platforms => [:mswin, :mingw] # Required by Clipboard on Windows
32
31
 
33
- spec.add_development_dependency "bundler", "~> 2.2"
32
+ spec.add_development_dependency "bundler", "~> 4"
34
33
  spec.add_development_dependency "minitest", "~> 5.0"
35
34
  spec.add_development_dependency "rake", "~> 13.0"
36
35
  end
@@ -21,19 +21,27 @@ module CyberarmEngine
21
21
  end
22
22
 
23
23
  def draw
24
- Gosu.clip_to(@x, @y, @width, @height) do
25
- Gosu.draw_quad(
26
- @top_left.x, @top_left.y, @paint.top_left,
27
- @top_right.x, @top_right.y, @paint.top_right,
28
- @bottom_right.x, @bottom_right.y, @paint.bottom_right,
29
- @bottom_left.x, @bottom_left.y, @paint.bottom_left,
30
- @z
31
- )
24
+ if @angle.zero?
25
+ render
26
+ else
27
+ Gosu.clip_to(@x, @y, @width, @height) do
28
+ render
29
+ end
32
30
  end
33
31
 
34
32
  debug_outline if @debug
35
33
  end
36
34
 
35
+ def render
36
+ Gosu.draw_quad(
37
+ @top_left.x, @top_left.y, @paint.top_left,
38
+ @top_right.x, @top_right.y, @paint.top_right,
39
+ @bottom_right.x, @bottom_right.y, @paint.bottom_right,
40
+ @bottom_left.x, @bottom_left.y, @paint.bottom_left,
41
+ @z
42
+ )
43
+ end
44
+
37
45
  def update
38
46
  @top_left.x = @x
39
47
  @top_left.y = @y
@@ -146,13 +154,3 @@ module CyberarmEngine
146
154
  end
147
155
  end
148
156
  end
149
-
150
- # Add <=> method to support Range based gradients
151
- # NOTE: Disabled, causes stack overflow 🙃
152
- # module Gosu
153
- # class Color
154
- # def <=>(_other)
155
- # self
156
- # end
157
- # end
158
- # end
@@ -1,10 +1,13 @@
1
1
  module CyberarmEngine
2
2
  class BackgroundNineSlice
3
3
  include CyberarmEngine::Common
4
- attr_accessor :x, :y, :z, :width, :height, :left, :top, :right, :bottom, :mode, :color
5
- attr_reader :image
6
4
 
7
- def initialize(image_path: nil, x: 0, y: 0, z: 0, width: 0, height: 0, mode: :tiled, left: 1, top: 1, right: 1, bottom: 1, color: Gosu::Color::WHITE)
5
+ CACHE = {}
6
+
7
+ attr_accessor :x, :y, :z, :width, :height, :mode, :color, :render_mode
8
+ attr_reader :image, :left, :top, :right, :bottom
9
+
10
+ def initialize(image_path: nil, x: 0, y: 0, z: 0, width: 0, height: 0, mode: :tiled, left: 1, top: 1, right: 1, bottom: 1, color: Gosu::Color::WHITE, render_mode: :default)
8
11
  @image = get_image(image_path) if image_path
9
12
 
10
13
  @x = x
@@ -22,6 +25,7 @@ module CyberarmEngine
22
25
  @bottom = bottom
23
26
 
24
27
  @color = color
28
+ @render_mode = render_mode
25
29
 
26
30
  nine_slice if @image
27
31
  end
@@ -32,8 +36,43 @@ module CyberarmEngine
32
36
  nine_slice if @image && old_image != @image
33
37
  end
34
38
 
39
+ def set_edges(left: @left, right: @right, top: @top, bottom: @bottom)
40
+ changed = [left == @left, right == @right, top == @top, bottom == @bottom].any?(false)
41
+
42
+ @left = left
43
+ @top = top
44
+ @right = right
45
+ @bottom = bottom
46
+
47
+ nine_slice if changed
48
+ end
49
+
50
+ def left=(n)
51
+ nine_slice if n != @left
52
+ @left = n
53
+ n
54
+ end
55
+
56
+ def right=(n)
57
+ nine_slice if n != @right
58
+ @right = n
59
+ n
60
+ end
61
+
62
+ def top=(n)
63
+ nine_slice if n != @top
64
+ @top = n
65
+ n
66
+ end
67
+
68
+ def bottom=(n)
69
+ nine_slice if n != @bottom
70
+ @bottom = n
71
+ n
72
+ end
73
+
35
74
  def nine_slice
36
- # pp [@left, @top, @right, @bottom, @image.width]
75
+ # pp [@left, @top, @right, @bottom, @image.width, @image.height]
37
76
 
38
77
  @segment_top_left = @image.subimage(0, 0, @left, @top)
39
78
  @segment_top_right = @image.subimage(@image.width - @right, 0, @right, @top)
@@ -83,57 +122,58 @@ module CyberarmEngine
83
122
  end
84
123
 
85
124
  def draw_stretched
86
- @segment_top_left.draw(@x, @y, @z, 1, 1, @color)
87
- @segment_top.draw(@x + @segment_top_left.width, @y, @z, width_scale, 1, @color) # SCALE X
88
- @segment_top_right.draw((@x + @width) - @segment_top_right.width, @y, @z, 1, 1, @color)
125
+ @segment_top_left.draw(@x, @y, @z, 1, 1, @color, @render_mode)
126
+ @segment_top.draw(@x + @segment_top_left.width, @y, @z, width_scale, 1, @color, @render_mode) # SCALE X
127
+ @segment_top_right.draw((@x + @width) - @segment_top_right.width, @y, @z, 1, 1, @color, @render_mode)
128
+
129
+ @segment_right.draw((@x + @width) - @segment_right.width, @y + @top, @z, 1, height_scale, @color, @render_mode) # SCALE Y
130
+ @segment_bottom_right.draw((@x + @width) - @segment_bottom_right.width, @y + @height - @segment_bottom_right.height, @z, 1, 1, @color, @render_mode)
131
+ @segment_bottom.draw(@x + @segment_bottom_left.width, (@y + @height) - @segment_bottom.height, @z, width_scale, 1, @color, @render_mode) # SCALE X
132
+ @segment_bottom_left.draw(@x, (@y + @height) - @segment_bottom_left.height, @z, 1, 1, @color, @render_mode)
133
+ @segment_left.draw(@x, @y + @top, @z, 1, height_scale, @color, @render_mode) # SCALE Y
89
134
 
90
- @segment_right.draw((@x + @width) - @segment_right.width, @y + @top, @z, 1, height_scale, @color) # SCALE Y
91
- @segment_bottom_right.draw((@x + @width) - @segment_bottom_right.width, @y + @height - @segment_bottom_right.height, @z, 1, 1, @color)
92
- @segment_bottom.draw(@x + @segment_bottom_left.width, (@y + @height) - @segment_bottom.height, @z, width_scale, 1, @color) # SCALE X
93
- @segment_bottom_left.draw(@x, (@y + @height) - @segment_bottom_left.height, @z, 1, 1, @color)
94
- @segment_left.draw(@x, @y + @top, @z, 1, height_scale, @color) # SCALE Y
95
- @segment_middle.draw(@x + @segment_top_left.width, @y + @segment_top.height, @z, width_scale, height_scale, @color) # SCALE X and SCALE Y
135
+ @segment_middle.draw(@x + @segment_top_left.width, @y + @segment_top.height, @z, width_scale, height_scale, @color, @render_mode) # SCALE X and SCALE Y
96
136
  end
97
137
 
98
138
  def draw_tiled
99
- @segment_top_left.draw(@x, @y, @z, 1, 1, @color)
139
+ @segment_top_left.draw(@x, @y, @z, 1, 1, @color, @render_mode)
100
140
 
101
141
  # p [width_scale, height_scale]
102
142
 
103
143
  Gosu.clip_to(@x + @segment_top_left.width, @y, @segment_top.width * width_scale, @segment_top.height) do
104
144
  width_scale.ceil.times do |i|
105
- @segment_top.draw(@x + @segment_top_left.width + (@segment_top.width * i), @y, @z, 1, 1, @color) # SCALE X
145
+ @segment_top.draw(@x + @segment_top_left.width + (@segment_top.width * i), @y, @z, 1, 1, @color, @render_mode) # SCALE X
106
146
  end
107
147
  end
108
148
 
109
- @segment_top_right.draw((@x + @width) - @segment_top_right.width, @y, @z, 1, 1, @color)
149
+ @segment_top_right.draw((@x + @width) - @segment_top_right.width, @y, @z, 1, 1, @color, @render_mode)
110
150
 
111
151
  Gosu.clip_to(@x + @width - @segment_top_right.width, @y + @top, @segment_right.width, @segment_right.height * height_scale) do
112
152
  height_scale.ceil.times do |i|
113
- @segment_right.draw((@x + @width) - @segment_right.width, @y + @top + (@segment_right.height * i), @z, 1, 1, @color) # SCALE Y
153
+ @segment_right.draw((@x + @width) - @segment_right.width, @y + @top + (@segment_right.height * i), @z, 1, 1, @color, @render_mode) # SCALE Y
114
154
  end
115
155
  end
116
156
 
117
- @segment_bottom_right.draw((@x + @width) - @segment_bottom_right.width, @y + @height - @segment_bottom_right.height, @z, 1, 1, @color)
157
+ @segment_bottom_right.draw((@x + @width) - @segment_bottom_right.width, @y + @height - @segment_bottom_right.height, @z, 1, 1, @color, @render_mode)
118
158
 
119
159
  Gosu.clip_to(@x + @segment_top_left.width, @y + @height - @segment_bottom.height, @segment_top.width * width_scale, @segment_bottom.height) do
120
160
  width_scale.ceil.times do |i|
121
- @segment_bottom.draw(@x + @segment_bottom_left.width + (@segment_bottom.width * i), (@y + @height) - @segment_bottom.height, @z, 1, 1, @color) # SCALE X
161
+ @segment_bottom.draw(@x + @segment_bottom_left.width + (@segment_bottom.width * i), (@y + @height) - @segment_bottom.height, @z, 1, 1, @color, @render_mode) # SCALE X
122
162
  end
123
163
  end
124
164
 
125
- @segment_bottom_left.draw(@x, (@y + @height) - @segment_bottom_left.height, @z, 1, 1, @color)
165
+ @segment_bottom_left.draw(@x, (@y + @height) - @segment_bottom_left.height, @z, 1, 1, @color, @render_mode)
126
166
 
127
167
  Gosu.clip_to(@x, @y + @top, @segment_left.width, @segment_left.height * height_scale) do
128
168
  height_scale.ceil.times do |i|
129
- @segment_left.draw(@x, @y + @top + (@segment_left.height * i), @z, 1, 1, @color) # SCALE Y
169
+ @segment_left.draw(@x, @y + @top + (@segment_left.height * i), @z, 1, 1, @color, @render_mode) # SCALE Y
130
170
  end
131
171
  end
132
172
 
133
173
  Gosu.clip_to(@x + @segment_top_left.width, @y + @segment_top.height, @width - (@segment_left.width + @segment_right.width), @height - (@segment_top.height + @segment_bottom.height)) do
134
174
  height_scale.ceil.times do |y|
135
175
  width_scale.ceil.times do |x|
136
- @segment_middle.draw(@x + @segment_top_left.width + (@segment_middle.width * x), @y + @segment_top.height + (@segment_middle.height * y), @z, 1, 1, @color) # SCALE X and SCALE Y
176
+ @segment_middle.draw(@x + @segment_top_left.width + (@segment_middle.width * x), @y + @segment_top.height + (@segment_middle.height * y), @z, 1, 1, @color, @render_mode) # SCALE X and SCALE Y
137
177
  end
138
178
  end
139
179
  end
@@ -1,4 +1,6 @@
1
1
  module CyberarmEngine
2
+ GUI_DEBUG = false
3
+
2
4
  module Common
3
5
  ImageBlob = Data.define(:to_blob, :columns, :rows)
4
6
 
@@ -0,0 +1,39 @@
1
+ module CyberarmEngine
2
+ class EventBus
3
+ Subscriber = Data.define(:event, :listener, :method, :callback)
4
+ @subscribers = []
5
+
6
+ def self.subscribe(event, listener = nil, method = nil, &block)
7
+ raise "Subscriber cannot have both a method AND a callback block" if method && block_given?
8
+
9
+ subscriber = Subscriber.new(event, listener, method, block)
10
+ @subscribers << subscriber
11
+
12
+ subscriber
13
+ end
14
+
15
+ def self.unsubscribe(event, listener_or_subscriber)
16
+ @subscribers.delete_if { |s| s.event == event && (s == listener_or_subscriber || s.listener == listener_or_subscriber) }
17
+ end
18
+
19
+ def self.unsubscribe!(event)
20
+ @subscribers.delete_if { |s| s.event == event }
21
+ end
22
+
23
+ def self.announce(event, *payload)
24
+ subscribers = @subscribers.select { |s| s.event == event }
25
+
26
+ subscribers.each do |subscriber|
27
+ subscriber&.listener&.send(subscriber.method, *payload) if subscriber&.method
28
+ subscriber.callback&.call(*payload)
29
+ rescue StandardError => e
30
+ # TODO
31
+ warn "Failed to deliver event (#{event}) to listener (#{subscriber.inspect}): #{e}"
32
+ end
33
+ end
34
+
35
+ def self.publish(*args)
36
+ announce(*args)
37
+ end
38
+ end
39
+ end
@@ -3,11 +3,12 @@ module CyberarmEngine
3
3
  include Common
4
4
 
5
5
  attr_accessor :options, :global_pause
6
- attr_reader :game_objects
6
+ attr_reader :game_objects, :timers
7
7
 
8
8
  def initialize(options = {})
9
9
  @options = options
10
10
  @game_objects = []
11
+ @timers = []
11
12
  @global_pause = false
12
13
  window.text_input = nil unless options[:preserve_text_input]
13
14
 
@@ -28,6 +29,11 @@ module CyberarmEngine
28
29
 
29
30
  def update
30
31
  @game_objects.each(&:update)
32
+ @timers.each(&:update)
33
+ @timers.delete_if(&:dead?)
34
+ end
35
+
36
+ def fixed_update(dt)
31
37
  end
32
38
 
33
39
  def needs_redraw?
@@ -120,5 +126,9 @@ module CyberarmEngine
120
126
  def add_game_object(object)
121
127
  @game_objects << object
122
128
  end
129
+
130
+ def add_timer(timer)
131
+ @timers << timer
132
+ end
123
133
  end
124
134
  end
@@ -0,0 +1,64 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class Graph
4
+ class Node
5
+ KEY_FORMAT = "%s:%s:%s".freeze
6
+
7
+ attr_reader :x, :y, :z, :cost, :key, :edge_nodes
8
+
9
+ def initialize(x:, y:, z: 0, cost: 0)
10
+ @x = x
11
+ @y = y
12
+ @z = z
13
+ @cost = cost
14
+
15
+ @key = format(KEY_FORMAT, @x, @y, @z).freeze
16
+
17
+ @edge_nodes = []
18
+ end
19
+
20
+ def add_edge_node(node)
21
+ index = @edge_nodes.find_index { |n| n.key == node.key }
22
+
23
+ return if index
24
+
25
+ @edge_nodes.push(node)
26
+ end
27
+
28
+ def remove_edge_node(node)
29
+ index = @edge_nodes.find_index { |n| n.key == node.key }
30
+
31
+ return unless index
32
+
33
+ @edge_nodes.delete(index)
34
+ end
35
+
36
+ def inspect
37
+ format "#<%<class>s:%<id>#018x @key=%<key>s @cost=%<cost>s @edge_nodes=%<edge_nodes>s>",
38
+ class: self.class,
39
+ id: object_id << 1,
40
+ key: key.inspect,
41
+ cost: cost.inspect,
42
+ edge_nodes: edge_nodes.map { |n| n&.key }
43
+ end
44
+ end
45
+
46
+ def initialize(nodes: {})
47
+ @nodes = nodes
48
+ end
49
+
50
+ def node(key)
51
+ @nodes[key]
52
+ end
53
+
54
+ def node_at(x, y, z = 0)
55
+ key = format(Node::KEY_FORMAT, x, y, z)
56
+ @nodes[key]
57
+ end
58
+
59
+ def neighbors(node)
60
+ node.edge_nodes
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class GraphSearch
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class AStarSearch < GraphSearch
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,27 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class BreadthFirstSearch < GraphSearch
4
+ def self.search(graph:, start:, goal: nil)
5
+ frontier = []
6
+ came_from = {}
7
+
8
+ frontier.push(start)
9
+ came_from[start.key] = start
10
+
11
+ while(current = frontier.shift)
12
+ # early exit if we reach our goal
13
+ break if current == goal
14
+
15
+ graph.neighbors(current).each do |neighbor|
16
+ next if came_from[neighbor.key]
17
+
18
+ frontier.push(neighbor)
19
+ came_from[neighbor.key] = current
20
+ end
21
+ end
22
+
23
+ came_from
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class DijkstraSearch < GraphSearch
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,52 @@
1
+ module CyberarmEngine
2
+ module Pathfinding
3
+ class Grid < Graph
4
+ def add_node(x:, y:, z: 0, cost: 0, replace: false)
5
+ key = format(Node::KEY_FORMAT, x, y, z)
6
+ node = @nodes[key]
7
+
8
+ raise "Node at: #{key} already exists! (#{node})" if node && !replace
9
+
10
+ node = Node.new(x: x, y: y, z: z, cost: cost)
11
+ @nodes[key] = node
12
+
13
+ update_node_edge_nodes(node)
14
+ node
15
+ end
16
+
17
+ def remove_node(node)
18
+ update_node_edge_nodes(node, remove_node: true)
19
+
20
+ @nodes.delete(node.key)
21
+ end
22
+
23
+ def update_node_edge_nodes(node, remove_node: false)
24
+ x = node.x
25
+ y = node.y
26
+ z = node.z
27
+
28
+ # add node to neighbors
29
+ # 2D
30
+ left = @nodes[format(Node::KEY_FORMAT, x - 1, y, z)]
31
+ right = @nodes[format(Node::KEY_FORMAT, x + 1, y, z)]
32
+ top = @nodes[format(Node::KEY_FORMAT, x, y - 1, z)]
33
+ bottom = @nodes[format(Node::KEY_FORMAT, x, y + 1, z)]
34
+ # 3D
35
+ front = @nodes[format(Node::KEY_FORMAT, x, y, z - 1)]
36
+ back = @nodes[format(Node::KEY_FORMAT, x, y, z + 1)]
37
+
38
+ possible_edge_nodes = [left, right, top, bottom, front, back]
39
+
40
+ if remove_node
41
+ possible_edge_nodes.each { |n| n&.remove_edge_node(node) }
42
+ else
43
+ possible_edge_nodes.each { |n| n&.add_edge_node(node) }
44
+ end
45
+ end
46
+
47
+ def update_edge_nodes
48
+ @nodes.each { |k, n| update_node_edge_nodes(n) }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -9,7 +9,7 @@ module CyberarmEngine
9
9
  end
10
10
 
11
11
  def okay?
12
- !@error
12
+ !error?
13
13
  end
14
14
 
15
15
  def error?
@@ -17,4 +17,3 @@ module CyberarmEngine
17
17
  end
18
18
  end
19
19
  end
20
-