diff --git a/NEWS.md b/NEWS.md
index 7b9f2bc72a..87b0c59a28 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -27,6 +27,7 @@
* Scripting: Added TileMap.chunkSize and TileMap.compressionLevel properties
* AutoMapping: Don't match rules based on empty input indexes
* AutoMapping: Optimized reloading of rule maps and load rule maps on-demand
+* tBIN plugin: Added support for the tIDE XML format (by Casey Warrington, #4308)
* Windows: Fixed issue with opening Tile Animation Editor (#4223)
* macOS: Fixed crash when JS code is JIT-compiled (#4218)
* Workaround tileset view layout regression in Qt 6.9
diff --git a/docs/manual/export-tbin.rst b/docs/manual/export-tbin.rst
index fb3d1546ad..89c8bd19a0 100644
--- a/docs/manual/export-tbin.rst
+++ b/docs/manual/export-tbin.rst
@@ -1,25 +1,32 @@
.. raw:: html
-
Since Tiled 1.1
+ tBIN since Tiled 1.1
+ tIDE new in Tiled 1.12
-tBIN
-----
+tBIN and tIDE
+-------------
-The tBIN map format is a binary format used by the `tIDE Tile Map Editor`_.
+The tBIN map format is a binary format used by the `tIDE Tile Map Editor`_,
+while the tIDE map format is an XML-based format used by it as well.
tIDE was used by `Stardew Valley`_, a successful game that spawned many
`community mods `__.
Tiled ships with a plugin that enables direct editing of Stardew Valley
-maps (and any other maps using the tBIN format). This plugin needs to be
-enabled in *Edit > Preferences > Plugins*. It is not enabled by default
+maps (and any other maps using the tBIN or tIDE formats). This plugin needs to
+be enabled in *Edit > Preferences > Plugins*. It is not enabled by default
because it won't store everything (most notably it doesn't support
object layers in general, nor external tilesets), so you need to know
what you are doing.
.. note::
- The tBIN format supports setting custom properties on the tiles of a
- tile layer. Since Tiled does not support this directly, "TileData"
+ There is only one plugin, called `tbin`. Despite its name, it enables
+ support for both tBIN and tIDE maps.
+
+.. note::
+
+ The tBIN and tIDE formats support setting custom properties on the tiles of
+ a tile layer. Since Tiled does not support this directly, "TileData"
objects are created that match the location of the tile, on which
such properties are then stored.
diff --git a/org.mapeditor.Tiled.appdata.xml b/org.mapeditor.Tiled.appdata.xml
index 3182c48487..76392ba358 100644
--- a/org.mapeditor.Tiled.appdata.xml
+++ b/org.mapeditor.Tiled.appdata.xml
@@ -379,10 +379,12 @@
https://www.mapeditor.org/img/appdata/screenshot_objects.png
+ Annotate your map with objects
https://www.mapeditor.org/img/appdata/screenshot_terrain.png
+ Automate placement of terrain transitions
diff --git a/src/plugins/tbin/tbin.qbs b/src/plugins/tbin/tbin.qbs
index 41a3a42b77..38d7732f71 100644
--- a/src/plugins/tbin/tbin.qbs
+++ b/src/plugins/tbin/tbin.qbs
@@ -5,6 +5,10 @@ TiledPlugin {
"tbin_global.h",
"tbinplugin.cpp",
"tbinplugin.h",
+ "tbinmapformat.cpp",
+ "tbinmapformat.h",
+ "tidemapformat.cpp",
+ "tidemapformat.h",
"plugin.json",
"tbin/Layer.hpp",
"tbin/Map.cpp",
@@ -12,6 +16,6 @@ TiledPlugin {
"tbin/PropertyValue.hpp",
"tbin/Tile.hpp",
"tbin/TileSheet.hpp",
- "tbin/FakeSfml.hpp",
+ "tbin/Vector2i.hpp",
]
}
diff --git a/src/plugins/tbin/tbin/FakeSfml.hpp b/src/plugins/tbin/tbin/FakeSfml.hpp
deleted file mode 100644
index 542b41b0cd..0000000000
--- a/src/plugins/tbin/tbin/FakeSfml.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef FAKESFML_HPP
-#define FAKESFML_HPP
-
-namespace sf
-{
- using Int32 = int;
- using Uint8 = unsigned char;
- struct Vector2i
- {
- Int32 x;
- Int32 y;
-
- Vector2i() : x(0),y(0) {}
- Vector2i( Int32 x, Int32 y ) : x(x), y(y) {}
- };
-}
-
-#endif // FAKESFML_HPP
diff --git a/src/plugins/tbin/tbin/Layer.hpp b/src/plugins/tbin/tbin/Layer.hpp
index fc5945e7f8..b6509bd3b3 100644
--- a/src/plugins/tbin/tbin/Layer.hpp
+++ b/src/plugins/tbin/tbin/Layer.hpp
@@ -1,6 +1,6 @@
/*
* TBIN
- * Copyright 2017, Chase Warrington
+ * Copyright 2017, Casey Warrington
*
* MIT License
*
@@ -30,7 +30,7 @@
#include
#include
-#include "FakeSfml.hpp"
+#include "Vector2i.hpp"
#include "PropertyValue.hpp"
#include "Tile.hpp"
@@ -42,8 +42,8 @@ namespace tbin
std::string id;
bool visible;
std::string desc;
- sf::Vector2i layerSize;
- sf::Vector2i tileSize;
+ tbin::Vector2i layerSize;
+ tbin::Vector2i tileSize;
Properties props;
std::vector< Tile > tiles;
};
diff --git a/src/plugins/tbin/tbin/Map.cpp b/src/plugins/tbin/tbin/Map.cpp
index b293c61422..171f466791 100644
--- a/src/plugins/tbin/tbin/Map.cpp
+++ b/src/plugins/tbin/tbin/Map.cpp
@@ -1,6 +1,6 @@
/*
* TBIN
- * Copyright 2017, Chase Warrington
+ * Copyright 2017, Casey Warrington
*
* MIT License
*
@@ -29,7 +29,7 @@
#include
#include
-#include "FakeSfml.hpp"
+#include "Vector2i.hpp"
namespace tbin
{
@@ -42,17 +42,17 @@ namespace tbin
}
template<>
- sf::Vector2i read< sf::Vector2i >( std::istream& in )
+ tbin::Vector2i read< tbin::Vector2i >( std::istream& in )
{
- sf::Int32 x = read< sf::Int32 >( in );
- sf::Int32 y = read< sf::Int32 >( in );
- return sf::Vector2i( x, y );
+ int32_t x = read< int32_t >( in );
+ int32_t y = read< int32_t >( in );
+ return tbin::Vector2i( x, y );
}
template<>
std::string read< std::string >( std::istream& in )
{
- auto len = read< sf::Int32 >( in );
+ auto len = read< int32_t >( in );
std::string str( len, 0 );
in.read( &str[ 0 ], len );
return str;
@@ -65,16 +65,16 @@ namespace tbin
}
template<>
- void write< sf::Vector2i >( std::ostream& out, const sf::Vector2i& vec )
+ void write< tbin::Vector2i >( std::ostream& out, const tbin::Vector2i& vec )
{
- write< sf::Int32 >( out, vec.x );
- write< sf::Int32 >( out, vec.y );
+ write< int32_t >( out, vec.x );
+ write< int32_t >( out, vec.y );
}
template<>
void write< std::string >( std::ostream& out, const std::string& str )
{
- write< sf::Int32 >( out, str.length() );
+ write< int32_t >( out, str.length() );
out.write( &str[ 0 ], str.length() );
}
@@ -82,18 +82,18 @@ namespace tbin
{
Properties ret;
- int count = read< sf::Int32 >( in );
+ int count = read< int32_t >( in );
for ( int i = 0; i < count; ++i )
{
std::string key;
PropertyValue value;
key = read< std::string >( in );
- value.type = static_cast< PropertyValue::Type >( read< sf::Uint8 >( in ) );
+ value.type = static_cast< PropertyValue::Type >( read< uint8_t >( in ) );
switch ( value.type )
{
- case PropertyValue::Bool: value.data.b = read< sf::Uint8 >( in ) > 0; break;
- case PropertyValue::Integer: value.data.i = read< sf::Int32 >( in ); break;
+ case PropertyValue::Bool: value.data.b = read< uint8_t >( in ) > 0; break;
+ case PropertyValue::Integer: value.data.i = read< int32_t >( in ); break;
case PropertyValue::Float: value.data.f = read< float >( in ); break;
case PropertyValue::String: value.dataStr = read< std::string >( in ); break;
default: throw std::invalid_argument( QT_TRANSLATE_NOOP("TbinMapFormat", "Bad property type") );
@@ -108,14 +108,14 @@ namespace tbin
void writeProperties( std::ostream& out, const Properties& props )
{
- write< sf::Int32 >( out, props.size() );
+ write< int32_t >( out, props.size() );
for ( const auto& prop : props )
{
write( out, prop.first );
- write< sf::Uint8 >( out, prop.second.type );
+ write< uint8_t >( out, prop.second.type );
switch ( prop.second.type )
{
- case PropertyValue::Bool: write< sf::Uint8 >( out, prop.second.data.b ? 1 : 0 ); break;
+ case PropertyValue::Bool: write< uint8_t >( out, prop.second.data.b ? 1 : 0 ); break;
case PropertyValue::Integer: write( out, prop.second.data.i ); break;
case PropertyValue::Float: write( out, prop.second.data.f ); break;
case PropertyValue::String: write( out, prop.second.dataStr ); break;
@@ -130,10 +130,10 @@ namespace tbin
ret.id = read< std::string >( in );
ret.desc = read< std::string >( in );
ret.image = read< std::string >( in );
- ret.sheetSize = read< sf::Vector2i >( in );
- ret.tileSize = read< sf::Vector2i >( in );
- ret.margin = read< sf::Vector2i >( in );
- ret.spacing = read< sf::Vector2i >( in );
+ ret.sheetSize = read< tbin::Vector2i >( in );
+ ret.tileSize = read< tbin::Vector2i >( in );
+ ret.margin = read< tbin::Vector2i >( in );
+ ret.spacing = read< tbin::Vector2i >( in );
ret.props = readProperties( in );
return ret;
}
@@ -154,8 +154,8 @@ namespace tbin
{
Tile ret;
ret.tilesheet = currTilesheet;
- ret.staticData.tileIndex = read< sf::Int32 >( in );
- ret.staticData.blendMode = read< sf::Uint8 >( in );
+ ret.staticData.tileIndex = read< int32_t >( in );
+ ret.staticData.blendMode = read< uint8_t >( in );
ret.props = readProperties( in );
return ret;
}
@@ -170,9 +170,9 @@ namespace tbin
Tile readAnimatedTile( std::istream& in )
{
Tile ret;
- ret.animatedData.frameInterval = read< sf::Int32 >( in );
+ ret.animatedData.frameInterval = read< int32_t >( in );
- int frameCount = read< sf::Int32 >( in );
+ int frameCount = read< int32_t >( in );
ret.animatedData.frames.reserve( frameCount );
std::string currTilesheet;
for ( int i = 0; i < frameCount; )
@@ -200,19 +200,19 @@ namespace tbin
void writeAnimatedTile( std::ostream& out, const Tile& tile )
{
write( out, tile.animatedData.frameInterval );
- write< sf::Int32 >( out, tile.animatedData.frames.size() );
+ write< int32_t >( out, tile.animatedData.frames.size() );
std::string currTilesheet;
for ( const Tile& frame : tile.animatedData.frames )
{
if ( frame.tilesheet != currTilesheet )
{
- write< sf::Uint8 >( out, 'T' );
+ write< uint8_t >( out, 'T' );
write( out, frame.tilesheet );
currTilesheet = frame.tilesheet;
}
- write< sf::Uint8 >( out, 'S' );
+ write< uint8_t >( out, 'S' );
writeStaticTile( out, frame );
}
@@ -223,10 +223,10 @@ namespace tbin
{
Layer ret;
ret.id = read< std::string >( in );
- ret.visible = read< sf::Uint8 >( in ) > 0;
+ ret.visible = read< uint8_t >( in ) > 0;
ret.desc = read< std::string >( in );
- ret.layerSize = read< sf::Vector2i >( in );
- ret.tileSize = read< sf::Vector2i >( in );
+ ret.layerSize = read< tbin::Vector2i >( in );
+ ret.tileSize = read< tbin::Vector2i >( in );
ret.props = readProperties( in );
Tile nullTile;
@@ -239,11 +239,11 @@ namespace tbin
int ix = 0;
while ( ix < ret.layerSize.x )
{
- sf::Uint8 c = read< sf::Uint8 >( in );
+ uint8_t c = read< uint8_t >( in );
switch ( c )
{
case 'N':
- ix += read< sf::Int32 >( in );
+ ix += read< int32_t >( in );
break;
case 'S':
ret.tiles[ ix + iy * ret.layerSize.x ] = readStaticTile( in, currTilesheet );
@@ -268,7 +268,7 @@ namespace tbin
void writeLayer( std::ostream& out, const Layer& layer )
{
write( out, layer.id );
- write< sf::Uint8 >( out, layer.visible ? 1 : 0 );
+ write< uint8_t >( out, layer.visible ? 1 : 0 );
write( out, layer.desc );
write( out, layer.layerSize );
write( out, layer.tileSize );
@@ -277,7 +277,7 @@ namespace tbin
std::string currTilesheet = "";
for ( int iy = 0; iy < layer.layerSize.y; ++iy )
{
- sf::Int32 nulls = 0;
+ int32_t nulls = 0;
for ( int ix = 0; ix < layer.layerSize.x; ++ix )
{
const Tile& tile = layer.tiles[ ix + iy * layer.layerSize.x ];
@@ -290,33 +290,33 @@ namespace tbin
if ( nulls > 0 )
{
- write< sf::Uint8 >( out, 'N' );
+ write< uint8_t >( out, 'N' );
write( out, nulls );
nulls = 0;
}
if ( tile.tilesheet != currTilesheet )
{
- write< sf::Uint8 >( out, 'T' );
+ write< uint8_t >( out, 'T' );
write( out, tile.tilesheet );
currTilesheet = tile.tilesheet;
}
if ( tile.animatedData.frames.size() == 0 )
{
- write< sf::Uint8 >( out, 'S' );
+ write< uint8_t >( out, 'S' );
writeStaticTile( out, tile );
}
else
{
- write< sf::Uint8 >( out, 'A' );
+ write< uint8_t >( out, 'A' );
writeAnimatedTile( out, tile );
}
}
if ( nulls > 0 )
{
- write< sf::Uint8 >( out, 'N' );
+ write< uint8_t >( out, 'N' );
write( out, nulls );
}
}
@@ -351,14 +351,14 @@ namespace tbin
Properties props = readProperties( in );
std::vector< TileSheet > tilesheets;
- int tilesheetCount = read< sf::Int32 >( in );
+ int tilesheetCount = read< int32_t >( in );
for ( int i = 0; i < tilesheetCount; ++i )
{
tilesheets.push_back( readTilesheet( in ) );
}
std::vector< Layer > layers;
- int layerCount = read< sf::Int32 >( in );
+ int layerCount = read< int32_t >( in );
for ( int i = 0; i < layerCount; ++i )
{
layers.push_back( readLayer( in ) );
@@ -394,11 +394,11 @@ namespace tbin
write( out, desc );
writeProperties( out, props );
- write< sf::Int32 >( out, tilesheets.size() );
+ write< int32_t >( out, tilesheets.size() );
for ( const TileSheet& ts : tilesheets )
writeTilesheet( out, ts );
- write< sf::Int32 >( out, layers.size() );
+ write< int32_t >( out, layers.size() );
for ( const Layer& layer : layers )
writeLayer( out, layer );
diff --git a/src/plugins/tbin/tbin/Map.hpp b/src/plugins/tbin/tbin/Map.hpp
index 1ac119411c..3455390c9a 100644
--- a/src/plugins/tbin/tbin/Map.hpp
+++ b/src/plugins/tbin/tbin/Map.hpp
@@ -1,6 +1,6 @@
/*
* TBIN
- * Copyright 2017, Chase Warrington
+ * Copyright 2017, Casey Warrington
*
* MIT License
*
diff --git a/src/plugins/tbin/tbin/PropertyValue.hpp b/src/plugins/tbin/tbin/PropertyValue.hpp
index 9f1fcb3b14..9b07dab9db 100644
--- a/src/plugins/tbin/tbin/PropertyValue.hpp
+++ b/src/plugins/tbin/tbin/PropertyValue.hpp
@@ -1,6 +1,6 @@
/*
* TBIN
- * Copyright 2017, Chase Warrington
+ * Copyright 2017, Casey Warrington
*
* MIT License
*
@@ -26,12 +26,11 @@
#ifndef TBIN_PROPERTYVALUE_HPP
#define TBIN_PROPERTYVALUE_HPP
+#include
#include