cyberarm_engine 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -1
  4. data/Rakefile +1 -1
  5. data/assets/textures/default.png +0 -0
  6. data/cyberarm_engine.gemspec +10 -8
  7. data/lib/cyberarm_engine.rb +13 -2
  8. data/lib/cyberarm_engine/animator.rb +6 -4
  9. data/lib/cyberarm_engine/background.rb +19 -15
  10. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  11. data/lib/cyberarm_engine/bounding_box.rb +18 -18
  12. data/lib/cyberarm_engine/cache.rb +4 -0
  13. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  14. data/lib/cyberarm_engine/common.rb +13 -13
  15. data/lib/cyberarm_engine/config_file.rb +2 -2
  16. data/lib/cyberarm_engine/game_object.rb +63 -72
  17. data/lib/cyberarm_engine/game_state.rb +6 -3
  18. data/lib/cyberarm_engine/model.rb +207 -0
  19. data/lib/cyberarm_engine/model/material.rb +21 -0
  20. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  21. data/lib/cyberarm_engine/model/parser.rb +74 -0
  22. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  23. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  24. data/lib/cyberarm_engine/model_cache.rb +31 -0
  25. data/lib/cyberarm_engine/opengl.rb +28 -0
  26. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  27. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  28. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  29. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  30. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  31. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  32. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  33. data/lib/cyberarm_engine/{shader.rb → opengl/shader.rb} +51 -43
  34. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  35. data/lib/cyberarm_engine/ray.rb +5 -5
  36. data/lib/cyberarm_engine/stats.rb +2 -2
  37. data/lib/cyberarm_engine/text.rb +41 -27
  38. data/lib/cyberarm_engine/timer.rb +1 -1
  39. data/lib/cyberarm_engine/transform.rb +43 -20
  40. data/lib/cyberarm_engine/ui/border_canvas.rb +4 -3
  41. data/lib/cyberarm_engine/ui/dsl.rb +25 -11
  42. data/lib/cyberarm_engine/ui/element.rb +30 -20
  43. data/lib/cyberarm_engine/ui/elements/button.rb +86 -16
  44. data/lib/cyberarm_engine/ui/elements/check_box.rb +1 -1
  45. data/lib/cyberarm_engine/ui/elements/container.rb +44 -20
  46. data/lib/cyberarm_engine/ui/elements/edit_box.rb +175 -2
  47. data/lib/cyberarm_engine/ui/elements/edit_line.rb +121 -37
  48. data/lib/cyberarm_engine/ui/elements/flow.rb +1 -1
  49. data/lib/cyberarm_engine/ui/elements/image.rb +12 -9
  50. data/lib/cyberarm_engine/ui/elements/label.rb +93 -14
  51. data/lib/cyberarm_engine/ui/elements/list_box.rb +64 -2
  52. data/lib/cyberarm_engine/ui/elements/progress.rb +5 -5
  53. data/lib/cyberarm_engine/ui/elements/radio.rb +1 -1
  54. data/lib/cyberarm_engine/ui/elements/slider.rb +13 -16
  55. data/lib/cyberarm_engine/ui/elements/stack.rb +1 -1
  56. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +27 -19
  57. data/lib/cyberarm_engine/ui/event.rb +7 -7
  58. data/lib/cyberarm_engine/ui/gui_state.rb +44 -10
  59. data/lib/cyberarm_engine/ui/style.rb +10 -9
  60. data/lib/cyberarm_engine/ui/theme.rb +28 -20
  61. data/lib/cyberarm_engine/vector.rb +33 -30
  62. data/lib/cyberarm_engine/version.rb +2 -2
  63. data/lib/cyberarm_engine/window.rb +27 -18
  64. metadata +65 -15
@@ -0,0 +1,4 @@
1
+ module CyberarmEngine
2
+ module Cache
3
+ end
4
+ end
@@ -0,0 +1,121 @@
1
+ module CyberarmEngine
2
+ module Cache
3
+ class DownloadManager
4
+ attr_reader :downloads
5
+
6
+ def initialize(max_parallel_downloads: 4)
7
+ @max_parallel_downloads = max_parallel_downloads
8
+ @downloads = []
9
+ end
10
+
11
+ def download(url:, save_as: nil, &callback)
12
+ uri = URI(url)
13
+ save_as ||= "filename_path" # TODO: if no save_as path is provided, then get one from the Cache controller
14
+
15
+ @downloads << Download.new(uri: uri, save_as: save_as, callback: callback)
16
+ end
17
+
18
+ def status
19
+ if active_downloads > 0
20
+ :busy
21
+ else
22
+ :idle
23
+ end
24
+ end
25
+
26
+ def progress
27
+ remaining_bytes = @downloads.map { |d| d.remaining_bytes }.sum
28
+ total_bytes = @downloads.map { |d| d.total_bytes }.sum
29
+
30
+ v = 1.0 - (remaining_bytes.to_f / total_bytes)
31
+ return 0.0 if v.nan?
32
+
33
+ v
34
+ end
35
+
36
+ def active_downloads
37
+ @downloads.select { |d| %i[pending downloading].include?(d.status) }
38
+ end
39
+
40
+ def update
41
+ @downloads.each do |download|
42
+ if download.status == :pending && active_downloads.size <= @max_parallel_downloads
43
+ download.status = :downloading
44
+ Thread.start { download.download }
45
+ end
46
+ end
47
+ end
48
+
49
+ def prune
50
+ @downloads.delete_if { |d| d.status == :finished || d.status == :failed }
51
+ end
52
+
53
+ class Download
54
+ attr_accessor :status
55
+ attr_reader :uri, :save_as, :callback, :remaining_bytes, :total_downloaded_bytes, :total_bytes,
56
+ :error_message, :started_at, :finished_at
57
+
58
+ def initialize(uri:, save_as:, callback: nil)
59
+ @uri = uri
60
+ @save_as = save_as
61
+ @callback = callback
62
+
63
+ @status = :pending
64
+
65
+ @remaining_bytes = 0.0
66
+ @total_downloaded_bytes = 0.0
67
+ @total_bytes = 0.0
68
+
69
+ @error_message = ""
70
+ end
71
+
72
+ def progress
73
+ v = 1.0 - (@remaining_bytes.to_f / total_bytes)
74
+ return 0.0 if v.nan?
75
+
76
+ v
77
+ end
78
+
79
+ def download
80
+ @status = :downloading
81
+ @started_at = Time.now # TODO: monotonic time
82
+
83
+ io = File.open(@save_as, "w")
84
+ streamer = lambda do |chunk, remaining_bytes, total_bytes|
85
+ io.write(chunk)
86
+
87
+ @remaining_bytes = remaining_bytes.to_f
88
+ @total_downloaded_bytes += chunk.size
89
+ @total_bytes = total_bytes.to_f
90
+ end
91
+
92
+ begin
93
+ response = Excon.get(
94
+ @uri.to_s,
95
+ middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::RedirectFollower],
96
+ response_block: streamer
97
+ )
98
+
99
+ if response.status == 200
100
+ @status = :finished
101
+ @finished_at = Time.now # TODO: monotonic time
102
+ @callback.call(self) if @callback
103
+ else
104
+ @error_message = "Got a non 200 HTTP status of #{response.status}"
105
+ @status = :failed
106
+ @finished_at = Time.now # TODO: monotonic time
107
+ @callback.call(self) if @callback
108
+ end
109
+ rescue StandardError => e # TODO: cherrypick errors to cature
110
+ @status = :failed
111
+ @finished_at = Time.now # TODO: monotonic time
112
+ @error_message = e.message
113
+ @callback.call(self) if @callback
114
+ end
115
+ ensure
116
+ io.close if io
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,6 +1,6 @@
1
1
  module CyberarmEngine
2
2
  module Common
3
- def push_state(klass, options={})
3
+ def push_state(klass, options = {})
4
4
  window.push_state(klass, options)
5
5
  end
6
6
 
@@ -20,7 +20,7 @@ module CyberarmEngine
20
20
  window.show_cursor
21
21
  end
22
22
 
23
- def show_cursor=boolean
23
+ def show_cursor=(boolean)
24
24
  window.show_cursor = boolean
25
25
  end
26
26
 
@@ -34,24 +34,24 @@ module CyberarmEngine
34
34
 
35
35
  def lighten(color, amount = 25)
36
36
  if defined?(color.alpha)
37
- return Gosu::Color.rgba(color.red + amount, color.green + amount, color.blue + amount, color.alpha)
37
+ Gosu::Color.rgba(color.red + amount, color.green + amount, color.blue + amount, color.alpha)
38
38
  else
39
- return Gosu::Color.rgb(color.red + amount, color.green + amount, color.blue + amount)
39
+ Gosu::Color.rgb(color.red + amount, color.green + amount, color.blue + amount)
40
40
  end
41
41
  end
42
42
 
43
43
  def darken(color, amount = 25)
44
44
  if defined?(color.alpha)
45
- return Gosu::Color.rgba(color.red - amount, color.green - amount, color.blue - amount, color.alpha)
45
+ Gosu::Color.rgba(color.red - amount, color.green - amount, color.blue - amount, color.alpha)
46
46
  else
47
- return Gosu::Color.rgb(color.red - amount, color.green - amount, color.blue - amount)
47
+ Gosu::Color.rgb(color.red - amount, color.green - amount, color.blue - amount)
48
48
  end
49
49
  end
50
50
 
51
51
  def opacity(color, ratio = 1.0)
52
52
  alpha = 255 * ratio
53
53
 
54
- return Gosu::Color.rgba(color.red, color.green, color.blue, alpha)
54
+ Gosu::Color.rgba(color.red, color.green, color.blue, alpha)
55
55
  end
56
56
 
57
57
  def get_asset(path, hash, klass, retro = false, tileable = false)
@@ -65,16 +65,16 @@ module CyberarmEngine
65
65
 
66
66
  unless asset
67
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
68
+ instance = if klass == Gosu::Image
69
+ klass.new(path, retro: retro, tileable: tileable)
70
+ else
71
+ klass.new(path)
72
+ end
73
73
  hash[path] = instance
74
74
  asset = instance
75
75
  end
76
76
 
77
- return asset
77
+ asset
78
78
  end
79
79
 
80
80
  def get_image(path, retro: false, tileable: false)
@@ -10,7 +10,7 @@ module CyberarmEngine
10
10
  end
11
11
  end
12
12
 
13
- def []= *keys, value
13
+ def []=(*keys, value)
14
14
  last_key = keys.last
15
15
 
16
16
  if keys.size == 1
@@ -20,7 +20,7 @@ module CyberarmEngine
20
20
  hash = @data[keys.shift] ||= {}
21
21
 
22
22
  keys.each do |key|
23
- hash[key] ||= {}
23
+ hash = hash[key] ||= {}
24
24
  end
25
25
  end
26
26
 
@@ -5,55 +5,60 @@ module CyberarmEngine
5
5
  attr_accessor :image, :angle, :position, :velocity, :center_x, :center_y, :scale_x, :scale_y,
6
6
  :color, :mode, :options, :paused, :radius, :last_position
7
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
8
+
9
+ def initialize(options = {})
10
+ $window.current_state.add_game_object(self) if options[:auto_manage] || options[:auto_manage].nil?
12
11
 
13
12
  @options = options
14
13
  @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
14
+ x = options[:x] || 0
15
+ y = options[:y] || 0
16
+ z = options[:z] || 0
18
17
  @position = Vector.new(x, y, z)
19
18
  @velocity = Vector.new
20
19
  @last_position = Vector.new
