cyberarm_engine 0.13.1 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -1
  4. data/README.md +1 -1
  5. data/Rakefile +1 -1
  6. data/assets/textures/default.png +0 -0
  7. data/cyberarm_engine.gemspec +12 -9
  8. data/lib/cyberarm_engine.rb +20 -5
  9. data/lib/cyberarm_engine/animator.rb +6 -4
  10. data/lib/cyberarm_engine/background.rb +19 -15
  11. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  12. data/lib/cyberarm_engine/bounding_box.rb +18 -18
  13. data/lib/cyberarm_engine/cache.rb +4 -0
  14. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  15. data/lib/cyberarm_engine/common.rb +16 -16
  16. data/lib/cyberarm_engine/config_file.rb +2 -2
  17. data/lib/cyberarm_engine/game_object.rb +63 -72
  18. data/lib/cyberarm_engine/game_state.rb +6 -3
  19. data/lib/cyberarm_engine/model.rb +207 -0
  20. data/lib/cyberarm_engine/model/material.rb +21 -0
  21. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  22. data/lib/cyberarm_engine/model/parser.rb +74 -0
  23. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  24. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  25. data/lib/cyberarm_engine/model_cache.rb +31 -0
  26. data/lib/cyberarm_engine/opengl.rb +28 -0
  27. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  28. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  29. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  30. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  31. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  32. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  33. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  34. data/lib/cyberarm_engine/{shader.rb → opengl/shader.rb} +51 -43
  35. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  36. data/lib/cyberarm_engine/ray.rb +5 -5
  37. data/lib/cyberarm_engine/stats.rb +21 -0
  38. data/lib/cyberarm_engine/text.rb +45 -31
  39. data/lib/cyberarm_engine/timer.rb +1 -1
  40. data/lib/cyberarm_engine/transform.rb +43 -20
  41. data/lib/cyberarm_engine/ui/border_canvas.rb +4 -3
  42. data/lib/cyberarm_engine/ui/dsl.rb +49 -10
  43. data/lib/cyberarm_engine/ui/element.rb +73 -21
  44. data/lib/cyberarm_engine/ui/elements/button.rb +121 -28
  45. data/lib/cyberarm_engine/ui/elements/check_box.rb +25 -33
  46. data/lib/cyberarm_engine/ui/elements/container.rb +113 -33
  47. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  48. data/lib/cyberarm_engine/ui/elements/edit_line.rb +145 -45
  49. data/lib/cyberarm_engine/ui/elements/flow.rb +1 -3
  50. data/lib/cyberarm_engine/ui/elements/image.rb +32 -12
  51. data/lib/cyberarm_engine/ui/elements/label.rb +122 -16
  52. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -0
  53. data/lib/cyberarm_engine/ui/elements/progress.rb +6 -5
  54. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  55. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  56. data/lib/cyberarm_engine/ui/elements/stack.rb +1 -3
  57. data/lib/cyberarm_engine/ui/elements/text_block.rb +156 -0
  58. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +40 -31
  59. data/lib/cyberarm_engine/ui/event.rb +7 -7
  60. data/lib/cyberarm_engine/ui/gui_state.rb +118 -6
  61. data/lib/cyberarm_engine/ui/style.rb +10 -9
  62. data/lib/cyberarm_engine/ui/theme.rb +84 -22
  63. data/lib/cyberarm_engine/vector.rb +33 -30
  64. data/lib/cyberarm_engine/version.rb +2 -2
  65. data/lib/cyberarm_engine/{engine.rb → window.rb} +32 -19
  66. metadata +87 -18
  67. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
@@ -2,28 +2,41 @@ module CyberarmEngine
2
2
  class Element
3
3
  class EditLine < Button
4
4
  def initialize(text, options = {}, block = nil)
5
+ @filter = options.delete(:filter)
5
6
  super(text, options, block)
6
7
 
7
8
  @type = default(:type)
8
9
 
9
10
  @caret_width = default(:caret_width)
10
- @caret_height= @text.height
11
+ @caret_height = @text.textobject.height
11
12
  @caret_color = default(:caret_color)
12
13
  @caret_interval = default(:caret_interval)
13
14
  @caret_last_interval = Gosu.milliseconds
14
- @show_caret = true
15
+ @show_caret = true
15
16
 
16
17
  @text_input = Gosu::TextInput.new
17
18
  @text_input.text = text
19
+ @last_text_value = text
20
+
21
+ if @filter && @filter.respond_to?(:call)
22
+ @text_input.instance_variable_set(:@filter, @filter)
23
+
24
+ def @text_input.filter(text_in)
25
+ @filter.call(text_in)
26
+ end
27
+ end
18
28
 
19
29
  @offset_x = 0
30
+ @offset_y = 0
20
31
 
21
- return self
32
+ event(:begin_drag)
33
+ event(:drag_update)
34
+ event(:end_drag)
22
35
  end
23
36
 
24
37
  def render
25
- Gosu.clip_to(@text.x, @text.y, @style.width, @text.height) do
26
- Gosu.translate(-@offset_x, 0) do
38
+ Gosu.clip_to(@text.x, @text.y, @width, @height) do
39
+ Gosu.translate(-@offset_x, -@offset_y) do
27
40
  draw_selection
28
41
  draw_caret if @focus && @show_caret
29
42
  draw_text
@@ -31,6 +44,10 @@ module CyberarmEngine
31
44
  end
32
45
  end
33
46
 
47
+ def draw_text
48
+ @text.draw(:draw_text)
49
+ end
50
+
34
51
  def draw_caret
35
52
  Gosu.draw_rect(caret_position, @text.y, @caret_width, @caret_height, @caret_color, @z)
36
53
  end
@@ -42,10 +59,18 @@ module CyberarmEngine
42
59
  end
43
60
 
44
61
  def update
45
- if @type == :password
46
- @text.text = default(:password_character) * @text_input.text.length
47
- else
48
- @text.text = @text_input.text
62
+ @text.text = if @type == :password
63
+ default(:password_character) * @text_input.text.length
64
+ else
65
+ @text_input.text
66
+ end
67
+
68
+ if @last_text_value != value
69
+ @last_text_value = value
70
+ @show_caret = true
71
+ @caret_last_interval = Gosu.milliseconds
72
+
73
+ publish(:changed, value)
49
74
  end
50
75
 
51
76
  if Gosu.milliseconds >= @caret_last_interval + @caret_interval
@@ -57,15 +82,60 @@ module CyberarmEngine
57
82
  keep_caret_visible
58
83
  end
59
84
 
60
- def move_caret_to_mouse(mouse_x)
61
- 1.upto(@text.text.length) do |i|
62
- if mouse_x < @text.x + @text.textobject.text_width(@text.text[0...i])
63
- @text_input.caret_pos = @text_input.selection_start = i - 1;
64
- return
85
+ def button_down(id)
86
+ handle_keyboard_shortcuts(id)
87
+ end
88
+
89
+ def handle_keyboard_shortcuts(id)
90
+ return unless @focus && @enabled
91
+
92
+ if Gosu.button_down?(Gosu::KB_LEFT_CONTROL) || Gosu.button_down?(Gosu::KB_RIGHT_CONTROL)
93
+ case id
94
+ when Gosu::KB_A
95
+ @text_input.selection_start = 0
96
+ @text_input.caret_pos = @text_input.text.length
97
+
98
+ when Gosu::KB_C
99
+ if @text_input.selection_start < @text_input.caret_pos
100
+ Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
101
+ else
102
+ Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
103
+ end
104
+
105
+ when Gosu::KB_X
106
+ chars = @text_input.text.chars
107
+
108
+ if @text_input.selection_start < @text_input.caret_pos
109
+ Clipboard.copy(@text_input.text[@text_input.selection_start...@text_input.caret_pos])
110
+ chars.slice!(@text_input.selection_start, @text_input.caret_pos)
111
+ else
112
+ Clipboard.copy(@text_input.text[@text_input.caret_pos...@text_input.selection_start])
113
+ chars.slice!(@text_input.caret_pos, @text_input.selection_start)
114
+ end
115
+
116
+ @text_input.text = chars.join
117
+
118
+ when Gosu::KB_V
119
+ if instance_of?(EditLine) # EditLine assumes a single line of text
120
+ @text_input.text = @text_input.text.insert(@text_input.caret_pos,
121
+ Clipboard.paste.encode("UTF-8").gsub("\n", ""))
122
+ else
123
+ @text_input.text = @text_input.text.insert(@text_input.caret_pos, Clipboard.paste.encode("UTF-8"))
124
+ end
65
125
  end
