dxruby_tiled 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,69 +1,56 @@
1
- require "pry"
2
-
3
1
  module DXRuby
4
2
  module Tiled
5
- class IsometricLayer < Layer
6
-
7
- def draw(x, y, target = DXRuby::Window)
8
- tile_width2, tile_height2 = @map.tile_width / 2, @map.tile_height / 2
9
- tile_images = @map.tilesets.tile_images
10
- left, top = xy_at(x - @offset_x, y - @offset_x)
11
- left -= 1
12
- top -= 1
13
- x_range = 0..(target.width / tile_width2 / 2 + 1).floor
14
- y_range = 0..(target.height / tile_height2 + 3).floor
15
- off_x = x - @map.pixel_width / 2 + @offset_x
16
- off_y = y - tile_height2 + @offset_y
17
- alpha = (@opacity * 255).to_i
3
+ class IsometricLayer < TileLayer
4
+ def render(pos_x, pos_y, target = DXRuby::Window, z = @z_index, offset_x = 0, offset_y = 0, opacity = 1.0)
5
+ super
6
+
7
+ pos_x, pos_y = 0, 0 if @fixed
8
+ tile_width2, tile_height2 = @tile_width / 2, @tile_height / 2
9
+ off_x = offset_x + @offset_x - pos_x
10
+ off_y = offset_y + @offset_y - pos_y
11
+ left, top = xy_at(@tile_width - @tilesets.tile_right - off_x,
12
+ @tile_height - @tilesets.tile_bottom - off_y)
13
+ x_range = -1..((@render_target.width - @tilesets.tile_left) / @tile_width + 2)
14
+ y_range = -1..((@render_target.height - @tilesets.tile_top) * 2 / @tile_height + 4)
18
15
 
19
- y_range.each do |yy|
20
- x_range.each do |xx|
21
- x2 = left + xx + yy / 2
22
- y2 = top - xx + (yy + 1) / 2
23
- image = tile_images[self[x2, y2]]
24
- target.draw_alpha(x2 * tile_width2 - y2 * tile_width2 - off_x - image.width / 2,
25
- y2 * tile_height2 + x2 * tile_height2 - off_y - image.height / 2,
26
- image, alpha)
16
+ y_range.public_send(@renderorder_y ? :each : :reverse_each) do |tmp_y|
17
+ x_range.public_send(@renderorder_x ? :each : :reverse_each) do |tmp_x|
18
+ x = left + tmp_x + tmp_y / 2
19
+ y = top + (tmp_y + 1) / 2 - tmp_x
20
+ @tilesets[self[x, y]].render(
21
+ x * tile_width2 - y * tile_width2 + off_x,
22
+ y * tile_height2 + x * tile_height2 + off_y,
23
+ @render_target)
27
24
  end
28
25
  end
26
+
27
+ target.draw_alpha(0, 0, @render_target, @opacity * 255, z + @z_index)
29
28
  end
29
+ alias_method :draw, :render
30
30
 
31
- def xy_at(tmp_x, y)
32
- x = tmp_x - @map.pixel_width / 2
33
- return (1.0 * x / @map.tile_width + 1.0 * y / @map.tile_height).floor,
34
- (1.0 * y / @map.tile_height - 1.0 * x / @map.tile_width).floor
35
- end
36
-
37
- def at(x, y)
38
- tmp_x, tmp_y = xy_at(x, y)
39
- return self[tmp_x, tmp_y]
40
- end
41
-
42
- def change_at(x, y, value)
43
- tmp_x, tmp_y = xy_at(x, y)
44
- self[tmp_x, tmp_y] = value
31
+ def xy_at(pos_x, pos_y)
32
+ return (1.0 * pos_x / @tile_width + 1.0 * pos_y / @tile_height).floor,
33
+ (1.0 * pos_y / @tile_height - 1.0 * pos_x / @tile_width ).floor
45
34
  end
46
35
 
47
36
  def vertexs(x, y)