21
- @angle = options[:angle] ? options[:angle] : 0
20
+ @angle = options[:angle] || 0
22
21
 
23
- @center_x = options[:center_x] ? options[:center_x] : 0.5
24
- @center_y = options[:center_y] ? options[:center_y] : 0.5
22
+ @center_x = options[:center_x] || 0.5
23
+ @center_y = options[:center_y] || 0.5
25
24
 
26
- @scale_x = options[:scale_x] ? options[:scale_x] : 1
27
- @scale_y = options[:scale_y] ? options[:scale_y] : 1
25
+ @scale_x = options[:scale_x] || 1
26
+ @scale_y = options[:scale_y] || 1
28
27
 
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
28
+ @color = options[:color] || Gosu::Color.argb(0xff_ffffff)
29
+ @alpha = options[:alpha] || 255
30
+ @mode = options[:mode] || :default
32
31
 
33
32
  @paused = false
34
33
  @speed = 0
35
34
  @debug_color = Gosu::Color::GREEN
36
- @world_center_point = Vector.new(0,0)
35
+ @world_center_point = Vector.new(0, 0)
37
36
 
38
37
  setup
39
38
 
40
- @debug_text = Text.new("", color: @debug_color, y: @position.y-(self.height*self.scale), z: 9999)
39
+ @debug_text = Text.new("", color: @debug_color, y: @position.y - (height * scale), z: 9999)
41
40
  @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
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
44
47
  end
45
48
  end
46
49
 
47
50
  def draw
48
51
  if @image
49
- @image.draw_rot(@position.x, @position.y, @position.z, @angle, @center_x, @center_y, @scale_x, @scale_y, @color, @mode)
52
+ @image.draw_rot(@position.x, @position.y, @position.z, @angle, @center_x, @center_y, @scale_x, @scale_y,
53
+ @color, @mode)
50
54
  end
51
55
 
52
56
  if $debug
53
57
  show_debug_heading
54
58
  $window.draw_circle(@position.x, @position.y, radius, 9999, @debug_color)
55
59
  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)
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)
57
62
  @debug_text.draw
58
63
  end
59
64
  end
@@ -64,13 +69,13 @@ module CyberarmEngine
64
69
 
65
70
  def debug_text(text)
66
71
  @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)
72
+ @debug_text.x = @position.x - (@debug_text.width / 2)
73
+ @debug_text.y = @position.y - (@debug_text.height + radius + height)
69
74
  end
70
75
 
71
76
  def scale
72
77
  if @scale_x == @scale_y
73
- return @scale_x
78
+ @scale_x
74
79
  else
75
80
  false
76
81
  # maths?
@@ -80,7 +85,7 @@ module CyberarmEngine
80
85
  def scale=(int)
81
86
  self.scale_x = int
82
87
  self.scale_y = int
83
- self.radius = ((@image.width+@image.height)/4)*self.scale
88
+ self.radius = ((@image.width + @image.height) / 4) * scale
84
89
  end
85
90
 
86
91
  def visible
@@ -97,16 +102,16 @@ module CyberarmEngine
97
102
  end
98
103
 
99
104
  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)
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)
102
107
  end
103
108
 
104
109
  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)
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)
107
112
  end
108
113
 
109
- def heading(ahead_by = 100, object = nil, angle_only = false)
114
+ def heading(ahead_by = 100, _object = nil, angle_only = false)
110
115
  direction = Gosu.angle(@last_position.x, @last_position.x, @position.x, position.y).gosu_to_radians
111
116
 
112
117
  _x = @position.x + (ahead_by * Math.cos(direction))
@@ -122,11 +127,11 @@ module CyberarmEngine
122
127
  end
123
128
 
124
129
  def width
125
- @image ? @image.width * self.scale : 0
130
+ @image ? @image.width * scale : 0
126
131
  end
127
132
 
128
133
  def height
129
- @image ? @image.height * self.scale : 0
134
+ @image ? @image.height * scale : 0
130
135
  end