66
126
  end
127
+ end
67
128
 
68
- @text_input.caret_pos = @text_input.selection_start = @text_input.text.length
129
+ def caret_position_under_mouse(mouse_x)
130
+ 1.upto(@text.text.length) do |i|
131
+ return i - 1 if mouse_x < @text.x - @offset_x + @text.width(@text.text[0...i])
132
+ end
133
+
134
+ @text_input.text.length
135
+ end
136
+
137
+ def move_caret_to_mouse(mouse_x, _mouse_y)
138
+ @text_input.caret_pos = @text_input.selection_start = caret_position_under_mouse(mouse_x)
69
139
  end
70
140
 
71
141
  def keep_caret_visible
@@ -74,25 +144,21 @@ module CyberarmEngine
74
144
  @last_text ||= "/\\"
75
145
  @last_pos ||= -1
76
146
 
77
- puts "caret pos: #{caret_pos}, width: #{@width}, offset: #{@offset_x}" if (@last_text != @text.text) || (@last_pos != caret_pos)
78
-
79
147
  @last_text = @text.text
80
148
  @last_pos = caret_pos
81
149
 
82
-
83
150
  if caret_pos.between?(@offset_x, @width + @offset_x)
84
151
  # Do nothing
85
152
 
86
153
  elsif caret_pos < @offset_x
87
- if caret_pos > @width
88
- @offset_x = caret_pos + @width
89
- else
90
- @offset_x = 0
91
- end
154
+ @offset_x = if caret_pos > @width
155
+ caret_pos + @width
156
+ else
157
+ 0
158
+ end
92
159
 
93
160
  elsif caret_pos > @width
94
161
  @offset_x = caret_pos - @width
95
- puts "triggered"
96
162
 
97
163
  else
98
164
  # Reset to Zero
@@ -100,6 +166,22 @@ module CyberarmEngine
100
166
  end
101
167
  end
102
168
 
169
+ def caret_position
170
+ text_input_position_for(:caret_pos)
171
+ end
172
+
173
+ def selection_start_position
174
+ text_input_position_for(:selection_start)
175
+ end
176
+
177
+ def text_input_position_for(method)
178
+ if @type == :password
179
+ @text.x + @text.width(default(:password_character) * @text_input.text[0...@text_input.send(method)].length)
180
+ else
181
+ @text.x + @text.width(@text_input.text[0...@text_input.send(method)])
182
+ end
183
+ end
184
+
103
185
  def left_mouse_button(sender, x, y)
104
186
  super
105
187
  window.text_input = @text_input
@@ -107,12 +189,12 @@ module CyberarmEngine
107
189
  @caret_last_interval = Gosu.milliseconds
108
190
  @show_caret = true
109
191
 
110
- move_caret_to_mouse(x)
192
+ move_caret_to_mouse(x, y)
111
193
 
112
- return :handled
194
+ :handled
113
195
  end
114
196
 
115
- def enter(sender)
197
+ def enter(_sender)
116
198
  if @focus
117
199
  @style.background_canvas.background = default(:active, :background)
118
200
  @text.color = default(:active, :color)
@@ -121,40 +203,54 @@ module CyberarmEngine
121
203
  @text.color = default(:hover, :color)
122
204
  end
123
205
 
