tmx 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }