tmx 0.0.1
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.
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/README.markdown +92 -0
- data/Rakefile +11 -0
- data/data/door.png +0 -0
- data/data/dude.png +0 -0
- data/data/test-tiles.png +0 -0
- data/data/test.tmx +62 -0
- data/examples/example-chingu.rb +114 -0
- data/lib/tmx.rb +16 -0
- data/lib/tmx/coder.rb +39 -0
- data/lib/tmx/coder/base64.rb +13 -0
- data/lib/tmx/coder/gzip.rb +21 -0
- data/lib/tmx/layer.rb +74 -0
- data/lib/tmx/map.rb +147 -0
- data/lib/tmx/map/tile_cache.rb +83 -0
- data/lib/tmx/map/xml_loader.rb +41 -0
- data/lib/tmx/nokogiri_additions.rb +56 -0
- data/lib/tmx/object_group.rb +30 -0
- data/lib/tmx/tile_set.rb +35 -0
- data/lib/tmx/version.rb +3 -0
- data/spec/coder_spec.rb +29 -0
- data/spec/map_spec.rb +50 -0
- data/spec/nokogiri_spec.rb +5 -0
- data/spec/object_group_spec.rb +41 -0
- data/spec/spec_helper.rb +52 -0
- data/tmx.gemspec +27 -0
- metadata +123 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# README
|
2
|
+
## WHAT IS THIS
|
3
|
+
|
4
|
+
Eventually, a usable TMX map loader that works with Gosu and doesn't care
|
5
|
+
whether you're using Chingu or some home-grown game engine of your own
|
6
|
+
devising.
|
7
|
+
|
8
|
+
### WHY ON EARTH
|
9
|
+
|
10
|
+
I like Chingu and a TMX loader already exists for it, but it's just not the
|
11
|
+
right tool for what I want to do. Hopefully others will find this useful as
|
12
|
+
well. :)
|
13
|
+
|
14
|
+
### WHAT IS MISSING
|
15
|
+
|
16
|
+
Here's what's not: so far, map data is loaded and layers, object groups and
|
17
|
+
tile sets are created.
|
18
|
+
|
19
|
+
Validating the XML document to its DTD would be nice too, but I'll be buggered
|
20
|
+
if I can get Nokogiri to actually load the DTD. There is inadequate or no
|
21
|
+
documentation on this topic. Probably we'll just have mysterious failures on
|
22
|
+
unsupported or erroneous TMX files, which is not ideal.
|
23
|
+
|
24
|
+
Possible consideration for the future: move the dependency on Gosu into a
|
25
|
+
separate, mixable, matchable module. Maybe add explicit Chingu support too.
|
26
|
+
Handle tile set creation and drawing ops the same way we do object creation:
|
27
|
+
define a callback hook and let the user take care of it. Awesome.
|
28
|
+
|
29
|
+
Help is welcome, obviously.
|
30
|
+
|
31
|
+
## INSTALL
|
32
|
+
|
33
|
+
Don't do it yet. The API is so unstable it does not have a half life but a
|
34
|
+
quarter life.
|
35
|
+
|
36
|
+
### PREREQUISITES
|
37
|
+
|
38
|
+
* ruby >= 1.9.1 (probably)
|
39
|
+
* nokogiri
|
40
|
+
|
41
|
+
## LICENSE
|
42
|
+
|
43
|
+
This software is available under the terms of the MIT license for no better
|
44
|
+
reason than that this is the license of Gosu itself. This means that you are
|
45
|
+
technically allowed to use my hard work as part of your proprietary,
|
46
|
+
commercial product with no obligation to give anything back but credit where
|
47
|
+
it's due. Use your discretion on that one.
|
48
|
+
|
49
|
+
The full license is reproduced here for posterity:
|
50
|
+
|
51
|
+
> Copyright © 2009–2010 Eris
|
52
|
+
>
|
53
|
+
> Permission is hereby granted, free of charge, to any person
|
54
|
+
> obtaining a copy of this software and associated documentation
|
55
|
+
> files (the "Software"), to deal in the Software without
|
56
|
+
> restriction, including without limitation the rights to use,
|
57
|
+
> copy, modify, merge, publish, distribute, sublicense, and/or sell
|
58
|
+
> copies of the Software, and to permit persons to whom the
|
59
|
+
> Software is furnished to do so, subject to the following
|
60
|
+
> conditions:
|
61
|
+
>
|
62
|
+
> The above copyright notice and this permission notice shall be
|
63
|
+
> included in all copies or substantial portions of the Software.
|
64
|
+
>
|
65
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
66
|
+
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
67
|
+
> OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
68
|
+
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
69
|
+
> HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
70
|
+
> WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
71
|
+
> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
72
|
+
> OTHER DEALINGS IN THE SOFTWARE.
|
73
|
+
|
74
|
+
## AUTHORS
|
75
|
+
|
76
|
+
* Eris <eris.discord@gmail.com>
|
77
|
+
* your name here!
|
78
|
+
|
79
|
+
## SEE ALSO
|
80
|
+
|
81
|
+
* [Gosu][], a 2D game development library for Ruby and C++
|
82
|
+
* [Chingu][], a higher level game library built on top of Gosu
|
83
|
+
* [Chipmunk][], a 2D rigid body physics engine in C
|
84
|
+
* [chipmunk-ffi][], more up-to-date Ruby bindings for Chipmunk
|
85
|
+
* [Tiled][], a flexible tile map editor and the origin of the TMX format (I
|
86
|
+
think).
|
87
|
+
|
88
|
+
[chingu]: http://github.com/ippa/chingu
|
89
|
+
[chipmunk]: http://code.google.com/p/chipmunk-physics
|
90
|
+
[chipmunk-ffi]: http://github.com/shawn42/chipmunk-ffi
|
91
|
+
[gosu]: http://libgosu.org
|
92
|
+
[tiled]: http://mapeditor.org/
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
RSpec::Core::RakeTask.new do |t|
|
9
|
+
# t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
10
|
+
# Put spec opts in a file named .rspec in root
|
11
|
+
end
|
data/data/door.png
ADDED
Binary file
|
data/data/dude.png
ADDED
Binary file
|
data/data/test-tiles.png
ADDED
Binary file
|
data/data/test.tmx
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<map version="1.0" orientation="orthogonal" width="16" height="16" tilewidth="16" tileheight="16">
|
3
|
+
<properties>
|
4
|
+
<property name="bg1" value="#402010"/>
|
5
|
+
<property name="bg2" value="#100000"/>
|
6
|
+
<property name="fg1" value="#00ff0000"/>
|
7
|
+
<property name="fg2" value="#10ff0000"/>
|
8
|
+
<property name="display_name" value="Test Map"/>
|
9
|
+
</properties>
|
10
|
+
<tileset firstgid="1" name="debug" tilewidth="16" tileheight="16">
|
11
|
+
<image source="test-tiles.png"/>
|
12
|
+
</tileset>
|
13
|
+
<layer name="background" width="16" height="16" opacity="0.75">
|
14
|
+
<data encoding="base64" compression="gzip">
|
15
|
+
H4sIAAAAAAAAA2NgGAXogInK5rGh0aNgFAwWAADxvIrYAAQAAA==
|
16
|
+
</data>
|
17
|
+
</layer>
|
18
|
+
<layer name="obstacle" width="16" height="16">
|
19
|
+
<data encoding="base64" compression="gzip">
|
20
|
+
H4sIAAAAAAAAA82T4QqAIAyEZ1Jakb3/27YjB2MMMfvjwSHMfTp1BiIqg47sTOPKk/EXvWeCAnupLmqMDR7aa95RnVQumfye+vV6GM+PfEtgV/b9g5/p/UR4u+jEe3ncSTIx9Ia9J4/H3pvDezUmwwvbw0uN0h+658Stv4t59NIDVh+CwAAEAAA=
|
21
|
+
</data>
|
22
|
+
</layer>
|
23
|
+
<objectgroup name="doors" width="16" height="16">
|
24
|
+
<object name="door1" type="Door" x="128" y="208" width="16" height="32">
|
25
|
+
<properties>
|
26
|
+
<property name="image" value="door.png"/>
|
27
|
+
<property name="target" value="door2"/>
|
28
|
+
</properties>
|
29
|
+
</object>
|
30
|
+
<object name="door2" type="Door" x="64" y="32" width="16" height="32">
|
31
|
+
<properties>
|
32
|
+
<property name="image" value="door.png"/>
|
33
|
+
<property name="target" value="door1"/>
|
34
|
+
</properties>
|
35
|
+
</object>
|
36
|
+
</objectgroup>
|
37
|
+
<objectgroup name="objects" width="16" height="16">
|
38
|
+
<properties>
|
39
|
+
<property name="what_is_this" value="it's boxes ok"/>
|
40
|
+
</properties>
|
41
|
+
<object name="cardboard_box" type="Box" x="160" y="160" width="32" height="32">
|
42
|
+
<properties>
|
43
|
+
<property name="image" value="tid:16"/>
|
44
|
+
</properties>
|
45
|
+
</object>
|
46
|
+
<object name="stone_block" type="Box" x="192" y="32" width="32" height="32">
|
47
|
+
<properties>
|
48
|
+
<property name="image" value="tid:16"/>
|
49
|
+
</properties>
|
50
|
+
</object>
|
51
|
+
<object name="dude" type="Dude" x="80" y="144" width="16" height="32">
|
52
|
+
<properties>
|
53
|
+
<property name="image" value="dude.png"/>
|
54
|
+
</properties>
|
55
|
+
</object>
|
56
|
+
</objectgroup>
|
57
|
+
<layer name="foreground" width="16" height="16" opacity="0.75">
|
58
|
+
<data encoding="base64" compression="gzip">
|
59
|
+
H4sIAAAAAAAAA2NgGAWjYPABRiBmIoAJATYCeCD1EwMo1U8MAADF4i9MAAQAAA==
|
60
|
+
</data>
|
61
|
+
</layer>
|
62
|
+
</map>
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'chingu'
|
2
|
+
require 'tmx'
|
3
|
+
require 'opengl' # Chingu needs it for retrofy
|
4
|
+
|
5
|
+
class Float
|
6
|
+
INFINITY = 1.0 / 0.0 unless const_defined? :INFINITY
|
7
|
+
end
|
8
|
+
|
9
|
+
class Box < Chingu::GameObject
|
10
|
+
# "It's just a box."
|
11
|
+
|
12
|
+
# (Chingu takes care of everything we need here (it's magic))
|
13
|
+
end
|
14
|
+
|
15
|
+
class Door < Chingu::GameObject
|
16
|
+
# this ought to take you somewhere else
|
17
|
+
end
|
18
|
+
|
19
|
+
class Dude < Chingu::GameObject
|
20
|
+
# maybe your player logic goes here
|
21
|
+
end
|
22
|
+
|
23
|
+
class MapState < Chingu::GameState
|
24
|
+
has_trait :viewport
|
25
|
+
|
26
|
+
def initialize map_name, *rest
|
27
|
+
super *rest
|
28
|
+
|
29
|
+
# load our map
|
30
|
+
@map = TMX::Map.new $window, map_name,
|
31
|
+
:on_object => method(:create_chingu_object)
|
32
|
+
@banner = Gosu::Image.from_text $window,
|
33
|
+
@map.properties[:display_name],
|
34
|
+
'Helvetica', 24
|
35
|
+
|
36
|
+
$window.caption = 'tmx demo - %s' % @map.properties[:display_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_chingu_object name, group, properties
|
40
|
+
map = group.map
|
41
|
+
obj_class = Kernel.const_get properties[:type] rescue nil
|
42
|
+
|
43
|
+
# assert that the object is a valid type
|
44
|
+
raise TypeError, "#{properties[:type]} is not a game object" \
|
45
|
+
unless obj_class.is_a? Class \
|
46
|
+
and obj_class.ancestors.include? Chingu::BasicGameObject
|
47
|
+
|
48
|
+
# load image
|
49
|
+
image_name = properties[:image]
|
50
|
+
image = case image_name
|
51
|
+
when /^tid:(\d+)$/
|
52
|
+
# a tile from the map's tile set
|
53
|
+
map.tile_set[Integer($1)]
|
54
|
+
when nil
|
55
|
+
# default image
|
56
|
+
Gosu::Image['default']
|
57
|
+
else
|
58
|
+
# image as named
|
59
|
+
Gosu::Image[File.join 'data', image_name]
|
60
|
+
end
|
61
|
+
|
62
|
+
# convert TMX properties to what Chingu is expecting
|
63
|
+
properties.merge! \
|
64
|
+
:image => image.retrofy,
|
65
|
+
:x => properties[:x] + properties[:width] / 2,
|
66
|
+
:y => properties[:y] + properties[:height] / 2,
|
67
|
+
:factor_x => properties[:width].to_f / image.width,
|
68
|
+
:factor_y => properties[:height].to_f / image.height,
|
69
|
+
:zorder => 1
|
70
|
+
|
71
|
+
# create and return the game object
|
72
|
+
obj_class.create properties
|
73
|
+
end
|
74
|
+
|
75
|
+
def update
|
76
|
+
super
|
77
|
+
# move the map opposite the mouse pointer, with lag
|
78
|
+
viewport.x = 0.9 * viewport.x + 0.1 * ($window.mouse_x - $window.width + @map.width / 2)
|
79
|
+
viewport.y = 0.9 * viewport.y + 0.1 * ($window.mouse_y - $window.height + @map.height / 2)
|
80
|
+
end
|
81
|
+
|
82
|
+
def draw
|
83
|
+
super
|
84
|
+
|
85
|
+
fill_gradient \
|
86
|
+
:from => @map.properties[:bg1],
|
87
|
+
:to => @map.properties[:bg2],
|
88
|
+
:zorder => -Float::INFINITY
|
89
|
+
|
90
|
+
# map is not a game object
|
91
|
+
@map.draw -viewport.x, -viewport.y
|
92
|
+
|
93
|
+
fill_gradient \
|
94
|
+
:from => @map.properties[:fg1],
|
95
|
+
:to => @map.properties[:fg2],
|
96
|
+
:zorder => Float::INFINITY
|
97
|
+
|
98
|
+
@banner.draw 8, 8, Float::INFINITY
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class MapWindow < Chingu::Window
|
103
|
+
def initialize
|
104
|
+
super 400, 200, false
|
105
|
+
|
106
|
+
# global inputs for our demo
|
107
|
+
self.input = { :escape => :close }
|
108
|
+
|
109
|
+
# load that map
|
110
|
+
push_game_state MapState.new('data/test.tmx')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
MapWindow.new.show
|
data/lib/tmx.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
autoload :Base64, 'base64'
|
2
|
+
autoload :Nokogiri, 'nokogiri'
|
3
|
+
autoload :WeakRef, 'weakref'
|
4
|
+
# autoload :Zlib, 'zlib'
|
5
|
+
|
6
|
+
# mysterious autoload failure
|
7
|
+
require 'zlib'
|
8
|
+
|
9
|
+
require 'tmx/nokogiri_additions'
|
10
|
+
|
11
|
+
module Tmx
|
12
|
+
autoload :Coder, 'tmx/coder'
|
13
|
+
autoload :Layer, 'tmx/layer'
|
14
|
+
autoload :Map, 'tmx/map'
|
15
|
+
autoload :ObjectGroup, 'tmx/object_group'
|
16
|
+
end
|
data/lib/tmx/coder.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Tmx
|
2
|
+
module Coder
|
3
|
+
# default coders
|
4
|
+
autoload :Base64, 'tmx/coder/base64'
|
5
|
+
autoload :Gzip, 'tmx/coder/gzip'
|
6
|
+
|
7
|
+
def self.encode str, *encodings
|
8
|
+
encodings.reject(&:nil?).reduce(str) do |data, encoding|
|
9
|
+
find_coder(encoding).encode(data)
|
10
|
+
end
|
11
|
+
end # encode
|
12
|
+
|
13
|
+
def self.decode str, *encodings
|
14
|
+
encodings.reject(&:nil?).reverse.reduce(str) do |data, encoding|
|
15
|
+
find_coder(encoding).decode(data)
|
16
|
+
end
|
17
|
+
end # decode
|
18
|
+
|
19
|
+
def self.find_coder name
|
20
|
+
const_name = name.to_s.capitalize.gsub /(?:\b|_)([a-z])/, &:upcase
|
21
|
+
if const_defined? const_name
|
22
|
+
const_get const_name
|
23
|
+
else
|
24
|
+
raise NameError, "unknown coder #{name}"
|
25
|
+
end
|
26
|
+
end # find_coder
|
27
|
+
|
28
|
+
def self.register_coder name, mod
|
29
|
+
const_name = name.to_s.capitalize.gsub /(?:\b|_)([a-z])/, &:upcase
|
30
|
+
if not const_defined? const_name
|
31
|
+
const_set const_name, mod
|
32
|
+
else
|
33
|
+
raise NameError, "coder #{name} already registered"
|
34
|
+
end
|
35
|
+
end # register_coder
|
36
|
+
|
37
|
+
end # Coder
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tmx
|
2
|
+
module Coder
|
3
|
+
module Gzip
|
4
|
+
def self.encode str
|
5
|
+
buffer = String.new
|
6
|
+
writer = Zlib::GzipWriter.new(StringIO.new(buffer))
|
7
|
+
writer << str
|
8
|
+
writer.close
|
9
|
+
buffer
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.decode str
|
13
|
+
reader = Zlib::GzipReader.new(StringIO.new(str))
|
14
|
+
output = reader.read
|
15
|
+
reader.close
|
16
|
+
output
|
17
|
+
end
|
18
|
+
|
19
|
+
end # Gzip
|
20
|
+
end
|
21
|
+
end
|
data/lib/tmx/layer.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Tmx
|
2
|
+
class Layer
|
3
|
+
attr_reader :properties
|
4
|
+
attr_reader :columns, :rows
|
5
|
+
|
6
|
+
def initialize map, data, properties
|
7
|
+
@map = WeakRef.new map
|
8
|
+
@properties = properties.dup
|
9
|
+
|
10
|
+
@columns = @properties.delete(:width) or raise ArgumentError, "layer width is required"
|
11
|
+
@rows = @properties.delete(:height) or raise ArgumentError, "layer height is required"
|
12
|
+
|
13
|
+
@tile_ids = case data
|
14
|
+
when String then data.unpack('V*')
|
15
|
+
when Array then data.dup
|
16
|
+
when nil then Array.new @columns * @rows, 0
|
17
|
+
else raise ArgumentError, "data must be a binary string or an array of integers"
|
18
|
+
end
|
19
|
+
end # initialize
|
20
|
+
|
21
|
+
def map; @map.__getobj__ end
|
22
|
+
|
23
|
+
def [] x, y
|
24
|
+
raise IndexError unless x_range.include? x and y_range.include? y
|
25
|
+
@tile_ids[offset(x, y)]
|
26
|
+
end
|
27
|
+
|
28
|
+
def []= x, y, id
|
29
|
+
raise IndexError unless x_range.include? x and y_range.include? y
|
30
|
+
@tile_ids[offset(x, y)] = id
|
31
|
+
end
|
32
|
+
|
33
|
+
def each_tile_id &block
|
34
|
+
y_range.each do |y|
|
35
|
+
x_range.each do |x|
|
36
|
+
yield x, y, @tile_ids[offset(x, y)]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end # each_tile_id
|
40
|
+
|
41
|
+
def x_range; 0...@columns end
|
42
|
+
def y_range; 0...@rows end
|
43
|
+
|
44
|
+
# def draw x_off, y_off, z_off, x_range, y_range
|
45
|
+
# x_range = [x_range.min, 0].max .. [x_range.max, @columns - 1].min
|
46
|
+
# y_range = [y_range.min, 0].max .. [y_range.max, @rows - 1].min
|
47
|
+
#
|
48
|
+
# tile_set = @map.tile_set
|
49
|
+
# tile_width = @map.tile_width
|
50
|
+
# tile_height = @map.tile_height
|
51
|
+
#
|
52
|
+
# y_range.each do |y|
|
53
|
+
# tile_y_off = y_off + y * tile_height
|
54
|
+
#
|
55
|
+
# x_range.each do |x|
|
56
|
+
# tile_x_off = x_off + x * tile_width
|
57
|
+
# tile_index = @tile_ids[offset(x, y)]
|
58
|
+
#
|
59
|
+
# image = tile_set[tile_index]
|
60
|
+
# next if image.nil?
|
61
|
+
#
|
62
|
+
# image.draw tile_x_off, tile_y_off, z_off, 1, 1, @color
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def offset x, y
|
70
|
+
x + y * @columns
|
71
|
+
end
|
72
|
+
|
73
|
+
end # Layer
|
74
|
+
end
|
data/lib/tmx/map.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
module Tmx
|
2
|
+
class Map
|
3
|
+
# autoload :TileCache, 'tmx/map/tile_cache'
|
4
|
+
autoload :XMLLoader, 'tmx/map/xml_loader'
|
5
|
+
|
6
|
+
include XMLLoader
|
7
|
+
|
8
|
+
attr_reader :properties
|
9
|
+
attr_reader :columns, :rows
|
10
|
+
attr_reader :width, :height
|
11
|
+
attr_reader :tile_width, :tile_height
|
12
|
+
|
13
|
+
attr_reader :layers, :object_groups#, :tile_set
|
14
|
+
|
15
|
+
DEFAULT_OPTIONS = {
|
16
|
+
# Scales pixel units to tile units (if true) or user-defined scale (if
|
17
|
+
# numeric) when passing them to callbacks.
|
18
|
+
:scale_units => false,
|
19
|
+
|
20
|
+
# Hooks for object, layer and tile set creation. Only on_object is
|
21
|
+
# implemented so far.
|
22
|
+
:on_tile_set => nil,
|
23
|
+
:on_layer => nil,
|
24
|
+
:on_object => nil,
|
25
|
+
|
26
|
+
# This option discards all layer, object group and tile set info after
|
27
|
+
# building the tile cache; uses less memory if you don't intend to
|
28
|
+
# modify the map at run time.
|
29
|
+
:discard_structure => false,
|
30
|
+
|
31
|
+
# These three options allow finer grained control of what to throw away
|
32
|
+
# in case you intend to modify only certain aspects of the map.
|
33
|
+
:discard_layer_info => false,
|
34
|
+
:discard_object_info => false,
|
35
|
+
}
|
36
|
+
|
37
|
+
def initialize file_name, options = {}
|
38
|
+
options = DEFAULT_OPTIONS.merge options
|
39
|
+
|
40
|
+
# TODO move this XML code to an external module
|
41
|
+
# TODO allow file name or xml document?
|
42
|
+
# TODO allow other map formats?
|
43
|
+
|
44
|
+
mapdef = File.open(file_name) do |io|
|
45
|
+
doc = Nokogiri::XML(io) { |conf| conf.noent.noblanks }
|
46
|
+
|
47
|
+
# TODO figure out why this always fails
|
48
|
+
# errors = doc.validate
|
49
|
+
|
50
|
+
doc.root
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO proper version check; learn about Tmx versions if there are any
|
54
|
+
raise "Only version 1.0 maps are currently supported" unless mapdef['version'] == '1.0'
|
55
|
+
raise "Only orthogonal maps are currently supported" unless mapdef['orientation'] == 'orthogonal'
|
56
|
+
|
57
|
+
# @cache = TileCache.new self
|
58
|
+
|
59
|
+
@tile_width = mapdef['tilewidth'].to_i
|
60
|
+
@tile_height = mapdef['tileheight'].to_i
|
61
|
+
|
62
|
+
@columns = mapdef['width'].to_i
|
63
|
+
@rows = mapdef['height'].to_i
|
64
|
+
|
65
|
+
@scale_units = case options[:scale_units]
|
66
|
+
when Numeric then options[:scale_units].to_f
|
67
|
+
when :tile_width then 1.0 / @tile_width
|
68
|
+
when :tile_height then 1.0 / @tile_height
|
69
|
+
when true then 1.0 / [@tile_width, @tile_height].min
|
70
|
+
else false
|
71
|
+
end
|
72
|
+
|
73
|
+
if @scale_units
|
74
|
+
@width = @columns.to_f * @scale_units
|
75
|
+
@height = @rows.to_f * @scale_units
|
76
|
+
else
|
77
|
+
@width = @columns * @tile_width
|
78
|
+
@height = @rows * @tile_height
|
79
|
+
end
|
80
|
+
|
81
|
+
@properties = mapdef.tmx_parse_properties
|
82
|
+
|
83
|
+
# @tile_set = TileSet.new self
|
84
|
+
|
85
|
+
@layers = Hash[]
|
86
|
+
@object_groups = Hash[]
|
87
|
+
|
88
|
+
# callback for custom object creation
|
89
|
+
@on_object = options[:on_object]
|
90
|
+
|
91
|
+
# mapdef.xpath('tileset').each do |xml|
|
92
|
+
# @tile_set.load_tiles *parse_tile_set_def(xml)
|
93
|
+
# end
|
94
|
+
|
95
|
+
mapdef.xpath('layer').each do |xml|
|
96
|
+
layer = parse_layer_def xml
|
97
|
+
name = layer.properties[:name]
|
98
|
+
@layers[name] = layer
|
99
|
+
end # layers
|
100
|
+
|
101
|
+
|
102
|
+
mapdef.xpath('objectgroup').each do |xml|
|
103
|
+
group = parse_object_group_def xml
|
104
|
+
name = group.name
|
105
|
+
@object_groups[name] = group
|
106
|
+
end # object groups
|
107
|
+
|
108
|
+
# @cache.rebuild!
|
109
|
+
|
110
|
+
discard_structure = @properties.delete(:discard_structure)
|
111
|
+
|
112
|
+
@layers = nil if @properties.delete(:discard_layer_info) || discard_structure
|
113
|
+
# @tile_sets = nil if @properties.delete(:discard_tile_info) || discard_structure
|
114
|
+
@object_groups = nil if @properties.delete(:discard_object_info) || discard_structure
|
115
|
+
|
116
|
+
end # initialize
|
117
|
+
|
118
|
+
# def create_tile_set name, file_name_or_images, properties
|
119
|
+
# raise NotImplementedError
|
120
|
+
# end
|
121
|
+
|
122
|
+
def create_layer name, data, properties
|
123
|
+
raise NotImplementedError
|
124
|
+
end
|
125
|
+
|
126
|
+
def create_object_group name, properties
|
127
|
+
raise NotImplementedError
|
128
|
+
end
|
129
|
+
|
130
|
+
def draw x_off, y_off, z_off = 0, x_range = 0...@columns, y_range = 0...@rows
|
131
|
+
# @cache.draw x_off, y_off, z_off, x_range, y_range
|
132
|
+
@layers.each_value.with_index do |layer, index|
|
133
|
+
layer.draw x_off, y_off, z_off + index, x_range, y_range
|
134
|
+
end
|
135
|
+
end # draw
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
def on_object name, group, properties
|
140
|
+
if @on_object
|
141
|
+
@on_object.call name, group, properties
|
142
|
+
else
|
143
|
+
properties
|
144
|
+
end
|
145
|
+
end # on_object
|
146
|
+
end # Map
|
147
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Tmx
|
2
|
+
# WARNING this is currently deprecated and not actually used
|
3
|
+
class Map::TileCache
|
4
|
+
def initialize map
|
5
|
+
@map = WeakRef.new map
|
6
|
+
|
7
|
+
@tile_cache = []
|
8
|
+
@map_cache = []
|
9
|
+
|
10
|
+
@layer_count = 0
|
11
|
+
|
12
|
+
@columns, @rows = 0, 0
|
13
|
+
@tile_width, @tile_height = 0, 0
|
14
|
+
end # initialize
|
15
|
+
|
16
|
+
def rebuild!
|
17
|
+
rebuild_tile_set!
|
18
|
+
rebuild_map!
|
19
|
+
end
|
20
|
+
|
21
|
+
def rebuild_tile_set!
|
22
|
+
raise RuntimeError, "tile set information has been discarded" if @map.tile_sets.nil?
|
23
|
+
|
24
|
+
@tile_cache.clear
|
25
|
+
@map.tile_sets.each_value do |tile_set|
|
26
|
+
@tile_cache[tile_set.range] = tile_set.tiles
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def rebuild_map!
|
31
|
+
raise RuntimeError, "layer information has been discarded" if @map.layers.nil?
|
32
|
+
|
33
|
+
@map_cache.clear
|
34
|
+
|
35
|
+
@layer_count = @map.layers.count
|
36
|
+
|
37
|
+
@columns, @rows = @map.columns, @map.rows
|
38
|
+
@tile_width, @tile_height = @map.tile_width, @map.tile_height
|
39
|
+
|
40
|
+
@map.layers.each_value.with_index do |layer, layer_index|
|
41
|
+
(0...@rows).each do |y|
|
42
|
+
(0...@columns).each do |x|
|
43
|
+
index = _tile_index layer_index, x, y
|
44
|
+
tile_id = layer[x, y]
|
45
|
+
@map_cache[index] = @tile_cache[tile_id]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end # rebuild_map!
|
50
|
+
|
51
|
+
def draw x_off, y_off, z_off, x_range, y_range
|
52
|
+
x_range = [x_range.min, 0].max .. [x_range.max, @columns - 1].min
|
53
|
+
y_range = [y_range.min, 0].max .. [y_range.max, @rows - 1].min
|
54
|
+
|
55
|
+
y_range.each do |y|
|
56
|
+
tile_y_off = y_off + y * @tile_height
|
57
|
+
|
58
|
+
x_range.each do |x|
|
59
|
+
tile_x_off = x_off + x * @tile_width
|
60
|
+
|
61
|
+
range = _tile_range x, y
|
62
|
+
@map_cache[range].each.with_index do |image, z|
|
63
|
+
next if image.nil?
|
64
|
+
image.draw tile_x_off, tile_y_off, z_off + z
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end # draw
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def _tile_index layer_index, x, y
|
74
|
+
y * @columns * @layer_count + x * @layer_count + layer_index
|
75
|
+
end
|
76
|
+
|
77
|
+
def _tile_range x, y
|
78
|
+
first = _tile_index 0, x, y
|
79
|
+
first...(first + @layer_count)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Tmx
|
2
|
+
module Map::XMLLoader
|
3
|
+
protected
|
4
|
+
|
5
|
+
def parse_tile_set_def xml
|
6
|
+
properties = xml.tmx_parse_attributes
|
7
|
+
image_path = File.absolute_path xml.xpath('image/@source').first.value, File.dirname(xml.document.url)
|
8
|
+
[ image_path, properties ]
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_layer_def xml
|
12
|
+
properties = xml.tmx_parse_properties.merge! xml.tmx_parse_attributes
|
13
|
+
Layer.new self, xml.tmx_data, properties
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_object_group_def xml
|
17
|
+
properties = xml.tmx_parse_properties.merge! xml.tmx_parse_attributes
|
18
|
+
group = ObjectGroup.new self, properties
|
19
|
+
|
20
|
+
xml.xpath('object').each do |child|
|
21
|
+
obj = parse_object_def(child, group)
|
22
|
+
group.add obj if obj
|
23
|
+
end
|
24
|
+
|
25
|
+
group
|
26
|
+
end # parse_object_group_def
|
27
|
+
|
28
|
+
def parse_object_def xml, group
|
29
|
+
properties = xml.tmx_parse_properties.merge! xml.tmx_parse_attributes
|
30
|
+
# name = properties[:name]
|
31
|
+
name = properties[:name]
|
32
|
+
|
33
|
+
[:x, :y, :width, :height].each do |key|
|
34
|
+
properties[key] = properties[key] * @scale_units
|
35
|
+
end if @scale_units
|
36
|
+
|
37
|
+
on_object name, group, properties
|
38
|
+
end
|
39
|
+
|
40
|
+
end # Map::XMLLoader
|
41
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# TODO investigate writing Nokogiri::TMX class hierarchy (yeah right)
|
2
|
+
|
3
|
+
class Nokogiri::XML::Node
|
4
|
+
def to_sym; self.to_s.to_sym end
|
5
|
+
|
6
|
+
def to_i; self.to_s.to_i end
|
7
|
+
def to_f; self.to_s.to_f end
|
8
|
+
def to_c; self.to_s.to_c end
|
9
|
+
def to_r; self.to_s.to_r end
|
10
|
+
|
11
|
+
def tmx_parse
|
12
|
+
str = to_s
|
13
|
+
case str
|
14
|
+
when '' then nil
|
15
|
+
when %r(^ (?: false | no | off ) $)ix then false
|
16
|
+
when %r(^ (?: true | yes | on ) $)ix then true
|
17
|
+
when %r(^ [+-]? \d+ / [+-]? \d+ $)ix then str.to_r
|
18
|
+
when %r(^ [+-]? (?: \d+ \. \d+ [+-] ) \d+ i $)ix then str.to_c
|
19
|
+
when %r(^ [+-]? \d+ \. \d+ $)ix then str.to_f
|
20
|
+
when %r(^ [+-]? \d+ $)ix then str.to_i
|
21
|
+
when %r(^ \# [0-9a-f]{6} $)ix
|
22
|
+
0xff000000 | str[1..6].to_i(16)
|
23
|
+
when %r(^ \# [0-9a-f]{8} $)ix
|
24
|
+
str[1..8].to_i(16)
|
25
|
+
else str
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Nokogiri::XML::Element
|
31
|
+
def tmx_parse_attributes
|
32
|
+
attributes.reduce({}) do |h, pair|
|
33
|
+
name = pair[0].to_sym
|
34
|
+
value = pair[1].tmx_parse
|
35
|
+
h.merge name => value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def tmx_parse_properties
|
40
|
+
properties_element = xpath('properties').first or return {}
|
41
|
+
properties_element.xpath('property').reduce({}) do |h, property|
|
42
|
+
name = property.attribute('name').to_sym
|
43
|
+
value = property.attribute('value').tmx_parse
|
44
|
+
h.merge! name => value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def tmx_data
|
49
|
+
data = ''
|
50
|
+
xpath('data').each do |data_element|
|
51
|
+
attrs = data_element.tmx_parse_attributes
|
52
|
+
data << Tmx::Coder.decode(data_element.text, attrs[:compression], attrs[:encoding])
|
53
|
+
end
|
54
|
+
data
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Tmx
|
2
|
+
class ObjectGroup
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :properties
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize map, properties = {}
|
9
|
+
@map = WeakRef.new map
|
10
|
+
@properties = properties
|
11
|
+
@objects = Hash[]
|
12
|
+
|
13
|
+
@name = properties.delete :name
|
14
|
+
end
|
15
|
+
|
16
|
+
def map; @map.__getobj__ end
|
17
|
+
|
18
|
+
def add obj
|
19
|
+
@objects[obj.object_id] = obj
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove obj
|
23
|
+
@objects.delete obj.object_id
|
24
|
+
end
|
25
|
+
|
26
|
+
def each &block
|
27
|
+
@objects.each_value &block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/tmx/tile_set.rb
ADDED
@@ -0,0 +1,35 @@
|
|
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
|
data/lib/tmx/version.rb
ADDED
data/spec/coder_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
describe 'Coder' do
|
3
|
+
it 'can find the default coders' do
|
4
|
+
Tmx::Coder.find_coder(:base64).should_not == nil
|
5
|
+
Tmx::Coder.find_coder(:gzip).should_not == nil
|
6
|
+
end
|
7
|
+
|
8
|
+
CODER_TEST_STRING = <<-EOS
|
9
|
+
Lorem ipsum dolor sit amet, consecteteur adapiscing elit.
|
10
|
+
EOS
|
11
|
+
|
12
|
+
def coder_round_trip *encodings
|
13
|
+
encoded = Tmx::Coder.encode CODER_TEST_STRING, *encodings
|
14
|
+
decoded = Tmx::Coder.decode encoded, *encodings
|
15
|
+
decoded.should == CODER_TEST_STRING
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can round trip base64' do
|
19
|
+
coder_round_trip :base64
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can round trip gzip' do
|
23
|
+
coder_round_trip :gzip
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can round trip multiple encodings' do
|
27
|
+
coder_round_trip :gzip, :base64
|
28
|
+
end
|
29
|
+
end
|
data/spec/map_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
describe 'Map' do
|
3
|
+
it 'can load the test map' do
|
4
|
+
$map = Tmx::Map.new File.join($data_dir, 'test.tmx'),
|
5
|
+
:scale_units => false
|
6
|
+
$map.should.is_a? Tmx::Map
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'has the correct dimensions' do
|
10
|
+
$map.columns.should == 16
|
11
|
+
$map.rows.should == 16
|
12
|
+
|
13
|
+
$map.width.should == 16 * $map.tile_width
|
14
|
+
$map.height.should == 16 * $map.tile_height
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has the correct tile dimensions' do
|
18
|
+
$map.tile_width.should == 16
|
19
|
+
$map.tile_height.should == 16
|
20
|
+
end
|
21
|
+
|
22
|
+
# it 'loads all tile set definitions into one flat set' do
|
23
|
+
# $map.tile_set.should.is_a? Tmx::TileSet
|
24
|
+
# $map.tile_set[0].should == nil
|
25
|
+
# $map.tile_set[1].should.is_a? Gosu::Image
|
26
|
+
# end
|
27
|
+
|
28
|
+
it 'loads all layers' do
|
29
|
+
$map.layers.count.should == 3
|
30
|
+
$map.layers.each do |name, layer|
|
31
|
+
layer.should.is_a? Tmx::Layer
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'loads all object groups' do
|
36
|
+
$map.object_groups.count.should == 2
|
37
|
+
$map.object_groups.each do |name, group|
|
38
|
+
Tmx::ObjectGroup.should === group
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# it 'can draw itself' do
|
43
|
+
# $window.run_test\
|
44
|
+
# :time => 1.0,
|
45
|
+
# :before => proc { @x, @y = 0, 0 },
|
46
|
+
# :update => proc { @y -= 1 },
|
47
|
+
# :draw => proc { $map.draw @x, @y },
|
48
|
+
# end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
describe 'object groups' do
|
4
|
+
|
5
|
+
# {:image=>"door.png", :target=>"door2", :name=>"door1", :type=>"Door", :x=>128, :y=>208, :width=>16, :height=>32}
|
6
|
+
# {:image=>"door.png", :target=>"door1", :name=>"door2", :type=>"Door", :x=>64, :y=>32, :width=>16, :height=>32}
|
7
|
+
|
8
|
+
it 'loads all object group properties' do
|
9
|
+
map = Tmx::Map.new File.join($data_dir, 'test.tmx'), :scale_units => false
|
10
|
+
object_group = map.object_groups["doors"]
|
11
|
+
object_group.should_not be_nil
|
12
|
+
|
13
|
+
captured = []
|
14
|
+
object_group.each do |obj|
|
15
|
+
captured << obj
|
16
|
+
end
|
17
|
+
|
18
|
+
obj = captured.first
|
19
|
+
|
20
|
+
obj[:target].should == "door2"
|
21
|
+
obj[:image].should == "door.png"
|
22
|
+
obj[:type].should == "Door"
|
23
|
+
obj[:x].should == 128
|
24
|
+
obj[:y].should == 208
|
25
|
+
obj[:width].should == 16
|
26
|
+
obj[:height].should == 32
|
27
|
+
obj[:name].should == "door1"
|
28
|
+
|
29
|
+
obj = captured.last
|
30
|
+
obj[:target].should == "door1"
|
31
|
+
obj[:image].should == "door.png"
|
32
|
+
obj[:type].should == "Door"
|
33
|
+
obj[:x].should == 64
|
34
|
+
obj[:y].should == 32
|
35
|
+
obj[:width].should == 16
|
36
|
+
obj[:height].should == 32
|
37
|
+
obj[:name].should == "door2"
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
tmx_root = File.dirname(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
$:.unshift File.join(tmx_root, 'lib')
|
5
|
+
$data_dir = File.join(tmx_root, 'data')
|
6
|
+
|
7
|
+
require 'gosu'
|
8
|
+
require 'tmx'
|
9
|
+
|
10
|
+
class TMXSpecWindow < Gosu::Window
|
11
|
+
def initialize
|
12
|
+
super 400, 200, false
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_test options = {}
|
16
|
+
@before = options.delete :before
|
17
|
+
@update = options.delete :update
|
18
|
+
@draw = options.delete :draw
|
19
|
+
@after = options.delete :after
|
20
|
+
|
21
|
+
@time_limit = options.delete(:time) || 1.0
|
22
|
+
@start_time = Gosu.milliseconds / 1000.0
|
23
|
+
|
24
|
+
@before.call if @before
|
25
|
+
|
26
|
+
show
|
27
|
+
end
|
28
|
+
|
29
|
+
def end_test
|
30
|
+
@after.call if @after
|
31
|
+
close
|
32
|
+
end
|
33
|
+
|
34
|
+
def update
|
35
|
+
this_time = Gosu.milliseconds / 1000.0
|
36
|
+
@start_time
|
37
|
+
|
38
|
+
@update.call(Gosu.milliseconds) if @update
|
39
|
+
|
40
|
+
if this_time - @start_time > @time_limit
|
41
|
+
end_test
|
42
|
+
end
|
43
|
+
|
44
|
+
@last_time = this_time
|
45
|
+
end
|
46
|
+
|
47
|
+
def draw
|
48
|
+
@draw.call(Gosu.milliseconds) if @draw
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
$window = TMXSpecWindow.new
|
data/tmx.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "tmx/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "tmx"
|
7
|
+
s.version = Tmx::VERSION
|
8
|
+
s.authors = ["erisdiscord"]
|
9
|
+
s.email = ["eris.discord@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Eventually, a usable TMX map loader that works with Gosu and doesn't care whether you're using Chingu or some home-grown game engine of your own devising.}
|
12
|
+
s.description = %q{Eventually, a usable TMX map loader that works with Gosu and doesn't care whether you're using Chingu or some home-grown game engine of your own devising.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "tmx"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "gosu"
|
25
|
+
|
26
|
+
s.add_runtime_dependency "nokogiri"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tmx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- erisdiscord
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-04 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &2161052740 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2161052740
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2161052300 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2161052300
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: gosu
|
38
|
+
requirement: &2161051880 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2161051880
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: nokogiri
|
49
|
+
requirement: &2161051440 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2161051440
|
58
|
+
description: Eventually, a usable TMX map loader that works with Gosu and doesn't
|
59
|
+
care whether you're using Chingu or some home-grown game engine of your own devising.
|
60
|
+
email:
|
61
|
+
- eris.discord@gmail.com
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- Gemfile
|
68
|
+
- README.markdown
|
69
|
+
- Rakefile
|
70
|
+
- data/door.png
|
71
|
+
- data/dude.png
|
72
|
+
- data/test-tiles.png
|
73
|
+
- data/test.tmx
|
74
|
+
- examples/example-chingu.rb
|
75
|
+
- lib/tmx.rb
|
76
|
+
- lib/tmx/coder.rb
|
77
|
+
- lib/tmx/coder/base64.rb
|
78
|
+
- lib/tmx/coder/gzip.rb
|
79
|
+
- lib/tmx/layer.rb
|
80
|
+
- lib/tmx/map.rb
|
81
|
+
- lib/tmx/map/tile_cache.rb
|
82
|
+
- lib/tmx/map/xml_loader.rb
|
83
|
+
- lib/tmx/nokogiri_additions.rb
|
84
|
+
- lib/tmx/object_group.rb
|
85
|
+
- lib/tmx/tile_set.rb
|
86
|
+
- lib/tmx/version.rb
|
87
|
+
- spec/coder_spec.rb
|
88
|
+
- spec/map_spec.rb
|
89
|
+
- spec/nokogiri_spec.rb
|
90
|
+
- spec/object_group_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- tmx.gemspec
|
93
|
+
homepage: ''
|
94
|
+
licenses: []
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project: tmx
|
113
|
+
rubygems_version: 1.8.6
|
114
|
+
signing_key:
|
115
|
+
specification_version: 3
|
116
|
+
summary: Eventually, a usable TMX map loader that works with Gosu and doesn't care
|
117
|
+
whether you're using Chingu or some home-grown game engine of your own devising.
|
118
|
+
test_files:
|
119
|
+
- spec/coder_spec.rb
|
120
|
+
- spec/map_spec.rb
|
121
|
+
- spec/nokogiri_spec.rb
|
122
|
+
- spec/object_group_spec.rb
|
123
|
+
- spec/spec_helper.rb
|