tiled
tiled copied to clipboard
Add scripting API for the project
Scripts should be able to access the files in the project and the project properties.
Split off from #1665.
So i could do with this feature for my use case.
I want to be able to open every map in a project and run a set of Actions which automate a whole bunch of stuff. Mostly auto mapping.
For the current work around im having to hardcode the map paths into the triggering action.
/// <reference types="@mapeditor/tiled-api" />
const automapAllMaps = tiled.registerAction("AutomapAllMaps", function(action) {
tiled.trigger("CloseAll");
processMap("X:\\Source\\MangoDungeon\\Mango\\Assets\\Tiled\\Maps\\Test.tmx");
});
function processMap(path:string) {
tiled.open(path);
tiled.trigger("AutomapExtra");
tiled.trigger("Close");
}
automapAllMaps.text = "Automap All Maps";
automapAllMaps.checkable = false;
tiled.extendMenu("Edit", [
{ action: "AutomapAllMaps" }
]);`
For the current work around im having to hardcode the map paths into the triggering action.
Just one thing to note, there is API available to access a list of files in a folder, so you may only need to hardcode the project path instead of the paths to each map.
Since you're applying AutoMapping to all files in your project, I hope you noticed the Tiled 1.9 Alpha release that greatly improves the speed of AutoMapping. Any feedback regarding AutoMapping is very welcome.
from #3527 - I also have a use case for this. I would like to get the path to the current project. I'm working on an extension to edit maps for Cataclysm: Dark Days Ahead. It involves importing tilesets and mapping the game's IDs to each sprite and preparing maps with data from the game files.
I generate tilesets, maps, and supplemental files to the project folder which I prompt from the user and then store in a config file to autofill next time the command is run for slight convenience.
I think I've asked for this elsewhere, but I think it makes sense being included here: the Project would be a very convenient place for scripts to store script configuration, which at present has to be either set every time the user restarts Tiled, hard-coded by the user, or stored in config files that clutter the user's directories. To this end, I'd like to be able to set Custom Properties or something like them on the project, both via scripts and via the GUI.
Edit: Oh, looks like there's an issue for that already: #2903
After #3622 we have access to the basic properties of the project, including the list of folders, which is usually more relevant than the project file path. However, I don't think we can close this issue, since there are at least two shortcomings:
- There is still no convenient way to access the files in a project, due to:
- Recursive search for files needs to be implemented manually.
- We can't easily get all files of a certain type, for example all files that can likely be read as map files.
- The project also stores the custom types, which needs to be accessible as well (see also #3419, which maybe could be considered to cover this aspect).
Regarding file search, the just added tiled.project
could be entry point of convenience functions like project.maps
, project.tilesets
and project.worlds
(or maybe project.assets(Asset.TileMap)
would be better?). I also wonder whether these functions should return file names, or actually loaded assets (which could be convenient, but would in some cases result in performance issues). A more generic file search like, project.files("*.tmx")
could also be useful.
While testing #3622, I ended up writing that big recursive search for map files:
function collectMaps(folder) {
//First, get all the files in this folder
let files = File.directoryEntries(folder, File.Files | File.Readable | File.NoDotAndDotDot);
for(file of files) {
let path = folder+"/"+file;
let format = tiled.mapFormatForFile(path);
if(format) {
let map = getOpenMap(path);
if(map) //If it's already open, use that instance
maps.push(map);
else //save the path to open later
maps.push(path);
} //else there's no map format that can read this file, it's not a Tiled map, skip it.
}
//Then, look at any subfolders:
files = File.directoryEntries(folder, File.Dirs | File.Readable | File.NoDotAndDotDot);
for(file of files) {
collectMaps(folder+"/"+file);
}
}
//Find all the maps in each project directory:
if(tiled.project) {
let folders = tiled.project.folders;
for(folder of folders)
collectMaps(folder);
}
As you can see, I ended up saving a list of paths, because opening all those maps at once was terrible for performance xP Opening them one by one made for a much more pleasant experience. They need to be the actual map documents in my case (i.e. tiled.open, not MapFormat.read), so there was no avoiding the overhead of that. I think returning a list of paths gives scripts the most options - they can WhateverFormat.read or tiled.open all of them, they can read them one at a time, read them in pairs for comparisons, whatever, all with the minimal memory impact possible for their given task.
It might also be nice if projects supported getting a list of images within the project, as that's another file type commonly used in Tiled, even if Tiled doesn't usually create them. Tiled knows best what it can open as an image xP However, I do also quite like the idea of project.assets(Asset.TileMap)
, and that wouldn't make sense for images.
Whatever the method for getting files of a particular type, I think it would be improved by the option to specify a path to look within, e.g. a script might only want the maps in /automap or in /jungle, e.g. project.assets(Asset.TileMap, '/jungle')
. If possible, both absolute paths (e.g. from project.folders
) and relative paths (treated as relative to the project root) should be allowed, and any attempts to search outside the project should return nothing.
A generic file search with filename wildcards would be pretty nice to have too. Perhaps this could be combined with all of the above, e.g. "find all the Maps in /automap/ with the name filter jungle*": project.files(Asset.TileMap, '/automap', 'jungle*')
. All those parameters would get in the way when you just want the filter OR the path though.
I would agree that the map/tileset/etc. list functions should just return the paths, especially because if you did want to load them all, you could just loop through the paths yourself without too much code.
All those parameters would get in the way when you just want the filter OR the path though.
Maybe passing ''
, null, or undefined as the search directory would default it to the project root
All those parameters would get in the way when you just want the filter OR the path though.
Maybe passing
''
, null, or undefined as the search directory would default it to the project root
Yeah, it'd have to be done that way if we have a single three-parameter files
method xP It's just not the prettiest, is all.