48
- x0 = @map.pixel_width / 2
49
- w, h = @map.tile_width / 2, @map.tile_height / 2
37
+ w, h = @tile_width / 2, @tile_height / 2
50
38
  return [
51
- [ x0 + x * w - y * w , y * h + x * h ],
52
- [ x0 + x * w - y * w - w, y * h + x * h + h ],
53
- [ x0 + x * w - y * w , y * h + x * h + h * 2 ],
54
- [ x0 + x * w - y * w + w, y * h + x * h + h ]
39
+ [ x * w - y * w , y * h + x * h ],
40
+ [ x * w - y * w - w, y * h + x * h + h ],
41
+ [ x * w - y * w , y * h + x * h + h * 2 ],
42
+ [ x * w - y * w + w, y * h + x * h + h ]
55
43
  ]
56
44
  end
57
45
 
58
46
 
59
47
  def self.pixel_width(map)
60
- return map.tile_width * (map.width + map.height) / 2
48
+ map.tile_width * (map.width + map.height) / 2
61
49
  end
62
50
 
63
51
  def self.pixel_height(map)
64
- return map.tile_height * (map.width + map.height) / 2
52
+ map.tile_height * (map.width + map.height) / 2
65
53
  end
66
-
67
54
  end
68
55
  end
69
56
  end
@@ -1,43 +1,44 @@
1
1
  module DXRuby
2
2
  module Tiled
3
- class OrthogonalLayer < Layer
4
-
5
- def draw(x, y, target = DXRuby::Window)
6
- tile_width, tile_height = @map.tile_width, @map.tile_height
7
- tile_images = @map.tilesets.tile_images
8
- left, top = xy_at(x - @offset_x, y - @offset_x)
9
- x_range = left..(left + (target.width / tile_width + 1).floor)
10
- y_range = top..(top + (target.height / tile_height + 1).floor)
11
- off_x = left * tile_width + (x - @offset_x) % tile_width - tile_width / 2
12
- off_y = top * tile_height + (y - @offset_y) % tile_height - tile_height / 2
13
- alpha = (@opacity * 255).floor
14
- x_range = x_range.to_a.reverse if @map.renderorder_x
15
- y_range = y_range.to_a.reverse if @map.renderorder_y
3
+ class OrthogonalLayer < TileLayer
4
+ def render(pos_x, pos_y, target = DXRuby::Window, z = 0, offset_x = 0, offset_y = 0, opacity = 1.0)
5
+ super
6
+
7
+ pos_x, pos_y = 0, 0 if @fixed
8
+ off_x = offset_x + @offset_x - pos_x
9
+ off_y = offset_y + @offset_y - pos_y
10
+ x1, y1 = xy_at(@tile_width - @tilesets.tile_right - off_x,
11
+ @tile_height - @tilesets.tile_bottom - off_y)
12
+ x2, y2 = xy_at(@render_target.width - @tilesets.tile_left - off_x - 1,
13
+ @render_target.height - @tilesets.tile_top - off_y - 1)
16
14
 
17
- y_range.each do |yy|
18
- x_range.each do |xx|
19
- image = tile_images[self[xx, yy]]
20
- target.draw_alpha(xx * tile_width - off_x - image.width / 2,
21
- yy * tile_height - off_y - image.height / 2,
22
- image, alpha)
15
+ (y1..(y2 + 1)).public_send(@renderorder_y ? :each : :reverse_each) do |y|
16
+ (x1..(x2 + 1)).public_send(@renderorder_x ? :each : :reverse_each) do |x|
17
+ @tilesets[self[x, y]].render(
18
+ x * @tile_width + off_x,
19
+ y * @tile_height + off_y,
20
+ @render_target)
23
21
  end
24
22
  end
23
+
24
+ target.draw_alpha(0, 0, @render_target, @opacity * 255, z + @z_index)
25
25
  end
26
+ alias_method :draw, :render
26
27
 
27
- def xy_at(x, y)
28
- return x / @map.tile_width, y / @map.tile_height
28
+ def xy_at(pos_x, pos_y)
29
+ return pos_x.to_i / @tile_width, pos_y.to_i / @tile_height
29
30
  end
30
31
 
31
- def at(x, y)
32
- return self[x / @map.tile_width, y / @map.tile_height]
32
+ def at(pos_x, pos_y)
33
+ self[pos_x.to_i / @tile_width, pos_y.to_i / @tile_height]
33
34
  end
34
35
 
35
- def change_at(x, y, value)
36
- self[x / @map.tile_width, y / @map.tile_height] = value
36
+ def change_at(pos_x, pos_y, value)
37
+ self[pos_x.to_i / @tile_width, pos_y.to_i / @tile_height] = value
37
38
  end
