Skip to content

Commit d4ac573

Browse files
authored
fix: multiple external tilesets (#37)
1 parent 7d34b15 commit d4ac573

8 files changed

+185
-10
lines changed

lib/src/tile_map_parser.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ class TileMapParser {
66
return TiledMap.parse(parser);
77
}
88

9-
static TiledMap parseTmx(String xml, {TsxProvider? tsx}) {
9+
/// Parses the provided map xml.
10+
///
11+
/// Accepts an optional list of external TsxProviders for external tilesets
12+
/// referenced in the map file.
13+
static TiledMap parseTmx(String xml, {List<TsxProvider>? tsxList}) {
1014
final xmlElement = XmlDocument.parse(xml).rootElement;
1115
if (xmlElement.name.local != 'map') {
1216
throw 'XML is not in TMX format';
1317
}
1418
final parser = XmlParser(xmlElement);
15-
return TiledMap.parse(parser, tsx: tsx);
19+
return TiledMap.parse(parser, tsxList: tsxList);
1620
}
1721
}

lib/src/tiled_map.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ class TiledMap {
196196
);
197197
}
198198

199-
static TiledMap parse(Parser parser, {TsxProvider? tsx}) {
199+
static TiledMap parse(Parser parser, {List<TsxProvider>? tsxList}) {
200200
final backgroundColor = parser.getStringOrNull('backgroundcolor');
201201
final compressionLevel = parser.getInt('compressionlevel', defaults: -1);
202202
final height = parser.getInt('height');
@@ -220,7 +220,20 @@ class TiledMap {
220220

221221
final tilesets = parser.getChildrenAs(
222222
'tileset',
223-
(e) => Tileset.parse(e, tsx: tsx),
223+
(tilesetData) {
224+
final tilesetSource = tilesetData.getStringOrNull('source');
225+
if (tilesetSource == null || tsxList == null) {
226+
return Tileset.parse(tilesetData);
227+
}
228+
final matchingTsx = tsxList.where(
229+
(tsx) => tsx.filename == tilesetSource,
230+
);
231+
232+
return Tileset.parse(
233+
tilesetData,
234+
tsx: matchingTsx.isNotEmpty ? matchingTsx.first : null,
235+
);
236+
},
224237
);
225238
final layers = Layer.parseLayers(parser);
226239
final properties = parser.getProperties();

lib/src/tileset/tileset.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ class Tileset {
168168

169169
void _checkIfExtenalTsx(String? source, TsxProvider? tsx) {
170170
if (tsx != null && source != null) {
171-
final tileset = Tileset.parse(tsx.getSource(source));
171+
final tileset = Tileset.parse(
172+
tsx.getChachedSource() ?? tsx.getSource(source),
173+
);
172174
// Copy attributes if not null
173175
backgroundColor = tileset.backgroundColor ?? backgroundColor;
174176
columns = tileset.columns ?? columns;

lib/src/tsx_provider.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
part of tiled;
22

3+
/// abstract class to be implemented for an external tileset data provider.
34
abstract class TsxProvider {
5+
/// Unique filename for the tileset to be loaded. This should match the
6+
/// 'source' property in the map.tmx file.
7+
String get filename;
8+
9+
/// Retrieves the external tileset data given the tileset filename.
410
Parser getSource(String filename);
11+
12+
/// Used when provider implementations cache the data. Returns the cached
13+
/// data for the exernal tileset.
14+
Parser? getChachedSource();
515
}

test/fixtures/external_tileset_1.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tileset version="1.2" tiledversion="2018.11.29" name="level1" tilewidth="16" tileheight="16" tilecount="136" columns="17">
3+
<image source="../images/map-level1.png" width="272" height="128"/>
4+
<tile id="64">
5+
<properties>
6+
<property name="type" value="sky"/>
7+
</properties>
8+
</tile>
9+
<tile id="65">
10+
<properties>
11+
<property name="type" value="xpto"/>
12+
</properties>
13+
</tile>
14+
</tileset>

test/fixtures/external_tileset_2.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tileset version="1.2" tiledversion="2018.11.29" name="level2" tilewidth="16" tileheight="16" tilecount="288" columns="24">
3+
<image source="../images/map-level2.png" width="384" height="192" />
4+
</tileset>

test/fixtures/map_with_multiple_tilesets.tmx

Lines changed: 30 additions & 0 deletions
Large diffs are not rendered by default.

test/parser_test.dart

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ void main() {
166166
group('Parser.parse fills Map with tileset & different img configs', () {
167167
setUp(() {
168168
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
169-
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
169+
map = TileMapParser.parseTmx(
170+
xml,
171+
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
172+
);
170173
});
171174
});
172175

@@ -222,7 +225,10 @@ void main() {
222225
group('Parser.parse with tsx provider', () {
223226
test('it loads external tsx', () {
224227
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
225-
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
228+
map = TileMapParser.parseTmx(
229+
xml,
230+
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
231+
);
226232
expect(
227233
map.tilesetByName('external').image!.source,
228234
equals('level1.png'),
@@ -234,18 +240,110 @@ void main() {
234240
group('Parser.parse with multiple layers', () {
235241
test('it has 2 layers', () {
236242
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
237-
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
243+
map = TileMapParser.parseTmx(
244+
xml,
245+
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
246+
);
238247
expect(map.layers.length, equals(2));
239248
});
240249
});
241250
});
251+
252+
group('Map Parses Multiple Tilesets', () {
253+
late TiledMap map;
254+
setUp(() {
255+
return File('./test/fixtures/map_with_multiple_tilesets.tmx')
256+
.readAsString()
257+
.then((xml) {
258+
final tilemapXml = XmlDocument.parse(xml).rootElement;
259+
final tsxSourcePaths = tilemapXml.children
260+
.whereType<XmlElement>()
261+
.where((element) => element.name.local == 'tileset')
262+
.map((tsx) => tsx.getAttribute('source'));
263+
264+
final tsxProviders = tsxSourcePaths
265+
.where((key) => key != null)
266+
.map((key) => CustomTsxProvider.parse(key!));
267+
268+
map = TileMapParser.parseTmx(
269+
xml,
270+
tsxList: tsxProviders.isEmpty ? null : tsxProviders.toList(),
271+
);
272+
return;
273+
});
274+
});
275+
test(
276+
'correct number of tilests',
277+
() => expect(
278+
map.tilesets.length == 3,
279+
true,
280+
),
281+
);
282+
283+
test('tilesets firstgid correct', () {
284+
expect(
285+
map.tilesets.first.firstGid == 1,
286+
true,
287+
);
288+
289+
expect(
290+
map.tilesets.last.firstGid == 273,
291+
true,
292+
);
293+
});
294+
test('first tileset details correct', () {
295+
expect(
296+
map.tilesets.first.name == 'level1',
297+
true,
298+
);
299+
300+
expect(
301+
map.tilesets.first.tileCount == 136,
302+
true,
303+
);
304+
});
305+
test('embedded tileset details correct', () {
306+
expect(
307+
map.tilesets[1].name == 'level_embed',
308+
true,
309+
);
310+
});
311+
test('third tileset details correct', () {
312+
expect(
313+
map.tilesets.last.name == 'level2',
314+
true,
315+
);
316+
317+
expect(
318+
map.tilesets.last.tileCount == 288,
319+
true,
320+
);
321+
});
322+
});
242323
}
243324

244325
class CustomTsxProvider extends TsxProvider {
326+
final String _filename;
327+
final String data;
328+
329+
CustomTsxProvider._(this.data, this._filename);
330+
331+
@override
332+
String get filename => _filename;
333+
245334
@override
246335
Parser getSource(String key) {
247-
final xml = File('./test/fixtures/tileset.tsx').readAsStringSync();
248-
final node = XmlDocument.parse(xml).rootElement;
336+
final node = XmlDocument.parse(data).rootElement;
249337
return XmlParser(node);
250338
}
339+
340+
@override
341+
Parser? getChachedSource() {
342+
return getSource('');
343+
}
344+
345+
static CustomTsxProvider parse(String filename) {
346+
final xml = File('./test/fixtures/$filename').readAsStringSync();
347+
return CustomTsxProvider._(xml, filename);
348+
}
251349
}

0 commit comments

Comments
 (0)