131
136
 
132
137
  def pause
@@ -138,8 +143,8 @@ module CyberarmEngine
138
143
  end
139
144
 
140
145
  def rotate(int)
141
- self.angle+=int
142
- self.angle%=360
146
+ self.angle += int
147
+ self.angle %= 360
143
148
  end
144
149
 
145
150
  def alpha=(int) # 0-255
@@ -149,7 +154,7 @@ module CyberarmEngine
149
154
  end
150
155
 
151
156
  def draw_rect(x, y, width, height, color, z = 0)
152
- $window.draw_rect(x,y,width,height,color,z)
157
+ $window.draw_rect(x, y, width, height, color, z)
153
158
  end
154
159
 
155
160
  def button_up(id)
@@ -163,14 +168,14 @@ module CyberarmEngine
163
168
  best_distance = 100_000_000_000 # Huge default number
164
169
 
165
170
  game_object_class.all.each do |object|
166
- distance = Gosu::distance(self.x, self.y, object.x, object.y)
171
+ distance = Gosu.distance(x, y, object.x, object.y)
167
172
  if distance <= best_distance
168
173
  best_object = object
169
174
  best_distance = distance
170
175
  end
171
176
  end
172
177
 
173
- return best_object
178
+ best_object
174
179
  end
175
180
 
176
181
  def look_at(object)
@@ -178,31 +183,24 @@ module CyberarmEngine
178
183
  end
179
184
 
180
185
  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
186
+ distance = Gosu.distance(x, y, object.x, object.y)
187
+ distance <= radius + object.radius
187
188
  end
188
189
 
189
190
  # Duplication... so DRY.
190
- def each_circle_collision(object, resolve_with = :width, &block)
191
+ def each_circle_collision(object, _resolve_with = :width, &block)
191
192
  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
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
197
196
  end
198
197
  else
199
- list = $window.current_state.game_objects.select {|i| i.class == object}
198
+ list = $window.current_state.game_objects.select { |i| i.instance_of?(object) }
200
199
  list.each do |o|
201
200
  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
201
+
202
+ distance = Gosu.distance(x, y, o.x, o.y)
203
+ block.call(self, o) if distance <= radius + o.radius && block
206
204
  end
207
205
  end
208
206
  end
@@ -210,35 +208,30 @@ module CyberarmEngine
210
208
  def destroy
211
209
  if $window.current_state
212
210
  $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
211
+ $window.current_state.game_objects.delete(o) if o.is_a?(self.class) && o == self
216
212
  end
217
213
  end
218
214
  end
219
215
 
220
216
  # NOTE: This could be implemented more reliably
221
217
  def all
222
- INSTANCES.select {|i| i.class == self}
218
+ INSTANCES.select { |i| i.instance_of?(self) }
223
219
  end
224
220
 
225
- def self.each_circle_collision(object, resolve_with = :width, &block)
221
+ def self.each_circle_collision(object, _resolve_with = :width, &block)
226
222
  if object.class != Class && object.instance_of?(object.class)
227
- $window.current_state.game_objects.select {|i| i.class == self}.each do |o|
223
+ $window.current_state.game_objects.select { |i| i.instance_of?(self) }.each do |o|
228
224
  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
225
+ block.call(o, object) if distance <= o.radius + object.radius && block
232
226
  end
233
227
  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}
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) }
236
230
  lista.product(listb).each do |o, o2|
237
231
  next if o == o2
232
+
238
233
  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
234
+ block.call(o, o2) if distance <= o.radius + o2.radius && block
242
235
  end
243
236
  end
244
237
  end
@@ -247,11 +240,9 @@ module CyberarmEngine
247
240
  INSTANCES.clear
248
241
  if $window.current_state
249
242
  $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
243
+ $window.current_state.game_objects.delete(o) if o.is_a?(self.class)
253
244
  end
254
245
  end
255
246
  end
256
247
  end
257
- end
248
+ end