diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 480c912c48..4ef876766e 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -19,7 +19,7 @@ on: - '.travis.yml' env: - QBS_VERSION: 3.1.1 + QBS_VERSION: 3.2.0 CMAKE_VERSION: 3.31 SENTRY_VERSION: 0.12.8 SENTRY_ORG: mapeditor @@ -241,17 +241,19 @@ jobs: with: cmake-version: '${{ env.CMAKE_VERSION }}' + - name: Install Qbs + run: | + tarball=qbs-macos-$(uname -m)-${{ env.QBS_VERSION }}.tar.gz + curl -fLO "https://download.qt.io/official_releases/qbs/${{ env.QBS_VERSION }}/${tarball}" + mkdir -p "${HOME}/qbs" + tar xzf "${tarball}" -C "${HOME}/qbs" + dirname "$(find "${HOME}/qbs" -name qbs -type f | head -n1)" >> "$GITHUB_PATH" + - name: Setup Qbs run: | - brew update - brew install qbs - export PATH="$(brew --prefix qbs)/bin:$PATH" - echo "$(brew --prefix qbs)/bin" >> "$GITHUB_PATH" qbs --version qbs setup-toolchains --detect - qbs setup-qt --detect - qbs config profiles.qt-6-9-3.baseProfile xcode - qbs config defaultProfile qt-6-9-3 + qbs config defaultProfile xcode - name: Build Zstandard run: | @@ -385,7 +387,8 @@ jobs: - name: Install Qbs run: | - choco install -y qbs --version ${{ env.QBS_VERSION }} + curl -fLO https://download.qt.io/official_releases/qbs/${{ env.QBS_VERSION }}/qbs.${{ env.QBS_VERSION }}.nupkg + choco install -y qbs --version ${{ env.QBS_VERSION }} --source . - name: Setup Qbs run: | diff --git a/NEWS.md b/NEWS.md index e88b2ecd50..26eb7cd425 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ * Added 'Collapse All' action and 'Only Expand to Current' mode to Project view (with rhythmcache, #4346) * Added command variables %exportfile and %exportpath (#4476) +* Scripting: Added 'tiled.cell' function, 'cell.flags' property and 'TileLayerEdit.setCell' function (#4538) +* Scripting: Added MapObject.resolvedClassName() (by MatusGuy, #4529) ### Tiled 1.12.2 (27 May 2026) diff --git a/docs/scripting-doc/index.d.ts b/docs/scripting-doc/index.d.ts index b406141a35..ec96439f7d 100644 --- a/docs/scripting-doc/index.d.ts +++ b/docs/scripting-doc/index.d.ts @@ -1826,6 +1826,15 @@ declare class MapObject extends TiledObject { */ layer: ObjectGroup | null; + /** + * Returns the resolved class name of this object. + * This may be the class name of the object's template or the class name of its tile, + * in the case of the object not having an explicitly set class name. + * + * @since 1.13 + */ + resolvedClassName(): string; + /** * Map this object is part of (or `null` in case of a * standalone object). @@ -3589,6 +3598,17 @@ interface cell { */ empty: boolean; + /** + * The flags used for the tile. + * + * This is a combination of {@link Tile.FlippedHorizontally}, + * {@link Tile.FlippedVertically}, {@link Tile.FlippedAntiDiagonally} and + * {@link Tile.RotatedHexagonal120}. + * + * @since 1.13 + */ + flags: number; + /** * Whether the tile is flipped horizontally. */ @@ -3718,6 +3738,24 @@ interface TileLayerEdit { */ setTile(x: number, y: number, tile: Tile | null, flags?: number): void; + /** + * Sets the cell at the given location. This is an alternative to + * {@link setTile} that takes a {@link cell} value, which is convenient for + * copying cells along with their flags (for example a value returned by + * {@link TileLayer.cellAt}). + * + * New cells can be created with {@link tiled.cell}. To remove a tile, set an + * empty cell (`tiled.cell()`). + * + * As with {@link setTile}, all locations which have been set retain a special + * flag that is taken into account by {@link TileMap.merge} and + * {@link Tool.preview}, to enable erasing tiles and highlighting the erased + * area, respectively. + * + * @since 1.13 + */ + setCell(x: number, y: number, cell: cell): void; + /** * Applies the changes made through this object to the target layer. This * object can be reused to make further changes. @@ -5220,6 +5258,19 @@ declare namespace tiled { */ export function color(r: number, g: number, b: number, a?: number): color; + /** + * Creates a {@link cell} referring to the given tile, optionally with the + * given flags (any combination of {@link Tile.FlippedHorizontally}, + * {@link Tile.FlippedVertically}, {@link Tile.FlippedAntiDiagonally} and + * {@link Tile.RotatedHexagonal120}). + * + * Pass no tile (or `null`) to create an empty cell, which can be used with + * {@link TileLayerEdit.setCell} to erase a tile. + * + * @since 1.13 + */ + export function cell(tile?: Tile | null, flags?: number): cell; + /** * Creates a {@link FilePath} object with the given URL. */ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a2d6d413e0..5a2f26bbf6 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -6,6 +6,12 @@ base: core24 grade: stable confinement: strict +platforms: + amd64: + arm64: + s390x: + ppc64el: + apps: tiled: command-chain: &command-chain @@ -82,6 +88,28 @@ parts: - -DUSE_SHARED_PIXMAN=on - -DUSE_SHARED_FREETYPE=on - -DUSE_SHARED_HARFBUZZ=on + # Aseprite's vendored `laf` library hardcodes the CPU-architecture + # whitelist (x86/x64/arm64/riscv64) and refuses to compile on s390x + # or ppc64el. Skip the plugin entirely on those arches so the rest + # of Tiled still builds; users there lose .ase/.aseprite import. + override-pull: | + case "$CRAFT_ARCH_BUILD_FOR" in + s390x|ppc64el) + echo "Skipping qaseprite pull on $CRAFT_ARCH_BUILD_FOR" + ;; + *) + craftctl default + ;; + esac + override-build: | + case "$CRAFT_ARCH_BUILD_FOR" in + s390x|ppc64el) + echo "Skipping qaseprite build on $CRAFT_ARCH_BUILD_FOR" + ;; + *) + craftctl default + ;; + esac build-packages: - libfreetype-dev - libgl1-mesa-dev diff --git a/src/libtiled/tilelayer.h b/src/libtiled/tilelayer.h index e38aa40c4b..be84dfb7d8 100644 --- a/src/libtiled/tilelayer.h +++ b/src/libtiled/tilelayer.h @@ -32,8 +32,8 @@ #include "tiled_global.h" #include "layer.h" -#include "tiled.h" #include "tile.h" +#include "tiled.h" #include "tileset.h" #include @@ -67,6 +67,7 @@ class TILEDSHARED_EXPORT Cell Q_PROPERTY(int tileId READ tileId) Q_PROPERTY(bool empty READ isEmpty) + Q_PROPERTY(int flags READ flags WRITE setFlags) Q_PROPERTY(bool flippedHorizontally READ flippedHorizontally WRITE setFlippedHorizontally) Q_PROPERTY(bool flippedVertically READ flippedVertically WRITE setFlippedVertically) Q_PROPERTY(bool flippedAntiDiagonally READ flippedAntiDiagonally WRITE setFlippedAntiDiagonally) @@ -117,6 +118,8 @@ class TILEDSHARED_EXPORT Cell bool flippedAntiDiagonally() const { return _flags & FlippedAntiDiagonally; } bool rotatedHexagonal120() const { return _flags & RotatedHexagonal120; } + void setFlags(int flags) { _flags = (_flags & ~VisualFlags) | (flags & VisualFlags); } + void setFlippedHorizontally(bool v) { v ? _flags |= FlippedHorizontally : _flags &= ~FlippedHorizontally; } void setFlippedVertically(bool v) { v ? _flags |= FlippedVertically : _flags &= ~FlippedVertically; } void setFlippedAntiDiagonally(bool v) { v ? _flags |= FlippedAntiDiagonally : _flags &= ~FlippedAntiDiagonally; } diff --git a/src/tiled/editablemapobject.h b/src/tiled/editablemapobject.h index 3689858f50..0cae959e63 100644 --- a/src/tiled/editablemapobject.h +++ b/src/tiled/editablemapobject.h @@ -133,6 +133,8 @@ class EditableMapObject : public EditableObject MapObject *mapObject() const; + Q_INVOKABLE QString resolvedClassName() const; + void detach(); MapObject *attach(EditableAsset *asset); void hold(std::unique_ptr mapObject); @@ -273,6 +275,11 @@ inline MapObject *EditableMapObject::mapObject() const return static_cast(object()); } +inline QString EditableMapObject::resolvedClassName() const +{ + return mapObject()->effectiveClassName(); +} + inline bool EditableMapObject::isOwning() const { return mDetachedMapObject.get() == object(); diff --git a/src/tiled/scriptmodule.cpp b/src/tiled/scriptmodule.cpp index 8badf86075..eab1d081a9 100644 --- a/src/tiled/scriptmodule.cpp +++ b/src/tiled/scriptmodule.cpp @@ -24,6 +24,7 @@ #include "commandmanager.h" #include "compression.h" #include "documentmanager.h" +#include "editabletile.h" #include "editabletileset.h" #include "issuesmodel.h" #include "logginginterface.h" @@ -261,6 +262,13 @@ QColor ScriptModule::color(float r, float g, float b, float a) const return QColor::fromRgbF(r, g, b, a); } +Cell ScriptModule::cell(EditableTile *tile, int flags) const +{ + Cell cell(tile ? tile->tile() : nullptr); + cell.setFlags(flags); + return cell; +} + FilePath ScriptModule::filePath(const QUrl &path) const { return { path }; diff --git a/src/tiled/scriptmodule.h b/src/tiled/scriptmodule.h index 94bb53ca71..8436e50b73 100644 --- a/src/tiled/scriptmodule.h +++ b/src/tiled/scriptmodule.h @@ -23,6 +23,7 @@ #include "id.h" #include "issuesdock.h" #include "properties.h" +#include "tilelayer.h" #include #include @@ -36,6 +37,7 @@ namespace Tiled { class Document; class EditableAsset; +class EditableTile; class MapEditor; class ScriptImage; class ScriptMapFormatWrapper; @@ -111,6 +113,7 @@ class ScriptModule : public QObject Q_INVOKABLE QColor color(const QString &name) const; Q_INVOKABLE QColor color(float r, float g, float b, float a = 1.0f) const; + Q_INVOKABLE Tiled::Cell cell(Tiled::EditableTile *tile = nullptr, int flags = 0) const; Q_INVOKABLE Tiled::FilePath filePath(const QUrl &path) const; Q_INVOKABLE Tiled::ObjectRef objectRef(int id) const; Q_INVOKABLE QVariant propertyValue(const QString &typeName, const QJSValue &value) const; diff --git a/src/tiled/tilelayeredit.cpp b/src/tiled/tilelayeredit.cpp index a6027c02e3..7cd9e01f85 100644 --- a/src/tiled/tilelayeredit.cpp +++ b/src/tiled/tilelayeredit.cpp @@ -40,18 +40,16 @@ TileLayerEdit::~TileLayerEdit() void TileLayerEdit::setTile(int x, int y, EditableTile *tile, int flags) { Cell cell(tile ? tile->tile() : nullptr); - cell.setChecked(true); // Used to find painted region later (allows erasing) + cell.setFlags(flags); + setCell(x, y, cell); +} - if (flags & EditableTile::FlippedHorizontally) - cell.setFlippedHorizontally(true); - if (flags & EditableTile::FlippedVertically) - cell.setFlippedVertically(true); - if (flags & EditableTile::FlippedAntiDiagonally) - cell.setFlippedAntiDiagonally(true); - if (flags & EditableTile::RotatedHexagonal120) - cell.setRotatedHexagonal120(true); +void TileLayerEdit::setCell(int x, int y, const Cell &cell) +{ + Cell changedCell = cell; + changedCell.setChecked(true); // Used to find painted region later (allows erasing) - mChanges.setCell(x, y, cell); + mChanges.setCell(x, y, changedCell); } void TileLayerEdit::apply() diff --git a/src/tiled/tilelayeredit.h b/src/tiled/tilelayeredit.h index 6d421cbf76..5b35544ff4 100644 --- a/src/tiled/tilelayeredit.h +++ b/src/tiled/tilelayeredit.h @@ -54,6 +54,7 @@ class TileLayerEdit : public QObject public slots: void setTile(int x, int y, EditableTile *tile, int flags = 0); + void setCell(int x, int y, const Tiled::Cell &cell); void apply(); private: