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