retmx 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/retmx.rb +476 -0
  2. metadata +45 -0
data/lib/retmx.rb ADDED
@@ -0,0 +1,476 @@
1
+ #Libreria para leer archivo TMX de mapeditor.org
2
+ #Author:: Jovany Leandro G.C (mailto: info@manadalibre.org)
3
+ #Copyright:: Copyright (c) 2011 Jovany Leandro G.C
4
+ #License:: GPLv3 or any later version
5
+ #date: 2011-12-10
6
+ require 'rexml/document'
7
+ require 'base64'
8
+ require 'zlib'
9
+ require 'csv'
10
+
11
+ module RETMX
12
+ include REXML
13
+
14
+ def RETMX.load(file)
15
+ doc = Document.new (File.new(file))
16
+ m = Map.new(doc.root)
17
+ end
18
+
19
+
20
+ #Wraps any number of custom properties. Can be used as a child of the map, tile (when part of a tileset), layer, objectgroup and object elements.
21
+ class Properties
22
+ include Enumerable
23
+
24
+ attr_reader :xml
25
+
26
+
27
+ def initialize(xml)
28
+ @xml = xml
29
+ @property = {}
30
+ @xml.elements.each('properties/property') {|e|
31
+ @property[e.attributes['name']] = e.attributes['value']
32
+ }
33
+ end
34
+
35
+ def [](i)
36
+ @property[i]
37
+ end
38
+
39
+ def each
40
+ @property.each {|i| yield i}
41
+ end
42
+ end
43
+
44
+
45
+ #The +tilewidth+ and +tileheight+ properties determine the general grid size of the map. The individual tiles may have different sizes. Larger tiles will extend at the top and right (anchored to the bottom left).
46
+ class Map
47
+
48
+
49
+ #The TMX format version, generally 1.0
50
+ attr_reader :version
51
+
52
+ #Map orientation. Tiled supports "orthogonal" and "isometric" at the moment.
53
+ attr_reader :orientation
54
+
55
+ #The map width in tiles.
56
+ attr_reader :width
57
+
58
+ #The map height in tiles.
59
+ attr_reader :height
60
+
61
+ #The width of a tile.
62
+ attr_reader :tilewidth
63
+
64
+ #The height of a tile.
65
+ attr_reader :tileheight
66
+
67
+ #Layers on map
68
+ attr_reader :layers
69
+
70
+ #Tileset on map
71
+ attr_reader :tilesets
72
+
73
+ #Objects groups on map
74
+ attr_reader :objectgroups
75
+
76
+ #Properties on map
77
+ attr_reader :property
78
+
79
+ #REXML internal
80
+ attr_reader :xml
81
+ def initialize(doc)
82
+ @version = doc.attributes['version']
83
+ @orientation = doc.attributes['orientation']
84
+ @width = doc.attributes['width'].to_i
85
+ @height = doc.attributes['height'].to_i
86
+ @tilewidth = doc.attributes['tilewidth'].to_i
87
+ @tileheight = doc.attributes['tileheight'].to_i
88
+
89
+ @tilesets = {}
90
+ @layers = {}
91
+ @objectgroups = {}
92
+ @xml = doc
93
+
94
+ build(doc)
95
+ end
96
+
97
+ private
98
+ def build(doc)
99
+ doc.elements.each("tileset") { |e|
100
+ @tilesets[e.attributes['name']] = TileSet.new(self, e)
101
+ }
102
+ doc.elements.each("layer") { |e|
103
+ @layers[e.attributes['name']] = Layer.new(self, e)
104
+ }
105
+ doc.elements.each("objectgroup") { |e|
106
+ @objectgroups[e.attributes['name']] = ObjectGroup.new(self, e)
107
+ }
108
+ @property = Properties.new(doc)
109
+ end
110
+
111
+ #The object group is in fact a map layer, and is hence called "object layer" in Tiled Qt.
112
+ class ObjectGroup
113
+ include Enumerable
114
+
115
+ #The name of the object group.
116
+ attr_reader :name
117
+
118
+ #The x coordinate of the object group in tiles. Defaults to 0 and can no longer be changed in Tiled Qt.
119
+ attr_reader :x
120
+
121
+ #The y coordinate of the object group in tiles. Defaults to 0 and can no longer be changed in Tiled Qt.
122
+ attr_reader :y
123
+
124
+ #The width of the object group in tiles. Meaningless.
125
+ attr_reader :width
126
+
127
+ #The height of the object group in tiles. Meaningless
128
+ attr_reader :height
129
+
130
+
131
+ def initialize(map, xml)
132
+ @map = map
133
+ @objects = []
134
+ build(xml)
135
+ end
136
+
137
+
138
+ def each
139
+ @objects.each { |i| yield i}
140
+ end
141
+
142
+ private
143
+ def build(xml)
144
+ xml.elements.each('object') {|e|
145
+ @objects << Object.new(self, e)
146
+ }
147
+ end
148
+
149
+
150
+ =begin
151
+ While tile layers are very suitable for anything repetitive aligned to the tile grid, sometimes you want to annotate your map with other information, not necessarily aligned to the grid. Hence the objects have their coordinates and size in pixels, but you can still easily align that to the grid when you want to.
152
+
153
+ You generally use objects to add custom information to your tile map, such as spawn points, warps, exits, etc.
154
+
155
+ When the object has a gid set, then it is represented by the image of the tile with that global ID. Currently that means width and height are ignored for such objects. The image alignment currently depends on the map orientation. In orthogonal orientation it's aligned to the bottom-left while in isometric it's aligned to the bottom-center.
156
+ =end
157
+ class Object
158
+ #The name of the object. An arbitrary string.
159
+ attr_reader :name
160
+
161
+ #The type of the object. An arbitrary string.
162
+ attr_reader :type
163
+
164
+ #The x coordinate of the object in pixels.
165
+ attr_reader :x
166
+
167
+ #The y coordinate of the object in pixels.
168
+ attr_reader :y
169
+
170
+ #The width of the object in pixels.
171
+ attr_reader :width
172
+
173
+ #The height of the object in pixels.
174
+ attr_reader :height
175
+
176
+ #An reference to a tile (optional).
177
+ attr_reader :gid
178
+
179
+ def initialize(og, e)
180
+ @objectgroup = og
181
+ @name = e.attributes['name']
182
+ @type = e.attributes['type']
183
+ @x = e.attributes['x'].to_i
184
+ @y = e.attributes['y'].to_i
185
+ @width = e.attributes['width'].to_i
186
+ @height = e.attributes['height'].to_i
187
+ @gid = e.attributes['gid'].nil? ? nil : e.attributes['gid'].to_i
188
+ end
189
+ end
190
+ end
191
+
192
+ class Layer
193
+ #The name of the layer.
194
+ attr_reader :name
195
+
196
+ #The x coordinate of the layer in tiles. Defaults to 0 and can no longer be changed in Tiled Qt.
197
+ attr_reader :x
198
+
199
+ #The y coordinate of the layer in tiles. Defaults to 0 and can no longer be changed in Tiled Qt.
200
+ attr_reader :y
201
+
202
+ #The width of the layer in tiles. Traditionally required, but as of Tiled Qt always the same as the map width.
203
+ attr_reader :width
204
+
205
+ #The height of the layer in tiles. Traditionally required, but as of Tiled Qt always the same as the map height.
206
+ attr_reader :height
207
+
208
+ #The opacity of the layer as a value from 0 to 1. Defaults to 1.
209
+ attr_reader :opacity
210
+
211
+ #Whether the layer is shown (1) or hidden (0). Defaults to 1.
212
+ attr_reader :visible
213
+
214
+ #Data
215
+ attr_reader :data
216
+
217
+ #Map belongs
218
+ attr_reader :map
219
+
220
+ #Properties
221
+ attr_reader :property
222
+
223
+ #REXML internal
224
+ attr_reader :xml
225
+ def initialize(map, xml)
226
+
227
+ @name = xml.attributes['name']
228
+ @x = xml.attributes['x'].nil? ? 0 : xml.attributes['x']
229
+ @y = xml.attributes['y'].nil? ? 0 : xml.attributes['y']
230
+ @width = xml.attributes['width'].nil? ? xml.parent.attributes['width'].to_i : xml.attributes['width'].to_i
231
+ @height = xml.attributes['height'].nil? ? xml.parent.attributes['height'].to_i : xml.attributes['height'].to_i
232
+ @opacity = xml.attributes['opacity'].nil? ? 1 : xml.attributes['opacity'].to_i
233
+ @visible = xml.attributes['visible'].nil? ? 1 : xml.attributes['visible'].to_i
234
+ @opacity = xml.attributes['opacity'].nil? ? 1 : xml.attributes['opacity'].to_f
235
+ @data = nil
236
+ @map = map
237
+ build(xml)
238
+ end
239
+
240
+
241
+ #This function is used for render the layer
242
+ def render(&block) #:yields: block_y, block_x, tileset, index
243
+ @height.times {|by| #block row
244
+ @width.times {|bx| #block col
245
+ cell = @data[(by * @height) + bx]
246
+ @map.tilesets.each {|k, t|
247
+ if t.firstgid <= cell.gid
248
+ cell.gid -= t.firstgid
249
+ block.call(bx, by, t, cell)
250
+ break
251
+ end
252
+ }
253
+ }
254
+ }
255
+ end
256
+
257
+ private
258
+ def build(doc)
259
+ @data = Data.new(self, doc.elements['data'])
260
+ @property = Properties.new(doc)
261
+ end
262
+
263
+ class Data
264
+ include Enumerable
265
+ GID_FLIP_X = 1.<< 31
266
+ GID_FLIP_Y = 1.<< 30
267
+
268
+ #The encoding used to encode the tile layer data. When used, it can be "base64" and "csv" at the moment.
269
+ attr_reader :encoding
270
+
271
+ #The compression used to compress the tile layer data. Tiled Qt supports "gzip" and "zlib".
272
+ attr_reader :compression
273
+
274
+ #Layer belongs
275
+ attr_reader :layer
276
+
277
+ #REXML internal
278
+ attr_reader :xml
279
+
280
+
281
+ def initialize(layer, xml)
282
+ @layer = layer
283
+ @xml = xml
284
+ @encoding = xml.attributes['encoding']
285
+ @compression = xml.attributes['compression']
286
+
287
+ @raw = []
288
+ @raw_data = xml.text
289
+ @data = []
290
+
291
+ #decoding
292
+ case @encoding
293
+ when 'base64'
294
+ @raw_data = Base64.decode64(@raw_data)
295
+ when 'csv'
296
+ @raw_data = @raw_data.tr("\n",'')
297
+ end
298
+
299
+ #inflate compress
300
+ case @compression
301
+ when 'zlib'
302
+ zs = Zlib::Inflate.new
303
+ @raw_data = zs.inflate(@raw_data)
304
+ zs.finish
305
+ zs.close
306
+ when 'gzip'
307
+ gz = Zlib::GzipReader.new(StringIO.new(@raw_data))
308
+ @raw_data = gz.read
309
+ gz.close
310
+ end
311
+
312
+ #Data XML
313
+ if @encoding.nil? and @compression.nil?
314
+ xml.elements.each('tile') {|e|
315
+ @raw << e.attributes['gid'].to_i
316
+ }
317
+ elsif @encoding == 'csv'
318
+ @raw = @raw_data.split(',').collect {|x| x.to_i}
319
+ else
320
+ @raw = @raw_data.unpack("V*")
321
+ end
322
+
323
+ get_data
324
+
325
+ end
326
+
327
+
328
+ def [] (i)
329
+ @data[i]
330
+ end
331
+
332
+ def each
333
+ @data.each { |i| yield i }
334
+ end
335
+
336
+ private
337
+
338
+ #Discover, global tile id
339
+ def decode_gid(raw_gid)
340
+ flags = 0
341
+ flags += 1 if raw_gid & GID_FLIP_X == GID_FLIP_X
342
+ flags += 2 if raw_gid & GID_FLIP_Y == GID_FLIP_Y
343
+ gid = raw_gid & ~(GID_FLIP_X | GID_FLIP_Y)
344
+ return [gid, flags]
345
+ end
346
+
347
+ #Extract array of gids from array of ints
348
+ def get_data
349
+ tile_index = 0
350
+ gi = Struct.new(:gid, :flags)
351
+ @layer.height.times {|y|
352
+ @layer.width.times {|x|
353
+ next_gid = @raw[tile_index]
354
+ gid, flags = decode_gid(next_gid)
355
+ @data[tile_index] = gi.new(gid, flags)
356
+ tile_index += 1
357
+ }
358
+ }
359
+ end
360
+ end
361
+ end
362
+
363
+
364
+ class TileSet
365
+ #The first global tile ID of this tileset (this global ID maps to the first tile in this tileset).
366
+ attr_reader :firstgid
367
+
368
+ #If this tileset is stored in an external TSX (Tile Set XML) file, this attribute refers to that file.
369
+ attr_reader :source
370
+
371
+ #The name of this tileset.
372
+ attr_reader :name
373
+
374
+ #The (maximum) width of the tiles in this tileset.
375
+ attr_reader :tilewidth
376
+
377
+ #The (maximum) height of the tiles in this tileset.
378
+ attr_reader :tileheight
379
+
380
+ #The spacing in pixels between the tiles in this tileset (applies to the tileset image).
381
+ attr_reader :spacing
382
+
383
+ #The margin around the tiles in this tileset (applies to the tileset image).
384
+ attr_reader :margin
385
+
386
+ #Image has
387
+ attr_reader :image
388
+
389
+ #Properties
390
+ attr_reader :property
391
+
392
+ #Map belongs
393
+ attr_reader :map
394
+ def initialize(map, xml)
395
+ @map = map
396
+ @firstgid = xml.attributes['firstgid'].to_i
397
+ @source = xml.attributes['source']
398
+ @name = xml.attributes['name']
399
+ @tilewidth = xml.attributes['tilewidth'].to_i
400
+ @tileheight = xml.attributes['tileheight'].to_i
401
+ @spacing = xml.attributes['spacing'].nil? ? 0 : xml.attributes['spacing'].to_i
402
+ @margin = xml.attributes['margin'].nil? ? 0 : xml.attributes['margin'].to_i
403
+
404
+ @image = nil
405
+ #array of Recs that know how cut a image
406
+ @tiles = []
407
+ build(xml)
408
+ end
409
+
410
+
411
+ #Get tile at index +i+
412
+ def at(i)
413
+ return @tiles[i.gid] if i.respond_to? :gid
414
+ return @tiles[i]
415
+ end
416
+
417
+ private
418
+ def build(xml)
419
+ @image = Image.new(xml)
420
+ @property = Properties.new(xml)
421
+ xml.elements.each('tile') { |e| @tiles << Tile.new(e) }
422
+
423
+
424
+
425
+ #Rect: x, y, w, h
426
+ srect = Struct.new(:x, :y, :w, :h)
427
+ stop_width = @image.width - @tilewidth
428
+ stop_height = @image.height - @tileheight
429
+ tile_num = 0
430
+ #calculate how cut image
431
+ @margin.step(stop_height, @tileheight + @spacing) {|y|
432
+ @margin.step(stop_width, @tilewidth + @spacing) {|x|
433
+ @tiles[tile_num] = srect.new(x, y, @tilewidth, @tileheight)
434
+ tile_num += 1
435
+ }
436
+ }
437
+ end
438
+
439
+ class Tile
440
+ #The local tile ID within its tileset.
441
+ attr_reader :id
442
+
443
+ def initialize(doc)
444
+ @id = doc.attributes['id']
445
+ end
446
+ end
447
+
448
+ class Image
449
+ #The reference to the tileset image file (Tiled supports most common image formats).
450
+ attr_reader :source
451
+
452
+ #Defines a specific color that is treated as transparent (example value: "FF00FF" for magenta).
453
+ attr_reader :trans
454
+
455
+ #Width of image
456
+ attr_reader :width
457
+
458
+ #Height of image
459
+ attr_reader :height
460
+
461
+ def initialize(xml)
462
+ doc = xml.elements['image']
463
+ @source = doc.attributes['source']
464
+ @trans = doc.attributes['trans']
465
+ @width = doc.attributes['width'].to_i
466
+ @height = doc.attributes['height'].to_i
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end
472
+
473
+
474
+ if $0 == __FILE__
475
+ RETMX.load('test.tmx')
476
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: retmx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jovany Leandro G.C
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-10 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: info@manadalibre.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/retmx.rb
21
+ homepage: https://www.manadalibre.org/desarrollos/doku.php?id=videojuegos:librerias:retmx
22
+ licenses: []
23
+ post_install_message:
24
+ rdoc_options: []
25
+ require_paths:
26
+ - lib
27
+ required_ruby_version: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 1.8.11
42
+ signing_key:
43
+ specification_version: 3
44
+ summary: Libray for read TMX (mapeditor.org)
45
+ test_files: []