124
- return :handled
206
+ :handled
125
207
  end
126
208
 
127
209
  def leave(sender)
128
- unless @focus
129
- super
130
- end
210
+ super unless @focus
211
+
212
+ :handled
213
+ end
214
+
215
+ def focus(sender)
216
+ @focus = true
217
+ @style.background_canvas.background = default(:active, :background)
218
+ @text.color = default(:active, :color)
219
+ window.text_input = @text_input
220
+ @text_input.caret_pos = @text_input.selection_start = @text_input.text.length
131
221
 
132
- return :handled
222
+ :handled
133
223
  end
134
224
 
135
- def blur(sender)
225
+ def blur(_sender)
136
226
  @focus = false
137
227
  @style.background_canvas.background = default(:background)
138
228
  @text.color = default(:color)
139
229
  window.text_input = nil
140
230
 
141
- return :handled
231
+ :handled
142
232
  end
143
233
 
144
- def caret_position
145
- text_input_position_for(:caret_pos)
234
+ def draggable?(button)
235
+ button == :left
146
236
  end
147
237
 
148
- def selection_start_position
149
- text_input_position_for(:selection_start)
238
+ def begin_drag(_sender, x, _y, _button)
239
+ @drag_start = x
240
+ @offset_drag_start = @offset_x
241
+ @drag_caret_position = @text_input.caret_pos
242
+
243
+ :handled
150
244
  end
151
245
 
152
- def text_input_position_for(method)
153
- if @type == :password
154
- @text.x + @text.textobject.text_width(default(:password_character) * @text_input.text[0..@text_input.send(method)].length)
155
- else
156
- @text.x + @text.textobject.text_width(@text_input.text[0..@text_input.send(method)])
157
- end
246
+ def drag_update(_sender, x, _y, _button)
247
+ @text_input.caret_pos = caret_position_under_mouse(x)
248
+
249
+ :handled
250
+ end
251
+
252
+ def end_drag(_sender, _x, _y, _button)
253
+ :handled
158
254
  end
159
255
 
160
256
  def recalculate
@@ -167,6 +263,10 @@ module CyberarmEngine
167
263
  def value
168
264
  @text_input.text
169
265
  end
266
+
267
+ def value=(string)
268
+ @text_input.text = string
269
+ end
170
270
  end
171
271
  end
172
- end
272
+ end
@@ -1,8 +1,6 @@
1
1
  module CyberarmEngine
2
2
  class Element
3
3
  class Flow < Container
4
- include Common
5
-
6
4
  def layout
7
5
  @children.each do |child|
8
6
  if fits_on_line?(child)
@@ -14,4 +12,4 @@ module CyberarmEngine
14
12
  end
15
13
  end
16
14
  end
17
- end
15
+ end
@@ -1,12 +1,15 @@
1
1
  module CyberarmEngine
2
2
  class Element
3
3
  class Image < Element
4
- def initialize(path, options = {}, block = nil)
4
+ def initialize(path_or_image, options = {}, block = nil)
5
5
  super(options, block)
6
- @path = path
6
+ @path = path_or_image if path_or_image.is_a?(String)
7
7
 
8
- @image = Gosu::Image.new(path, retro: @options[:image_retro])
9
- @scale_x, @scale_y = 1, 1
8
+ @image = Gosu::Image.new(path_or_image, retro: @options[:retro], tileable: @options[:tileable]) if @path
9
+ @image = path_or_image unless @path
10
+
11
+ @scale_x = 1
12
+ @scale_y = 1
10
13
  end
11
14
 
12
15
  def render
@@ -14,18 +17,19 @@ module CyberarmEngine
14
17
  @style.border_thickness_left + @style.padding_left + @x,
15
18
  @style.border_thickness_top + @style.padding_top + @y,
16
19
  @z + 2,
17
- @scale_x, @scale_y) # TODO: Add color support?
20
+ @scale_x, @scale_y, @style.color
21
+ )
18
22
  end
19
23
 
