tmx 0.0.1 → 0.1.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.
@@ -0,0 +1,30 @@
1
+ module Tmx
2
+
3
+ #
4
+ # This module defines the parsers for the formats of various
5
+ # TMX output formats.
6
+ #
7
+ module Parsers
8
+ extend self
9
+
10
+ def format(format)
11
+ parsers[format]
12
+ end
13
+
14
+ def parsers
15
+ @parsers ||= {}
16
+ end
17
+
18
+ def register(format,parser)
19
+ parsers[format] = parser
20
+ end
21
+
22
+ def default=(parser)
23
+ parsers.default = parser
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'tmx/parsers/unknown'
29
+ require 'tmx/parsers/json'
30
+ require 'tmx/parsers/tmx'
@@ -0,0 +1,145 @@
1
+ require 'nokogiri'
2
+ require 'zlib'
3
+ require 'base64'
4
+
5
+ module Tmx
6
+ module Parser
7
+
8
+ #
9
+ # Parses the TMX formatted output from Tiled.
10
+ #
11
+ class Tmx
12
+ attr_reader :options
13
+
14
+ def initialize(options)
15
+ @options = options
16
+ end
17
+
18
+ def parse(contents)
19
+ results = Nokogiri::XML(contents)
20
+ convert_to_hash(results)
21
+ end
22
+
23
+ private
24
+
25
+ def convert_to_hash(xml)
26
+ {
27
+ "version" => map_attr(xml,"version").to_i,
28
+ "orientation" => map_attr(xml,"orientation"),
29
+ "width" => map_attr(xml,"width").to_i,
30
+ "height" => map_attr(xml,"height").to_i,
31
+ "tilewidth" => map_attr(xml,"tilewidth").to_i,
32
+ "tileheight" => map_attr(xml,"tileheight").to_i,
33
+ "properties" => properties(xml.xpath("/map")),
34
+ "layers" => map_layers(xml),
35
+ "tilesets" => map_tilesets(xml)
36
+ }
37
+ end
38
+
39
+ def map_attr(xml,name)
40
+ xml.xpath("/map/@#{name}").text
41
+ end
42
+
43
+ def properties(xml)
44
+ properties = {}
45
+ xml.xpath("properties/property").each do |property|
46
+ properties[property.xpath("@name").text] = property.xpath("@value").text
47
+ end
48
+ properties
49
+ end
50
+
51
+ def layer_attr(layer,name,options)
52
+ layer.xpath("@#{name}").text == "" ? options[:default] : layer.xpath("@#{name}").text
53
+ end
54
+
55
+ def map_layers(xml)
56
+ layers = []
57
+
58
+ xml.xpath("/map/layer").each do |layer|
59
+ layer_hash = {}
60
+
61
+ layer_hash["name"] = layer.xpath("@name").text
62
+ layer_hash["width"] = layer.xpath("@width").text.to_i
63
+ layer_hash["height"] = layer.xpath("@height").text.to_i
64
+
65
+ layer_hash["type"] = layer_attr(layer,"type",default: "tilelayer")
66
+ layer_hash["opacity"] = layer_attr(layer,"opacity",default: 1.0).to_f
67
+ layer_hash["visible"] = (layer.xpath("@visible").text =~ /^false$/ ? false : true)
68
+
69
+ layer_hash["x"] = layer.xpath("@x").text.to_i
70
+ layer_hash["y"] = layer.xpath("@y").text.to_i
71
+
72
+ layer_hash["data"] = data_from_layer(layer)
73
+
74
+ layers.push layer_hash
75
+ end
76
+ layers
77
+ end
78
+
79
+ def data_from_layer(layer)
80
+ layer_data = layer.xpath("data").text.strip
81
+
82
+ if layer.xpath("data/@encoding").text == "base64"
83
+ layer_data = Base64.decode64(layer_data)
84
+ end
85
+
86
+ if layer.xpath("data/@compression").text == "zlib"
87
+ data = unpack_data zlib_decompress(layer_data)
88
+ elsif layer.xpath("data/@compression").text == "gzip"
89
+ data = unpack_data gzip_decompress(layer_data)
90
+ elsif layer.xpath("data/@encoding").text == "base64"
91
+ data = unpack_data(layer_data)
92
+ elsif layer.xpath("data/@encoding").text == "csv"
93
+ data = layer_data.split(",").map(&:to_i)
94
+ else
95
+ data = layer.xpath("data/tile").map { |tile| tile.xpath("@gid").text.to_i }
96
+ end
97
+ end
98
+
99
+ def zlib_decompress(data)
100
+ Zlib::Inflate.inflate(data)
101
+ end
102
+
103
+ def gzip_decompress(data)
104
+ Zlib::GzipReader.new(StringIO.new(data)).read
105
+ end
106
+
107
+ #
108
+ # Convert the XML data format into a valid format in Ruby.
109
+ #
110
+ # @see http://sourceforge.net/apps/mediawiki/tiled/index.php?title=Examining_the_map_format
111
+ # @see http://www.ruby-doc.org/core-2.0/String.html#method-i-unpack
112
+ #
113
+ # V | Integer | 32-bit unsigned, VAX (little-endian) byte order
114
+ #
115
+ def unpack_data(data)
116
+ data.unpack("V" * (data.length / 4))
117
+ end
118
+
119
+ def map_tilesets(xml)
120
+ xml.xpath("map/tileset").map do |tileset|
121
+ image = tileset.xpath("image")
122
+
123
+ properties = {
124
+ "firstgid" => tileset.xpath("@firstgid").text.to_i,
125
+ "name" => tileset.xpath("@name").text,
126
+ "tilewidth" => tileset.xpath("@tilewidth").text.to_i,
127
+ "tileheight" => tileset.xpath("@tileheight").text.to_i,
128
+ "spacing" => tileset.xpath("@spacing").text.to_i,
129
+ "margin" => tileset.xpath("@margin").text.to_i,
130
+ "image" => image.xpath("@source").text,
131
+ "imageheight" => image.xpath("@height").text.to_i,
132
+ "imagewidth" => image.xpath("@width").text.to_i,
133
+ "properties" => properties(xml)
134
+ }
135
+ end
136
+
137
+
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+ Parsers.register :tmx, Parser::Tmx
145
+ end
@@ -0,0 +1,24 @@
1
+ module Tmx
2
+ module Parser
3
+
4
+ #
5
+ # The Unknown Parser is used to generate an error when the format
6
+ # is not supported or unknown.
7
+ #
8
+ class Unknown
9
+ attr_reader :options
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ end
14
+
15
+ def parse(contents)
16
+ raise "Unknown Parser Type: Could not find an available parser for format #{options[:format]}"
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ Parsers.default = Parser::Unknown
23
+
24
+ end
data/lib/tmx/tile_set.rb CHANGED
@@ -1,35 +1,5 @@
1
1
  module Tmx
2
- # class TileSet
3
- # def initialize map
4
- # @map = WeakRef.new map
5
- # @tiles = [ nil ]
6
- # end
7
- #
8
- # def load_tiles file_name_or_images, properties = {}
9
- # properties = properties.dup
10
- # first_id = properties.delete :firstgid
11
- # tile_width = properties.delete :tilewidth
12
- # tile_height = properties.delete :tileheight
13
- #
14
- # @tiles[first_id..-1] = case file_name_or_images
15
- # when String then Gosu::Image.load_tiles(@map.window, file_name_or_images, tile_width, tile_height, true).freeze
16
- # when Array then file_name_or_images.dup
17
- # else raise ArgumentError, "must supply a file name or an array of images"
18
- # end
19
- #
20
- # end # load_tiles
21
- #
22
- # def map; @map.__getobj__ end
23
- #
24
- # def [] tile_id
25
- # raise IndexError unless (0...@tiles.length).include? tile_id
26
- # @tiles[tile_id]
27
- # end
28
- #
29
- # def []= tile_id, image
30
- # raise IndexError unless (1...@tiles.length).include? tile_id
31
- # @tiles[tile_id] = image
32
- # end
33
- #
34
- # end
35
- end
2
+ class TileSet < OpenStruct
3
+
4
+ end
5
+ end
data/lib/tmx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tmx
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tmx, "JSON Format" do
4
+
5
+ let(:fixture_file) { File.join File.dirname(__FILE__), "..", "fixtures", "map.json" }
6
+
7
+ let(:map) do
8
+ described_class.load(fixture_file)
9
+ end
10
+
11
+ let(:subject) { map }
12
+
13
+ its(:version) { should eq 1 }
14
+
15
+ its(:height) { should eq 12 }
16
+ its(:width) { should eq 16 }
17
+
18
+ its(:tileheight) { should eq 50 }
19
+ its(:tilewidth) { should eq 50 }
20
+
21
+ its(:orientation) { should eq "orthogonal" }
22
+
23
+ describe '#properties' do
24
+ it "has the correct number of properties" do
25
+ expect(subject.properties).to have(1).item
26
+ end
27
+
28
+ it "property values are correct" do
29
+ expect(subject.properties["hero.position"]).to eq "400,525,1"
30
+ end
31
+ end
32
+
33
+ describe '#layers' do
34
+ it "has the correct number of layers" do
35
+ expect(subject.layers).to have(1).item
36
+ end
37
+
38
+ context "when evaluating the first layer" do
39
+
40
+ let(:subject) { map.layers.first }
41
+
42
+ its(:name) { should eq "Tile Layer 1" }
43
+ its(:opacity) { should eq 1 }
44
+ its(:type) { should eq "tilelayer" }
45
+ its(:visible) { should be_true }
46
+ its(:height) { should eq 12 }
47
+ its(:width) { should eq 16 }
48
+ its(:x) { should eq 0 }
49
+ its(:y) { should eq 0 }
50
+
51
+ its(:data) { should eq [ 3, 3, 3, 3, 3, 3, 2, 5, 5, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 18, 9, 9, 17, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 6, 7, 3, 3, 3, 3, 3, 18, 9, 17, 2, 4, 4, 10, 13, 4, 10, 11, 3, 3, 3, 3, 18, 14, 6, 7, 2, 4, 4, 7, 16, 8, 11, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 16, 8, 8, 8, 13, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 12, 12, 12, 12, 16, 8, 8, 11, 3, 3, 3, 3, 3, 3] }
52
+ end
53
+ end
54
+
55
+ describe '#tilesets' do
56
+ it "has the correct number of tilesets" do
57
+ expect(subject.tilesets).to have(1).item
58
+ end
59
+
60
+ context "when evaluating the first tileset" do
61
+ let(:subject) { map.tilesets.first }
62
+
63
+ its(:firstgid) { should eq 1 }
64
+ its(:image) { should eq "tiles.png" }
65
+ its(:imageheight) { should eq 250 }
66
+ its(:imagewidth) { should eq 200 }
67
+ its(:margin) { should eq 0 }
68
+ its(:name) { should eq "tiles" }
69
+ its(:spacing) { should eq 0 }
70
+ its(:tileheight) { should eq 50 }
71
+ its(:tilewidth) { should eq 50 }
72
+ its(:properties) { should eq({}) }
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tmx, "TMX Formats" do
4
+
5
+ [ :zlib, :gzip, :uncompressed, :csv, :xml ].each do |format|
6
+
7
+ describe "#{format}" do
8
+
9
+ let(:fixture_file) { File.join File.dirname(__FILE__), "..", "fixtures", "map_#{format}.tmx" }
10
+
11
+ let(:map) do
12
+ described_class.load(fixture_file)
13
+ end
14
+
15
+ let(:subject) { map }
16
+
17
+ its(:version) { should eq 1 }
18
+
19
+ its(:height) { should eq 12 }
20
+ its(:width) { should eq 16 }
21
+
22
+ its(:tileheight) { should eq 50 }
23
+ its(:tilewidth) { should eq 50 }
24
+
25
+ its(:orientation) { should eq "orthogonal" }
26
+
27
+ describe '#properties' do
28
+ it "has the correct number of properties" do
29
+ expect(subject.properties).to have(1).item
30
+ end
31
+
32
+ it "property values are correct" do
33
+ expect(subject.properties["hero.position"]).to eq "400,525,1"
34
+ end
35
+ end
36
+
37
+ describe '#layers' do
38
+ it "has the correct number of layers" do
39
+ expect(subject.layers).to have(1).item
40
+ end
41
+
42
+ context "when evaluating the first layer" do
43
+
44
+ let(:subject) { map.layers.first }
45
+
46
+ its(:name) { should eq "Tile Layer 1" }
47
+ its(:opacity) { should eq 1 }
48
+ its(:type) { should eq "tilelayer" }
49
+ its(:visible) { should be_true }
50
+ its(:height) { should eq 12 }
51
+ its(:width) { should eq 16 }
52
+ its(:x) { should eq 0 }
53
+ its(:y) { should eq 0 }
54
+
55
+ its(:data) { should eq [ 3, 3, 3, 3, 3, 3, 2, 5, 5, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 18, 9, 9, 17, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 6, 7, 3, 3, 3, 3, 3, 18, 9, 17, 2, 4, 4, 10, 13, 4, 10, 11, 3, 3, 3, 3, 18, 14, 6, 7, 2, 4, 4, 7, 16, 8, 11, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 16, 8, 8, 8, 13, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 12, 12, 12, 12, 16, 8, 8, 11, 3, 3, 3, 3, 3, 3] }
56
+ end
57
+ end
58
+
59
+ describe '#tilesets' do
60
+ it "has the correct number of tilesets" do
61
+ expect(subject.tilesets).to have(1).item
62
+ end
63
+
64
+ context "when evaluating the first tileset" do
65
+ let(:subject) { map.tilesets.first }
66
+
67
+ its(:firstgid) { should eq 1 }
68
+ its(:image) { should eq "tiles.png" }
69
+ its(:imageheight) { should eq 250 }
70
+ its(:imagewidth) { should eq 200 }
71
+ its(:margin) { should eq 0 }
72
+ its(:name) { should eq "tiles" }
73
+ its(:spacing) { should eq 0 }
74
+ its(:tileheight) { should eq 50 }
75
+ its(:tilewidth) { should eq 50 }
76
+ its(:properties) { should eq({}) }
77
+ end
78
+ end
79
+
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ { "height":12,
2
+ "layers":[
3
+ {
4
+ "data":[3, 3, 3, 3, 3, 3, 2, 5, 5, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 7, 18, 9, 9, 17, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 6, 7, 3, 3, 3, 3, 3, 18, 9, 17, 2, 4, 4, 10, 13, 4, 10, 11, 3, 3, 3, 3, 18, 14, 6, 7, 2, 4, 4, 7, 16, 8, 11, 3, 3, 3, 3, 3, 2, 4, 4, 15, 14, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 16, 8, 8, 8, 13, 4, 4, 7, 3, 3, 3, 3, 3, 3, 3, 3, 12, 12, 12, 12, 16, 8, 8, 11, 3, 3, 3, 3, 3, 3],
5
+ "height":12,
6
+ "name":"Tile Layer 1",
7
+ "opacity":1,
8
+ "type":"tilelayer",
9
+ "visible":true,
10
+ "width":16,
11
+ "x":0,
12
+ "y":0
13
+ }],
14
+ "orientation":"orthogonal",
15
+ "properties":
16
+ {
17
+ "hero.position":"400,525,1"
18
+ },
19
+ "tileheight":50,
20
+ "tilesets":[
21
+ {
22
+ "firstgid":1,
23
+ "image":"tiles.png",
24
+ "imageheight":250,
25
+ "imagewidth":200,
26
+ "margin":0,
27
+ "name":"tiles",
28
+ "properties":
29
+ {
30
+
31
+ },
32
+ "spacing":0,
33
+ "tileheight":50,
34
+ "tilewidth":50
35
+ }],
36
+ "tilewidth":50,
37
+ "version":1,
38
+ "width":16
39
+ }