Starting out with scripting

Hi

Just wondering how I get started on scripting. Need to write a script that replaces 1 tileset with another then exports the lot. Have no experience with Javascript so wondering if there were any simple templates I could follow etc. I’ve had a look at recommended Tiled scripts but having trouble following what does what.

Thanks

Sounds like you need an Action that you’d trigger from a menu or shortcut.

You can create an Action and put it in a menu like this:

let myAction = tiled.registerAction("MyActionName", function(action) {
	//do your thing
});
myAction.text = "My Action"; //display name for the action

//Add it to the Tileset menu. If you want it somewhere else, you can choose a different menu.
//If you type tiled.menus into the Tiled console, you can see a full list of modifiable menus.
tiled.extendMenu("Tileset", [
	{ action: "MyActionName", before: "TilesetProperties" }
]);

The above boilerplate is how I start most of my scripts.

For your actual action code (the “do your thing” bit), you’ll need to get the current tileset and make sure it’s a tileset like this:

let oldTileset = tiled.activeAsset;
if(!oldTileset.isTileset) return; //do nothing if the current document isn't a tileset

Then, you’ll need to get the replacement tileset. You can hard-code this, or prompt the user for the file using a Dialog.
However you get the path for the replacement tileset, you can open it with tiled.open:

let newTileset = tiled.open(newTilesetPath);
if(!newTileset.isTileset) {
	tiled.warn("The replacement tileset file is not a Tileset!");
	return;
}

Then, you can iterate the maps and perform the replacement using TileMap.replaceTileset:

let assets = tiled.openAssets; //Using open maps here, but you can populate assets by other means too, e.g. by making a list of all maps in the Project

let exportFormat = tiled.mapFormat("json"); //The export format we'll use. Choose whichever one you want.
//You can get a full list of format shortnames for this function by typing tiled.mapFormats into the Tiled console

for(asset of assets) {
	if(!asset.isTileMap) continue;
	asset.replaceTileset(oldTileset, newTileset);

	//Export the map:
	//First, figure out the filename we'll use for the export. Let's go with the map's current filename, with the extension changed:
	//In your code, I imagine you'll want to change the directory or something as well. Check out the FileInfo docs for some useful helper functions.
	let filename = map.fileName;
	//Get the base name (full path, minus extension)
	filename = FileInfo.baseName(filename);
	filename += ".tmj"; //Add the desired format's extension. Since we're using JSON above, use the JSON map's extension here.
	//You may also  want to handle the case that map.fileName is empty, which will be the case if the map has not been saved.

	//Finally, do the actual export using this filename:
	exportFormat.write(map, filename);
	
	//Optional: Undo the tileset replacement, if only the export is meant to have the replacement done.
	asset.undo();
}

In this example, I used the current asset as the tileset to replace, but perhaps you’ll want to do something else, depending on your desired workflow. Maybe you’ll hard-code a full list of tilesets and their replacements, for example, or use custom properties. In this case, both oldTileset and newTileset would probably be opened via tiled.open, or obtained from each map’s list of tilesets.

It’s also possible to make this not a manually-triggered action, but a function that runs whenever a map is about to be saved, by listening for the tiled.assetAboutToBeSaved function. In this case, you wouldn’t iterate all the maps, but would just do this work on the given map.

I doubt this example will do exactly what you need, but hopefully all this commentary will help you figure out how to use the API for your task.

1 Like

Wow that’s so helpful of you. I’m getting stuck in now, thanks so much!

So I’m having a bit of trouble.

I’m hard coding it to always replace a certain tileset with another. The project has 2 tilesets already loaded called gra.tsx and tar.tsx. I want to replace those two with loggra.tsx and logtar.tsx. When I run the script it loads all of the tilesets again and doesn’t replace anything. Here’s what I have so far:

let myAction = tiled.registerAction("MyActionName", function (action) {

	// Replace the tarmac tileset
	let oldTileset = tiled.open('/tilesets/tar.tsx');
	let newTileset = tiled.open('/tilesets/LogicalTilesets/logtar.tsx');
	map.replaceTileset(oldTileset, newTileset);

});
myAction.text = "My Action"; //display name for the action

//Add it to the Tileset menu. If you want it somewhere else, you can choose a different menu.
//If you type tiled.menus into the Tiled console, you can see a full list of modifiable menus.
tiled.extendMenu("File", [
	{ action: "MyActionName", before: "TilesetProperties" }
]);

You’re referring to map, but you’re not assigning map anywhere. Are you sure you’re not getting an undefined error?

I don’t seem to get any error. I guess I meant map to mean the currently open tmx file…

The error would show up in the console and in the issues panel.

You can get the currently open document with let map = tiled.activeAsset;, and you can check whether it’s a map with map.isTileMap.

Also, your paths for tiled.open need to be full paths, your partial paths will fail. If you want them to be relative to the current map’s location, you’ll need to build those paths based on the map’s fileName. You can use the FileInfo API to get the map’s directory, and then append your relative paths to it.

Yeah I took out the full path not to complicate things.

It worked after referencing the map, really cool. I’m now looking to export to PNG format. Is there a way to export certain tilesets or layers and exclude all others?

You can hide the unwanted layers and remove tiles from unwanted tilesets prior to the export, and then undo after the export.

Ah OK thanks, you can’t have it all :wink:

Lastly, it doesn’t seem to export to the directory, is this the correct syntax?

map.toImage('..../Assets/tiled/logical.png');

No, please look at the documentation. toImage takes an optional size, and returns an Image. It’s up to you to then save that image to a file in whatever format you want using image.save().

1 Like

Thanks for all the help and sorry for the silly questions. I’ll study the documentation tomorrow when I’m a bit fresher.