# Starting out with scripting

**URL:** <https://discourse.mapeditor.org/t/starting-out-with-scripting/6128>\
**Category:** Questions\
**Created:** [April 19, 2023, 6:04pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128 "2023-04-19T18:04:54Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 19, 2023, 6:04pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/1 "2023-04-19T18:04:54Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![eishiya](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/eishiya/32/2534_2.png) [@eishiya](https://discourse.mapeditor.org/u/eishiya)\
**Post date:** [April 19, 2023, 6:47pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/2 "2023-04-19T18:47:13Z")

</div>

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:

```js
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:

```js
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](https://www.mapeditor.org/docs/scripting/classes/Dialog.html).  
However you get the path for the replacement tileset, you can open it with `tiled.open`:

```js
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](https://www.mapeditor.org/docs/scripting/classes/TileMap.html#replaceTileset):

```js
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.

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 19, 2023, 7:54pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/3 "2023-04-19T19:54:53Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 24, 2023, 8:14pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/4 "2023-04-24T20:14:27Z")

</div>

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:

```auto
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" }
]);

```

---

<div class="post-metadata">

**Author:** ![eishiya](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/eishiya/32/2534_2.png) [@eishiya](https://discourse.mapeditor.org/u/eishiya)\
**Post date:** [April 24, 2023, 8:16pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/5 "2023-04-24T20:16:18Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 24, 2023, 8:29pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/6 "2023-04-24T20:29:06Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![eishiya](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/eishiya/32/2534_2.png) [@eishiya](https://discourse.mapeditor.org/u/eishiya)\
**Post date:** [April 24, 2023, 8:32pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/7 "2023-04-24T20:32:58Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 24, 2023, 9:44pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/8 "2023-04-24T21:44:06Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![eishiya](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/eishiya/32/2534_2.png) [@eishiya](https://discourse.mapeditor.org/u/eishiya)\
**Post date:** [April 24, 2023, 9:46pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/9 "2023-04-24T21:46:13Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 24, 2023, 9:51pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/10 "2023-04-24T21:51:21Z")

</div>

Ah OK thanks, you can’t have it all 😉

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

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

```

---

<div class="post-metadata">

**Author:** ![eishiya](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/eishiya/32/2534_2.png) [@eishiya](https://discourse.mapeditor.org/u/eishiya)\
**Post date:** [April 24, 2023, 9:53pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/11 "2023-04-24T21:53:39Z")

</div>

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()`.

---

<div class="post-metadata">

**Author:** ![SitKneesweak](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.mapeditor.org/sitkneesweak/32/4091_2.png) [@SitKneesweak](https://discourse.mapeditor.org/u/SitKneesweak)\
**Post date:** [April 24, 2023, 10:24pm UTC](https://discourse.mapeditor.org/t/starting-out-with-scripting/6128/12 "2023-04-24T22:24:04Z")

</div>

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