38
39
 
39
40
  def vertexs(x, y)
40
- w, h = @map.tile_width, @map.tile_height
41
+ w, h = @tile_width, @tile_height
41
42
  return [
42
43
  [ x * w , y * h ],
43
44
  [ x * w , y * h + h ],
@@ -47,13 +48,12 @@ module DXRuby
47
48
  end
48
49
 
49
50
  def self.pixel_width(map)
50
- return map.tile_width * map.width
51
+ map.tile_width * map.width
51
52
  end
52
53
 
53
54
  def self.pixel_height(map)
54
- return map.tile_height * map.height
55
+ map.tile_height * map.height
55
56
  end
56
-
57
57
  end
58
58
  end
59
59
  end
@@ -1,91 +1,95 @@
1
1
  module DXRuby
2
2
  module Tiled
3
- class StaggeredLayer < Layer
3
+ class StaggeredLayer < TileLayer
4
+ def initialize(data, map)
5
+ super
6
+ @stagger_axis_y = map.stagger_axis_y
7
+ @stagger_index_odd = map.stagger_index_odd
8
+ end
4
9
 
5
- def draw(x, y, target = DXRuby::Window)
6
- tile_width2, tile_height2 = @map.tile_width / 2, @map.tile_height / 2
7
- tile_images = @map.tilesets.tile_images
8
- left, top = xy_at(x - @offset_x, y - @offset_x)
9
- left -= 1
10
- top -= 1
11
- off_x = x - tile_width2 + @offset_x
12
- off_y = y - tile_height2 + @offset_y
13
- alpha = (@opacity * 255).floor
10
+ def render(pos_x, pos_y, target = DXRuby::Window, z = @z_index, offset_x = 0, offset_y = 0, opacity = 1.0)
11
+ super
12
+
13
+ pos_x, pos_y = 0, 0 if @fixed
14
+ tile_width2 = @tile_width / 2
15
+ tile_height2 = @tile_height / 2
16
+ off_x = offset_x + @offset_x - pos_x
17
+ off_y = offset_y + @offset_y - pos_y
18
+ x1, y1 = xy_at(@tile_width - @tilesets.tile_right - off_x,
19
+ @tile_height - @tilesets.tile_bottom - off_y)
20
+ x2, y2 = xy_at(@render_target.width - @tilesets.tile_left - off_x - 1,
21
+ @render_target.height - @tilesets.tile_top - off_y - 1)
22
+ x_range = ((x1 - 1)..(x2 + 1))
23
+ y_range = ((y1 - 1)..(y2 + 1))
14
24
 
15
- if @map.stagger_axis_y
16
- x_range = left..(left + target.width / tile_width2 / 2 + 1).floor
17
- y_range = top..(top + target.height / tile_height2 + 3).floor
18
-
19
- y_range.each do |y2|
20
- x0 = @map.stagger_index_odd ^ y2.even? ? tile_width2 : 0
21
- x_range.each do |x2|
22
- image = tile_images[self[x2, y2]]
23
- target.draw_alpha(x0 + x2 * tile_width2 * 2 - off_x - image.width / 2,
24
- y2 * tile_height2 - off_y - image.height / 2,
25
- image, alpha)
25
+ if @stagger_axis_y
26
+ y_range.public_send(@renderorder_y ? :each : :reverse_each) do |y|
27
+ x0 = @stagger_index_odd ^ y.even? ? tile_width2 : 0
28
+ x_range.public_send(@renderorder_x ? :each : :reverse_each) do |x|
29
+ @tilesets[self[x, y]].render(
30
+ x * @tile_width + off_x + x0,
31
+ y * tile_height2 + off_y,
32
+ @render_target)
26
33
  end
27
34
  end
28
35
  else
29
- x_range = left..(left + target.width / tile_width2 + 3).floor
30
- y_range = top..(top + target.height / tile_height2 / 2 + 1).floor
31
-
32
- y_range.each do |y2|
33
- x_range.each do |x2|
34
- y0 = @map.stagger_index_odd ^ x2.even? ? tile_height2 : 0
35
- image = tile_images[self[x2, y2]]
36
- target.draw_alpha( x2 * tile_width2 - off_x - image.width / 2,
37
- y0 + y2 * tile_height2 * 2 - off_y - image.height / 2,
38
- image, alpha)
36
+ y_range.public_send(@renderorder_y ? :each : :reverse_each) do |y|
37
+ x_range.public_send(@renderorder_x ? :each : :reverse_each) do |x|
38
+ next if @stagger_index_odd ^ x.even?
39
+ @tilesets[self[x, y]].render(
40
+ x * tile_width2 + off_x,
41
+ y * @tile_height + off_y,
42
+ @render_target)
43
+ end
44
+ x_range.public_send(@renderorder_x ? :each : :reverse_each) do |x|
45
+ next unless @stagger_index_odd ^ x.even?
46
+ @tilesets[self[x, y]].render(
47
+ x * tile_width2 + off_x,
48
+ y * @tile_height + off_y + tile_height2,
49
+ @render_target)
39
50
  end
40
51
  end
41
52
  end
53
+
54
+ target.draw_alpha(0, 0, @render_target, @opacity * 255, z + @z_index)
42
55
  end
56
+ alias_method :draw, :render
43
57
 
44
- def xy_at(x, y)
45
- if @map.stagger_axis_y
46
- y2 = y * 2 / @map.tile_height
47
- x -= @map.tile_width / 2 if @map.stagger_index_odd ^ y2.even?
48
- x2 = x / @map.tile_width
49
- x0 = x % @map.tile_width
50
- y0 = y % (@map.tile_height / 2)
51
- if (y0 < @map.tile_height / 2 - x0 * @map.tile_height / @map.tile_width)
52
- y2 -= 1
53
- x2 += @map.stagger_index_odd ^ y2.even? ? -1 : 0
54
- elsif (y0 < x0 * @map.tile_height / @map.tile_width - @map.tile_height / 2)
55
- y2 -= 1
56
- x2 += @map.stagger_index_odd ^ y2.even? ? 0 : 1
58
+ def xy_at(pos_x, pos_y)
59
+ if @stagger_axis_y
60
+ y = (pos_y * 2 / @tile_height).floor
61
+ pos_x -= @tile_width / 2 if @stagger_index_odd ^ y.even?
62
+ x = (pos_x / @tile_width).floor
63
+ dx = pos_x % @tile_width
64
+ dy = pos_y % (@tile_height / 2)
65
+ if (dy < @tile_height / 2 - dx * @tile_height / @tile_width)
66
+ y -= 1
67
+ x += @stagger_index_odd ^ y.even? ? -1 : 0
68
+ elsif (dy < dx * @tile_height / @tile_width - @tile_height / 2)
69
+ y -= 1
70
+ x += @stagger_index_odd ^ y.even? ? 0 : 1
57
71
  end
58
72
  else
59
- x2 = x * 2 / @map.tile_width
60
- y -= @map.tile_height / 2 if @map.stagger_index_odd ^ x2.even?
61
- y2 = y / @map.tile_height
62
- x0 = x % (@map.tile_width / 2)
63
- y0 = y % @map.tile_height
64
- if (y0 < @map.tile_height / 2 - x0 * @map.tile_height / @map.tile_width)
65
- x2 -= 1
66
- y2 += @map.stagger_index_odd ^ x2.even? ? -1 : 0
67
- elsif (y0 > x0 * @map.tile_height / @map.tile_width + @map.tile_height / 2)
68
- x2 -= 1
69
- y2 += @map.stagger_index_odd ^ x2.even? ? 0 : 1
73
+ x = (pos_x * 2 / @tile_width).floor
74
+ pos_y -= @tile_height / 2 if @stagger_index_odd ^ x.even?
75
+ y = (pos_y / @tile_height).floor
76
+ dx = pos_x % (@tile_width / 2)
77
+ dy = pos_y % @tile_height
78
+ if (dy < @tile_height / 2 - dx * @tile_height / @tile_width)
79
+ x -= 1
80
+ y += @stagger_index_odd ^ x.even? ? -1 : 0
81
+ elsif (dy > dx * @tile_height / @tile_width + @tile_height / 2)
82
+ x -= 1
83
+ y += @stagger_index_odd ^ x.even? ? 0 : 1
70
84
  end
71
85
  end
72
- return x2, y2
73
- end
74
-
75
- def at(x, y)
76
- tmp_x, tmp_y = xy_at(x, y)
77
- return self[tmp_x, tmp_y]
78
- end
79
-
80
- def change_at(x, y, value)
81
- tmp_x, tmp_y = xy_at(x, y)
82
- self[tmp_x, tmp_y] = value
86
+ return x, y
83
87
  end
84
88
 
85
89
  def vertexs(x, y)
86
- w, h = @map.tile_width / 2, @map.tile_height / 2
87
- if @map.stagger_axis_y
88
- x0 = @map.stagger_index_odd ^ y.even? ? w : 0
90
+ w, h = @tile_width / 2, @tile_height / 2
91
+ if @stagger_axis_y
92
+ x0 = @stagger_index_odd ^ y.even? ? w : 0
89
93
  return [
90
94
  [ x0 + x * w * 2 + w , y * h ],
91
95
  [ x0 + x * w * 2 , y * h + h ],
@@ -93,7 +97,7 @@ module DXRuby
93
97
  [ x0 + x * w * 2 + w * 2, y * h + h ]
94
98
  ]
95
99
  else
96
- y0 = @map.stagger_index_odd ^ x.even? ? h : 0
100
+ y0 = @stagger_index_odd ^ x.even? ? h : 0
97
101
  return [
98
102
  [ x * w + w , y0 + y * h * 2 ],
99
103
  [ x * w , y0 + y * h * 2 + h ],
@@ -105,17 +109,16 @@ module DXRuby
105
109
 
106
110
 
107
111
  def self.pixel_width(map)
108
- return map.stagger_axis_y ?
109
- map.tile_width * map.width + map.tile_width / 2 :
110
- map.tile_width * (map.width + 1) / 2
112
+ map.stagger_axis_y ?
113
+ map.tile_width * map.width + map.tile_width / 2 :
114
+ map.tile_width * (map.width + 1) / 2
111
115
  end
112
116
 
113
117
  def self.pixel_height(map)
114
- return map.stagger_axis_y ?
115
- map.tile_height * (map.height + 1) / 2 :
116
- map.tile_height * map.height + map.tile_height / 2
118
+ map.stagger_axis_y ?
119
+ map.tile_height * (map.height + 1) / 2 :
120
+ map.tile_height * map.height + map.tile_height / 2
117
121
  end
118
-
119
122
  end
120
123
  end
121
124
  end
@@ -2,25 +2,23 @@ module DXRuby
2
2
  module Tiled
3
3
  class Map
4
4
  attr_reader :data_dir, :tilesets, :layers,
5
- :version, :orientation, :renderorder_x, :renderorder_y,
5
+ :version, :tiledversion, :orientation, :renderorder_x, :renderorder_y,
6
6
  :width, :height, :tile_width, :tile_height,
7
7
  :hex_side_length, :stagger_axis_y, :stagger_index_odd,
8
- :next_object_id, :properties, :x_loop, :y_loop
8
+ :properties, :loop
9
9
  attr_accessor :background_color
10
10
 
11
11
  def initialize(data, data_dir)
12
12
  @data_dir = data_dir
13
13
 
14
14
  @version = data[:version]
15
- @orientation = case data[:orientation]
16
- when "isometric"
17
- IsometricLayer
18
- when "staggered"
19
- StaggeredLayer
20
- when "hexagonal"
21
- HexagonalLayer
22
- else
23
- OrthogonalLayer
15
+ @tiledversion = data[:tiledversion]
16
+ @orientation =
17
+ case data[:orientation]
18
+ when "isometric" then IsometricLayer
19
+ when "staggered" then StaggeredLayer
20
+ when "hexagonal" then HexagonalLayer
21
+ else OrthogonalLayer
24
22
  end
25
23
  @width = data[:width] || 100
26
24
  @height = data[:height] || 100
@@ -31,61 +29,79 @@ module DXRuby
31
29
  @stagger_index_odd = data[:staggerindex] != "even"
32
30
  @next_object_id = data[:nextobjectid] || 1
33
31
  @properties = data[:properties] || {}
34
- @x_loop = !!@properties[:x_loop]
35
- @y_loop = !!@properties[:y_loop]
36
- @renderorder_x = false
37
- @renderorder_y = false
38
- case data[:renderorder]
39
- when "left-down"
40
- @renderorder_x = true
41
- when "right-up"
42
- @renderorder_y = true
43
- when "left-up"
44
- @renderorder_x = true
45
- @renderorder_y = true
46
- end
32
+ @loop = !!@properties[:loop]
33
+ @renderorder_x = !data[:renderorder].to_s.match("left")
34
+ @renderorder_y = !data[:renderorder].to_s.match("top")
47
35
  @background_color = nil
48
36
  if data[:backgroundcolor]
49
- @background_color = data[:backgroundcolor].sub("#", "").scan(/../).map do |color|
50
- color.to_i(16)
51
- end
52
- end
53
-
54
- @layers = data[:layers].map do |layer|
55
- case layer[:type]
56
- when "tilelayer"
57
- @orientation.new(layer, self)
58
- when "objectgroup"
59
- ObjectGroup.new(layer, self)
60
- when "imagelayer"
61
- ImageLayer.new(layer, self)
62
- end
63
- end
64
- def @layers.name(name)
65
- return self.find{|layer| layer.name == name}
37
+ @background_color = data[:backgroundcolor].sub("#", "").scan(/../).map{ |c| c.to_i(16) }
66
38
  end
67
39
 
68
40
  @tilesets = Tilesets.new(data[:tilesets], self)
41
+ @layers = GroupLayer.new({ layers: data[:layers] }, self)
42
+ end
43
+
44
+ def render(x, y, target = DXRuby::Window, z = 0)
45
+ raise DXRuby::DXRubyError, "disposed object" if self.disposed?
46
+ @tilesets.animate
47
+ target.draw_box_fill(0, 0, target.width, target.height, @background_color, z) if @background_color
48
+ @layers.render(x, y, target, z)
49
+ end
50
+ alias_method :draw, :render
51
+
52
+ def pixel_width
53
+ @orientation.pixel_width(self)
69
54
  end
70
55
 
71
- def draw(x, y, target = DXRuby::Window)
72
- target.draw_box_fill(0, 0, target.width, target.height, @background_color) if @background_color
73
- tilesets.animation()
74
- @layers.each{|layer| layer.draw(x, y, target) if layer.visible }
56
+ def pixel_height
57
+ @orientation.pixel_height(self)
75
58
  end
76
59
 
77
- def load_image(filename)
78
- return DXRuby::Image.load(File.join(@data_dir, filename))
60
+ def next_object_id
61
+ @next_object_id += 1
62
+ return @next_object_id - 1
79
63
  end
80
64
 
81
- def pixel_width()
82
- return @orientation.pixel_width(self)
65
+ def dispose
66
+ @tilesets.dispose
83
67
  end
84
68
 
85
- def pixel_height()
86
- return @orientation.pixel_height(self)
69
+ def delayed_dispose
70
+ @tilesets.delayed_dispose
87
71
  end
88
72
 
73
+ def disposed?
74
+ @tilesets.disposed?
75
+ end
76
+
77
+ def load_image(filename, transparentcolor = nil, data_dir = @data_dir)
78
+ image = DXRuby::Image.load(File.join(data_dir, filename))
79
+ if transparentcolor
80
+ color = transparentcolor.sub("#", "").scan(/../).map { |c| c.to_i(16) }
81
+ image.set_color_key(color)
82
+ end
83
+ image
84
+ end
85
+
86
+ def load_tileset(filename, encoding = Encoding::UTF_8, data_dir = @data_dir)
87
+ filepath = File.join(data_dir, filename)
88
+ case File.extname(filename)
89
+ when ".tsx", ".xml"
90
+ DXRuby::Tiled::TMXLoader.tsx_to_hash(DXRuby::Tiled::TMXLoader.read_xmlfile(filepath, encoding))
91
+ else
92
+ DXRuby::Tiled.read_jsonfile(filepath, encoding)
93
+ end
94
+ end
95
+
96
+ def load_template(filename, encoding = Encoding::UTF_8, data_dir = @data_dir)
97
+ filepath = File.join(data_dir, filename)
98
+ case File.extname(filename)
99
+ when ".tx", ".xml"
100
+ DXRuby::Tiled::TMXLoader.tx_to_hash(DXRuby::Tiled::TMXLoader.read_xmlfile(filepath, encoding))
101
+ else
102
+ DXRuby::Tiled.read_jsonfile(filepath, encoding)
103
+ end
104
+ end
89
105
  end
90
106
  end
91
107
  end