mirror of
https://github.com/aseprite/aseprite.git
synced 2024-12-28 06:21:25 +00:00
Add property to disable the standard tilemap UI
Added a Sprite.tileManagementPlugin property for plugins that want to replace the standard tilemap/tileset interface. This includes a new external file field in .aseprite files to specify that the sprite tiles are controlled by a specific plugin. Once this property is set, the standard tilemap/tileset modes selectors will disappear and the only way to make then available will be setting this property to nil/empty string again. Fix https://github.com/aseprite/Attachment-System/issues/21
This commit is contained in:
parent
9475ff67d4
commit
64ce25fae2
@ -275,8 +275,9 @@ reference external palettes, tilesets, or extensions that make use of extended p
|
||||
0 - External palette
|
||||
1 - External tileset
|
||||
2 - Extension name for properties
|
||||
3 - Extension name for tile management (can exist one per sprite)
|
||||
BYTE[7] Reserved (set to zero)
|
||||
STRING External file name or extension ID
|
||||
STRING External file name or extension ID (see NOTE.4)
|
||||
|
||||
### Mask Chunk (0x2016) DEPRECATED
|
||||
|
||||
@ -515,6 +516,15 @@ Details about the ZLIB and DEFLATE compression methods:
|
||||
* Some extra notes that might help you to decode the data:
|
||||
http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
|
||||
|
||||
#### NOTE.4
|
||||
|
||||
The extension ID must be a string like `publisher/ExtensionName`, for
|
||||
example, the [Aseprite Attachment System](https://github.com/aseprite/Attachment-System)
|
||||
uses `aseprite/Attachment-System`.
|
||||
|
||||
This string will be used in a future to automatically link to the
|
||||
extension URL in the [Aseprite Store](https://github.com/aseprite/aseprite/issues/1928).
|
||||
|
||||
## File Format Changes
|
||||
|
||||
1. The first change from the first release of the new .ase format,
|
||||
|
@ -520,6 +520,7 @@ add_library(app-lib
|
||||
cmd/set_slice_key.cpp
|
||||
cmd/set_slice_name.cpp
|
||||
cmd/set_sprite_size.cpp
|
||||
cmd/set_sprite_tile_management_plugin.cpp
|
||||
cmd/set_tag_anidir.cpp
|
||||
cmd/set_tag_color.cpp
|
||||
cmd/set_tag_name.cpp
|
||||
|
53
src/app/cmd/set_sprite_tile_management_plugin.cpp
Normal file
53
src/app/cmd/set_sprite_tile_management_plugin.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/set_sprite_tile_management_plugin.h"
|
||||
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_event.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
SetSpriteTileManagementPlugin::SetSpriteTileManagementPlugin(
|
||||
Sprite* sprite,
|
||||
const std::string& value)
|
||||
: WithSprite(sprite)
|
||||
, m_oldValue(sprite->tileManagementPlugin())
|
||||
, m_newValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
void SetSpriteTileManagementPlugin::onExecute()
|
||||
{
|
||||
Sprite* spr = sprite();
|
||||
spr->setTileManagementPlugin(m_newValue);
|
||||
spr->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSpriteTileManagementPlugin::onUndo()
|
||||
{
|
||||
Sprite* spr = sprite();
|
||||
spr->setTileManagementPlugin(m_oldValue);
|
||||
spr->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSpriteTileManagementPlugin::onFireNotifications()
|
||||
{
|
||||
Sprite* spr = sprite();
|
||||
Doc* doc = static_cast<Doc*>(spr->document());
|
||||
DocEvent ev(doc);
|
||||
ev.sprite(spr);
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onTileManagementPluginChange, ev);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
42
src/app/cmd/set_sprite_tile_management_plugin.h
Normal file
42
src/app/cmd/set_sprite_tile_management_plugin.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_SET_SPRITE_TILE_MANAGEMENT_PLUGIN_H_INCLUDED
|
||||
#define APP_CMD_SET_SPRITE_TILE_MANAGEMENT_PLUGIN_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_sprite.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class SetSpriteTileManagementPlugin : public Cmd
|
||||
, public WithSprite {
|
||||
public:
|
||||
SetSpriteTileManagementPlugin(Sprite* sprite,
|
||||
const std::string& value);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onFireNotifications() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_oldValue.size() + m_newValue.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_oldValue;
|
||||
std::string m_newValue;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -99,6 +99,9 @@ namespace app {
|
||||
// The tileset was remapped (e.g. when tiles are re-ordered).
|
||||
virtual void onRemapTileset(DocEvent& ev, const doc::Remap& remap) { }
|
||||
|
||||
// When the tile management plugin property is changed.
|
||||
virtual void onTileManagementPluginChange(DocEvent& ev) { }
|
||||
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -1375,6 +1375,12 @@ static void ase_file_write_external_files_chunk(
|
||||
putExtentionIds(slice->userData().propertiesMaps(), ext_files);
|
||||
}
|
||||
|
||||
// Tile management plugin
|
||||
if (sprite->hasTileManagementPlugin()) {
|
||||
ext_files.insert(ASE_EXTERNAL_FILE_TILE_MANAGEMENT,
|
||||
sprite->tileManagementPlugin());
|
||||
}
|
||||
|
||||
// No external files to write
|
||||
if (ext_files.items().empty())
|
||||
return;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "app/cmd/set_mask.h"
|
||||
#include "app/cmd/set_pixel_ratio.h"
|
||||
#include "app/cmd/set_sprite_size.h"
|
||||
#include "app/cmd/set_sprite_tile_management_plugin.h"
|
||||
#include "app/cmd/set_transparent_color.h"
|
||||
#include "app/color_spaces.h"
|
||||
#include "app/commands/commands.h"
|
||||
@ -978,6 +979,31 @@ int Sprite_set_pixelRatio(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Sprite_get_tileManagementPlugin(lua_State* L)
|
||||
{
|
||||
const auto sprite = get_docobj<Sprite>(L, 1);
|
||||
if (sprite->hasTileManagementPlugin())
|
||||
lua_pushstring(L, sprite->tileManagementPlugin().c_str());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_set_tileManagementPlugin(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
std::string value;
|
||||
if (const char* p = lua_tostring(L, 2))
|
||||
value = p;
|
||||
|
||||
if (sprite->tileManagementPlugin() != value) {
|
||||
Tx tx;
|
||||
tx(new cmd::SetSpriteTileManagementPlugin(sprite, value));
|
||||
tx.commit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg Sprite_methods[] = {
|
||||
{ "__eq", Sprite_eq },
|
||||
{ "resize", Sprite_resize },
|
||||
@ -1041,6 +1067,7 @@ const Property Sprite_properties[] = {
|
||||
{ "properties", UserData_get_properties<Sprite>, UserData_set_properties<Sprite> },
|
||||
{ "pixelRatio", Sprite_get_pixelRatio, Sprite_set_pixelRatio },
|
||||
{ "events", Sprite_get_events, nullptr },
|
||||
{ "tileManagementPlugin", Sprite_get_tileManagementPlugin, Sprite_set_tileManagementPlugin },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -539,6 +539,12 @@ TilemapMode ColorBar::tilemapMode() const
|
||||
|
||||
void ColorBar::setTilemapMode(TilemapMode mode)
|
||||
{
|
||||
// With sprites that has a custom tile management plugin, we support
|
||||
// only editing pixels in manual mode.
|
||||
if (customTileManagement()) {
|
||||
mode = TilemapMode::Pixels;
|
||||
}
|
||||
|
||||
if (m_tilemapMode != mode) {
|
||||
m_tilemapMode = mode;
|
||||
updateFromTilemapMode();
|
||||
@ -610,8 +616,14 @@ TilesetMode ColorBar::tilesetMode() const
|
||||
return TilesetMode::Manual;
|
||||
}
|
||||
|
||||
void ColorBar::setTilesetMode(const TilesetMode mode)
|
||||
void ColorBar::setTilesetMode(TilesetMode mode)
|
||||
{
|
||||
// With sprites that has a custom tile management plugin, we support
|
||||
// only the manual mode.
|
||||
if (customTileManagement()) {
|
||||
mode = TilesetMode::Manual;
|
||||
}
|
||||
|
||||
m_tilesetMode = mode;
|
||||
|
||||
for (int i=0; i<3; ++i) {
|
||||
@ -652,9 +664,16 @@ void ColorBar::onActiveSiteChange(const Site& site)
|
||||
}
|
||||
|
||||
bool isTilemap = false;
|
||||
if (site.layer())
|
||||
if (site.layer()) {
|
||||
isTilemap = site.layer()->isTilemap();
|
||||
|
||||
if (isTilemap && customTileManagement()) {
|
||||
isTilemap = false;
|
||||
m_tilesetMode = TilesetMode::Manual;
|
||||
m_tilemapMode = TilemapMode::Pixels;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_tilesHBox.isVisible() != isTilemap) {
|
||||
m_tilesHBox.setVisible(isTilemap);
|
||||
updateFromTilemapMode();
|
||||
@ -692,6 +711,12 @@ void ColorBar::onTilesetChanged(DocEvent& ev)
|
||||
m_tilesView.deselect();
|
||||
}
|
||||
|
||||
void ColorBar::onTileManagementPluginChange(DocEvent& ev)
|
||||
{
|
||||
// Same update process as in onActiveSiteChange()
|
||||
onActiveSiteChange(UIContext::instance()->activeSite());
|
||||
}
|
||||
|
||||
void ColorBar::onAppPaletteChange()
|
||||
{
|
||||
COLOR_BAR_TRACE("ColorBar::onAppPaletteChange()\n");
|
||||
@ -2008,7 +2033,15 @@ bool ColorBar::canEditTiles() const
|
||||
{
|
||||
const Site site = UIContext::instance()->activeSite();
|
||||
return (site.layer() &&
|
||||
site.layer()->isTilemap());
|
||||
site.layer()->isTilemap() &&
|
||||
!customTileManagement());
|
||||
}
|
||||
|
||||
bool ColorBar::customTileManagement() const
|
||||
{
|
||||
return (m_lastDocument &&
|
||||
m_lastDocument->sprite() &&
|
||||
m_lastDocument->sprite()->hasTileManagementPlugin());
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -94,7 +94,7 @@ namespace app {
|
||||
void setTilemapMode(TilemapMode mode);
|
||||
|
||||
TilesetMode tilesetMode() const;
|
||||
void setTilesetMode(const TilesetMode mode);
|
||||
void setTilesetMode(TilesetMode mode);
|
||||
|
||||
ColorButton* fgColorButton() { return &m_fgColor; }
|
||||
ColorButton* bgColorButton() { return &m_bgColor; }
|
||||
@ -105,6 +105,7 @@ namespace app {
|
||||
// DocObserver impl
|
||||
void onGeneralUpdate(DocEvent& ev) override;
|
||||
void onTilesetChanged(DocEvent& ev) override;
|
||||
void onTileManagementPluginChange(DocEvent& ev) override;
|
||||
|
||||
// InputChainElement impl
|
||||
void onNewInputPriority(InputChainElement* element,
|
||||
@ -190,6 +191,7 @@ namespace app {
|
||||
void showPalettePresets();
|
||||
void showPaletteOptions();
|
||||
bool canEditTiles() const;
|
||||
bool customTileManagement() const;
|
||||
void updateFromTilemapMode();
|
||||
static void fixColorIndex(ColorButton& color);
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
# Aseprite Document IO Library
|
||||
# Copyright (c) 2022 Igara Studio S.A.
|
||||
# Copyright (c) 2022-2023 Igara Studio S.A.
|
||||
# Copyright (c) 2016-2018 David Capello
|
||||
|
||||
add_library(dio-lib
|
||||
aseprite_common.cpp
|
||||
aseprite_decoder.cpp
|
||||
decode_file.cpp
|
||||
decoder.cpp
|
||||
|
70
src/dio/aseprite_common.cpp
Normal file
70
src/dio/aseprite_common.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// Aseprite Document IO Library
|
||||
// Copyright (c) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "dio/aseprite_common.h"
|
||||
|
||||
namespace dio {
|
||||
|
||||
uint32_t AsepriteExternalFiles::insert(const uint8_t type,
|
||||
const std::string& filename)
|
||||
{
|
||||
auto it = m_toID[type].find(filename);
|
||||
if (it != m_toID[type].end())
|
||||
return it->second;
|
||||
else {
|
||||
insert(++m_lastid, type, filename);
|
||||
return m_lastid;
|
||||
}
|
||||
}
|
||||
|
||||
void AsepriteExternalFiles::insert(uint32_t id,
|
||||
const uint8_t type,
|
||||
const std::string& filename)
|
||||
{
|
||||
ASSERT(type >= 0 && type < ASE_EXTERNAL_FILE_TYPES);
|
||||
|
||||
m_items[id] = Item{ filename, type };
|
||||
m_toID[type][filename] = id;
|
||||
}
|
||||
|
||||
bool AsepriteExternalFiles::getIDByFilename(const uint8_t type,
|
||||
const std::string& fn,
|
||||
uint32_t& id) const
|
||||
{
|
||||
ASSERT(type >= 0 && type < ASE_EXTERNAL_FILE_TYPES);
|
||||
|
||||
auto it = m_toID[type].find(fn);
|
||||
if (it == m_toID[type].end())
|
||||
return false;
|
||||
id = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsepriteExternalFiles::getFilenameByID(uint32_t id, std::string& fn) const
|
||||
{
|
||||
auto it = m_items.find(id);
|
||||
if (it == m_items.end())
|
||||
return false;
|
||||
fn = it->second.fn;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string AsepriteExternalFiles::tileManagementPlugin() const
|
||||
{
|
||||
constexpr uint8_t type = ASE_EXTERNAL_FILE_TILE_MANAGEMENT;
|
||||
auto it = m_toID[type].begin();
|
||||
if (it != m_toID[type].end())
|
||||
return it->first;
|
||||
else
|
||||
return std::string();
|
||||
}
|
||||
|
||||
} // namespace dio
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document IO Library
|
||||
// Copyright (c) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (c) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -66,7 +66,8 @@
|
||||
#define ASE_EXTERNAL_FILE_PALETTE 0
|
||||
#define ASE_EXTERNAL_FILE_TILESET 1
|
||||
#define ASE_EXTERNAL_FILE_EXTENSION 2
|
||||
#define ASE_EXTERNAL_FILE_TYPES 3
|
||||
#define ASE_EXTERNAL_FILE_TILE_MANAGEMENT 3
|
||||
#define ASE_EXTERNAL_FILE_TYPES 4
|
||||
|
||||
namespace dio {
|
||||
|
||||
@ -115,53 +116,27 @@ public:
|
||||
|
||||
using Items = std::map<uint32_t, Item>;
|
||||
|
||||
const Items& items() const {
|
||||
return m_items;
|
||||
}
|
||||
bool empty() const { return m_items.empty(); }
|
||||
const Items& items() const { return m_items; }
|
||||
|
||||
// Adds the external filename with the next autogenerated ID and specified type.
|
||||
uint32_t insert(const uint8_t type,
|
||||
const std::string& filename) {
|
||||
auto it = m_toID[type].find(filename);
|
||||
if (it != m_toID[type].end())
|
||||
return it->second;
|
||||
else {
|
||||
insert(++m_lastid, type, filename);
|
||||
return m_lastid;
|
||||
}
|
||||
}
|
||||
const std::string& filename);
|
||||
|
||||
// Adds the external filename using the specified ID and type.
|
||||
void insert(uint32_t id,
|
||||
const uint8_t type,
|
||||
const std::string& filename) {
|
||||
ASSERT(type >= 0 && type < ASE_EXTERNAL_FILE_TYPES);
|
||||
|
||||
m_items[id] = Item{ filename, type };
|
||||
m_toID[type][filename] = id;
|
||||
}
|
||||
const std::string& filename);
|
||||
|
||||
// Returns true if the given filename exists in the external files
|
||||
// chunk, and assign its ID in "id"
|
||||
bool getIDByFilename(const uint8_t type,
|
||||
const std::string& fn,
|
||||
uint32_t& id) const {
|
||||
ASSERT(type >= 0 && type < ASE_EXTERNAL_FILE_TYPES);
|
||||
uint32_t& id) const;
|
||||
|
||||
auto it = m_toID[type].find(fn);
|
||||
if (it == m_toID[type].end())
|
||||
return false;
|
||||
id = it->second;
|
||||
return true;
|
||||
}
|
||||
bool getFilenameByID(uint32_t id, std::string& fn) const;
|
||||
|
||||
bool getFilenameByID(uint32_t id, std::string& fn) const {
|
||||
auto it = m_items.find(id);
|
||||
if (it == m_items.end())
|
||||
return false;
|
||||
fn = it->second.fn;
|
||||
return true;
|
||||
}
|
||||
std::string tileManagementPlugin() const;
|
||||
|
||||
private:
|
||||
uint32_t m_lastid = 0; // ID used to add new items
|
||||
|
@ -187,6 +187,13 @@ bool AsepriteDecoder::decode()
|
||||
|
||||
case ASE_FILE_CHUNK_EXTERNAL_FILE:
|
||||
readExternalFiles(extFiles);
|
||||
|
||||
// Tile management plugin
|
||||
if (!extFiles.empty()) {
|
||||
std::string fn = extFiles.tileManagementPlugin();
|
||||
if (!fn.empty())
|
||||
sprite->setTileManagementPlugin(fn);
|
||||
}
|
||||
break;
|
||||
|
||||
case ASE_FILE_CHUNK_MASK: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -29,6 +29,7 @@
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define DOC_SPRITE_MAX_WIDTH 65535
|
||||
@ -222,6 +223,18 @@ namespace doc {
|
||||
bool hasTilesets() const { return m_tilesets != nullptr; }
|
||||
Tilesets* tilesets() const;
|
||||
|
||||
const std::string& tileManagementPlugin() const {
|
||||
return m_tileManagementPlugin;
|
||||
}
|
||||
|
||||
bool hasTileManagementPlugin() const {
|
||||
return !m_tileManagementPlugin.empty();
|
||||
}
|
||||
|
||||
void setTileManagementPlugin(const std::string& plugin) {
|
||||
m_tileManagementPlugin = plugin;
|
||||
}
|
||||
|
||||
private:
|
||||
Document* m_document;
|
||||
ImageSpec m_spec;
|
||||
@ -242,6 +255,15 @@ namespace doc {
|
||||
// Tilesets
|
||||
mutable Tilesets* m_tilesets;
|
||||
|
||||
// Custom tile management plugin. This can be an ID that specifies
|
||||
// a custom plugin that will be used to handle tilesets and
|
||||
// tilemaps for this specific sprite. This property is saved
|
||||
// inside .aseprite files (ASE_EXTERNAL_FILE_TILE_MANAGEMENT), and
|
||||
// it's used by the UI to disable the standard tileset/tilemap UX
|
||||
// (e.g. drag & drop tiles, or TilesetMode::Auto mode, etc.),
|
||||
// giving the possibility to handle tiles exclusively to a plugin.
|
||||
std::string m_tileManagementPlugin;
|
||||
|
||||
// Disable default constructor and copying
|
||||
Sprite();
|
||||
DISABLE_COPYING(Sprite);
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
-- Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
-- Copyright (C) 2018 David Capello
|
||||
--
|
||||
-- This file is released under the terms of the MIT license.
|
||||
@ -206,3 +206,25 @@ do
|
||||
assert(a == a)
|
||||
assert(a ~= b) -- Compares IDs, not sprite size
|
||||
end
|
||||
|
||||
-- Tile management plugin
|
||||
|
||||
do
|
||||
local fn = "_test_sprite_tileManagementPlugin.aseprite"
|
||||
local a = Sprite(1, 1)
|
||||
assert(a.tileManagementPlugin == nil)
|
||||
a.tileManagementPlugin = "test"
|
||||
app.undo()
|
||||
assert(a.tileManagementPlugin == nil)
|
||||
app.redo()
|
||||
assert(a.tileManagementPlugin == "test")
|
||||
a:saveAs(fn)
|
||||
|
||||
b = app.open(fn)
|
||||
assert(b.tileManagementPlugin == "test")
|
||||
b.tileManagementPlugin = nil
|
||||
b:saveAs(fn)
|
||||
|
||||
c = app.open(fn)
|
||||
assert(c.tileManagementPlugin == nil)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user