20
- def clicked_left_mouse_button(sender, x, y)
24
+ def clicked_left_mouse_button(_sender, _x, _y)
21
25
  @block.call(self) if @block
22
26
 
23
- return :handled
27
+ :handled
24
28
  end
25
29
 
26
30
  def recalculate
27
31
  _width = dimensional_size(@style.width, :width)
28
- _height= dimensional_size(@style.height,:height)
32
+ _height = dimensional_size(@style.height, :height)
29
33
 
30
34
  if _width && _height
31
35
  @scale_x = _width.to_f / @image.width
@@ -37,16 +41,32 @@ module CyberarmEngine
37
41
  @scale_y = _height.to_f / @image.height
38
42
  @scale_x = @scale_y
39
43
  else
40
- @scale_x, @scale_y = 1, 1
44
+ @scale_x = 1
45
+ @scale_y = 1
41
46
  end
42
47
 
43
- @width = _width ? _width : @image.width.round * @scale_x
44
- @height= _height ? _height : @image.height.round * @scale_y
48
+ @width = _width || @image.width.round * @scale_x
49
+ @height = _height || @image.height.round * @scale_y
50
+
51
+ update_background
45
52
  end
46
53
 
47
54
  def value
55
+ @image
56
+ end
57
+
58
+ def value=(path_or_image, retro: false, tileable: false)
59
+ @path = path_or_image if path_or_image.is_a?(String)
60
+
61
+ @image = Gosu::Image.new(path_or_image, retro: retro, tileable: tileable) if @path
62
+ @image = path_or_image unless @path
63
+
64
+ recalculate
65
+ end
66
+
67
+ def path
48
68
  @path
49
69
  end
50
70
  end
51
71
  end
52
- end
72
+ end
@@ -1,50 +1,156 @@
1
1
  module CyberarmEngine
2
2
  class Element
3
- class Label < Element
3
+ class TextBlock < Element
4
4
  def initialize(text, options = {}, block = nil)
5
5
  super(options, block)
6
6
 
7
- @text = Text.new(text, font: @options[:font], z: @z, color: @options[:color], size: @options[:text_size], shadow: @options[:text_shadow])
7
+ @text = Text.new(
8
+ text, font: @options[:font], z: @z, color: @options[:color],
9
+ size: @options[:text_size], shadow: @options[:text_shadow],
10
+ shadow_size: @options[:text_shadow_size],
11
+ shadow_color: @options[:text_shadow_color]
12
+ )
13
+
14
+ @raw_text = text
8
15
  end
9
16
 
10
17
  def render
11
18
  @text.draw
12
19
  end
13
20
 
14
- def clicked_left_mouse_button(sender, x, y)
15
- @block.call(self) if @block
21
+ def clicked_left_mouse_button(_sender, _x, _y)
22
+ @block&.call(self) if @enabled
16
23
 
17
- return :handled
24
+ # return :handled
18
25
  end
19
26
 
20
27
  def recalculate
21
- @width, @height = 0, 0
28
+ @width = 0
29
+ @height = 0
30
+
31
+ _width = dimensional_size(@style.width, :width)
32
+ _height = dimensional_size(@style.height, :height)
22
33
 
23
- _width = dimensional_size(@style.width, :width)
24
- _height= dimensional_size(@style.height,:height)
34
+ handle_text_wrapping(_width)
25
35
 
26
- @width = _width ? _width : @text.width.round
27
- @height= _height ? _height : @text.height.round
36
+ @width = _width || @text.width.round
37
+ @height = _height || @text.height.round
28
38
 
29
- @text.x = @style.border_thickness_left + @style.padding_left + @x
30
- @text.y = @style.border_thickness_top + @style.padding_top + @y
39
+ @text.y = @style.border_thickness_top + @style.padding_top + @y
31
40
  @text.z = @z + 3
32
41
 
