Skip to content

Commit

Permalink
fix: multiple external tilesets (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
lfraker authored Feb 1, 2022
1 parent 7d34b15 commit d4ac573
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 10 deletions.
8 changes: 6 additions & 2 deletions lib/src/tile_map_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ class TileMapParser {
return TiledMap.parse(parser);
}

static TiledMap parseTmx(String xml, {TsxProvider? tsx}) {
/// Parses the provided map xml.
///
/// Accepts an optional list of external TsxProviders for external tilesets
/// referenced in the map file.
static TiledMap parseTmx(String xml, {List<TsxProvider>? tsxList}) {
final xmlElement = XmlDocument.parse(xml).rootElement;
if (xmlElement.name.local != 'map') {
throw 'XML is not in TMX format';
}
final parser = XmlParser(xmlElement);
return TiledMap.parse(parser, tsx: tsx);
return TiledMap.parse(parser, tsxList: tsxList);
}
}
17 changes: 15 additions & 2 deletions lib/src/tiled_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class TiledMap {
);
}

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

final tilesets = parser.getChildrenAs(
'tileset',
(e) => Tileset.parse(e, tsx: tsx),
(tilesetData) {
final tilesetSource = tilesetData.getStringOrNull('source');
if (tilesetSource == null || tsxList == null) {
return Tileset.parse(tilesetData);
}
final matchingTsx = tsxList.where(
(tsx) => tsx.filename == tilesetSource,
);

return Tileset.parse(
tilesetData,
tsx: matchingTsx.isNotEmpty ? matchingTsx.first : null,
);
},
);
final layers = Layer.parseLayers(parser);
final properties = parser.getProperties();
Expand Down
4 changes: 3 additions & 1 deletion lib/src/tileset/tileset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ class Tileset {

void _checkIfExtenalTsx(String? source, TsxProvider? tsx) {
if (tsx != null && source != null) {
final tileset = Tileset.parse(tsx.getSource(source));
final tileset = Tileset.parse(
tsx.getChachedSource() ?? tsx.getSource(source),
);
// Copy attributes if not null
backgroundColor = tileset.backgroundColor ?? backgroundColor;
columns = tileset.columns ?? columns;
Expand Down
10 changes: 10 additions & 0 deletions lib/src/tsx_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
part of tiled;

/// abstract class to be implemented for an external tileset data provider.
abstract class TsxProvider {
/// Unique filename for the tileset to be loaded. This should match the
/// 'source' property in the map.tmx file.
String get filename;

/// Retrieves the external tileset data given the tileset filename.
Parser getSource(String filename);

/// Used when provider implementations cache the data. Returns the cached
/// data for the exernal tileset.
Parser? getChachedSource();
}
14 changes: 14 additions & 0 deletions test/fixtures/external_tileset_1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="2018.11.29" name="level1" tilewidth="16" tileheight="16" tilecount="136" columns="17">
<image source="../images/map-level1.png" width="272" height="128"/>
<tile id="64">
<properties>
<property name="type" value="sky"/>
</properties>
</tile>
<tile id="65">
<properties>
<property name="type" value="xpto"/>
</properties>
</tile>
</tileset>
4 changes: 4 additions & 0 deletions test/fixtures/external_tileset_2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="2018.11.29" name="level2" tilewidth="16" tileheight="16" tilecount="288" columns="24">
<image source="../images/map-level2.png" width="384" height="192" />
</tileset>
30 changes: 30 additions & 0 deletions test/fixtures/map_with_multiple_tilesets.tmx

Large diffs are not rendered by default.

108 changes: 103 additions & 5 deletions test/parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ void main() {
group('Parser.parse fills Map with tileset & different img configs', () {
setUp(() {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
});
});

Expand Down Expand Up @@ -222,7 +225,10 @@ void main() {
group('Parser.parse with tsx provider', () {
test('it loads external tsx', () {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
expect(
map.tilesetByName('external').image!.source,
equals('level1.png'),
Expand All @@ -234,18 +240,110 @@ void main() {
group('Parser.parse with multiple layers', () {
test('it has 2 layers', () {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
expect(map.layers.length, equals(2));
});
});
});

group('Map Parses Multiple Tilesets', () {
late TiledMap map;
setUp(() {
return File('./test/fixtures/map_with_multiple_tilesets.tmx')
.readAsString()
.then((xml) {
final tilemapXml = XmlDocument.parse(xml).rootElement;
final tsxSourcePaths = tilemapXml.children
.whereType<XmlElement>()
.where((element) => element.name.local == 'tileset')
.map((tsx) => tsx.getAttribute('source'));

final tsxProviders = tsxSourcePaths
.where((key) => key != null)
.map((key) => CustomTsxProvider.parse(key!));

map = TileMapParser.parseTmx(
xml,
tsxList: tsxProviders.isEmpty ? null : tsxProviders.toList(),
);
return;
});
});
test(
'correct number of tilests',
() => expect(
map.tilesets.length == 3,
true,
),
);

test('tilesets firstgid correct', () {
expect(
map.tilesets.first.firstGid == 1,
true,
);

expect(
map.tilesets.last.firstGid == 273,
true,
);
});
test('first tileset details correct', () {
expect(
map.tilesets.first.name == 'level1',
true,
);

expect(
map.tilesets.first.tileCount == 136,
true,
);
});
test('embedded tileset details correct', () {
expect(
map.tilesets[1].name == 'level_embed',
true,
);
});
test('third tileset details correct', () {
expect(
map.tilesets.last.name == 'level2',
true,
);

expect(
map.tilesets.last.tileCount == 288,
true,
);
});
});
}

class CustomTsxProvider extends TsxProvider {
final String _filename;
final String data;

CustomTsxProvider._(this.data, this._filename);

@override
String get filename => _filename;

@override
Parser getSource(String key) {
final xml = File('./test/fixtures/tileset.tsx').readAsStringSync();
final node = XmlDocument.parse(xml).rootElement;
final node = XmlDocument.parse(data).rootElement;
return XmlParser(node);
}

@override
Parser? getChachedSource() {
return getSource('');
}

static CustomTsxProvider parse(String filename) {
final xml = File('./test/fixtures/$filename').readAsStringSync();
return CustomTsxProvider._(xml, filename);
}
}

0 comments on commit d4ac573

Please sign in to comment.