42
+ if (text_alignment = @options[:text_align])
43
+ case text_alignment
44
+ when :left
45
+ @text.x = @style.border_thickness_left + @style.padding_left + @x
46
+ when :center
47
+ @text.x = if @text.width <= outer_width
48
+ @x + outer_width / 2 - @text.width / 2
49
+ else # Act as left aligned
50
+ @style.border_thickness_left + @style.padding_left + @x
51
+ end
52
+ when :right
53
+ @text.x = @x + outer_width - (@text.width + @style.border_thickness_right + @style.padding_right)
54
+ end
55
+ end
56
+
33
57
  update_background
34
58
  end
35
59
 
60
+ def handle_text_wrapping(max_width)
61
+ max_width ||= @parent&.width
62
+ max_width ||= @x - (window.width + noncontent_width)
63
+ wrap_behavior = style.text_wrap
64
+ copy = @raw_text.to_s.dup
65
+
66
+ if max_width >= line_width(copy[0]) && line_width(copy) > max_width && wrap_behavior != :none
67
+ breaks = []
68
+ line_start = 0
69
+ line_end = copy.length
70
+
71
+ while line_start != copy.length
72
+ if line_width(copy[line_start...line_end]) > max_width
73
+ line_end = ((line_end - line_start) / 2.0)
74
+ elsif line_end < copy.length && line_width(copy[line_start...line_end + 1]) < max_width
75
+ # To small, grow!
76
+ # TODO: find a more efficient way
77
+ line_end += 1
78
+
79
+ else # FOUND IT!
80
+ entering_line_end = line_end.floor
81
+ max_reach = line_end.floor - line_start < 63 ? line_end.floor - line_start : 63
82
+ reach = 0
83
+
84
+ if wrap_behavior == :word_wrap
85
+ max_reach.times do |i|
86
+ reach = i
87
+ break if copy[line_end.floor - i].to_s.match(/[[:punct:]]| /)
88
+ end
89
+
90
+ puts "Max width: #{max_width}/#{line_width(@raw_text)} Reach: {#{reach}/#{max_reach}} Line Start: #{line_start}/#{line_end.floor} (#{copy.length}|#{@raw_text.length}) [#{entering_line_end}] '#{copy}' {#{copy[line_start...line_end]}}"
91
+ line_end = line_end.floor - reach + 1 if reach != max_reach # Add +1 to walk in front of punctuation
92
+ end
93
+
94
+ breaks << line_end.floor
95
+ line_start = line_end.floor
96
+ line_end = copy.length
97
+
98
+ break if entering_line_end == copy.length || reach == max_reach
99
+ end
100
+ end
101
+
102
+ breaks.each_with_index do |pos, index|
103
+ copy.insert(pos + index, "\n") if pos + index >= 0 && pos + index < copy.length
104
+ end
105
+ end
106
+
107
+ @text.text = copy
108
+ end
109
+
110
+ def line_width(text)
111
+ (@x + @text.textobject.markup_width(text) + noncontent_width)
112
+ end
113
+
36
114
  def value
37
- @text.text
115
+ @raw_text
38
116
  end
39
117
 
40
118
  def value=(value)
41
- @text.text = value
119
+ @raw_text = value.to_s.chomp
42
120
 
43
- old_width, old_height = width, height
121
+ old_width = width
122
+ old_height = height
44
123
  recalculate
45
124
 
46
125
  root.gui_state.request_recalculate if old_width != width || old_height != height
126
+
127
+ publish(:changed, self.value)
47
128
  end
48
129
  end
130
+
131
+ class Banner < TextBlock
132
+ end
133
+
134
+ class Title < TextBlock
135
+ end
136
+
137
+ class Subtitle < TextBlock
138
+ end
139
+
140
+ class Tagline < TextBlock
141
+ end
142
+
143
+ class Caption < TextBlock
144
+ end
145
+
146
+ class Para < TextBlock
147
+ end
148
+
149
+ class Inscription < TextBlock
150
+ end
151
+
152
+ # TODO: Remove in version 0.16.0+
153
+ class Label < TextBlock
154
+ end
49
155
  end
50
- end
156
+ end