]> git.bts.cx Git - aseprite-tools.git/commitdiff
Initial commit
authorBen Sherratt <redacted>
Thu, 18 Dec 2025 23:48:01 +0000 (23:48 +0000)
committerBen Sherratt <redacted>
Thu, 18 Dec 2025 23:48:01 +0000 (23:48 +0000)
export-metrics.lua [new file with mode: 0644]
export-p8-font-metrics.lua [new file with mode: 0644]
export-puzzlescript.lua [new file with mode: 0644]

diff --git a/export-metrics.lua b/export-metrics.lua
new file mode 100644 (file)
index 0000000..f06294c
--- /dev/null
@@ -0,0 +1,110 @@
+local pluginKey = "bts/metrics"
+
+local sprite = app.sprite
+
+local name = app.fs.fileTitle(sprite.filename)
+
+local previousExportFilename = sprite.properties(pluginKey).previousExportFilename
+
+if previousExportFilename == nil then
+       local defaultPath = app.fs.filePath(sprite.filename)
+       local defaultFilename = name..".lua"
+       previousExportFilename = app.fs.joinPath(defaultPath, defaultFilename)
+end
+
+local dlg = Dialog()
+dlg:file{ id="metrics_filename", label="Metrics output:", filename=previousExportFilename, filetypes=".lua", save=true }
+dlg:button{ id="confirm", text="Confirm" }
+dlg:button{ id="cancel", text="Cancel" }
+dlg:show()
+
+local data = dlg.data
+if data.confirm then
+       name = app.fs.fileTitle(data.metrics_filename)
+
+       local basePath = app.fs.filePath(data.metrics_filename)
+       local imagePath = app.fs.joinPath(basePath, name)
+
+       local layerLookup = {}
+       local originalLayerVisible = {}
+       
+       for i, layer in ipairs(sprite.layers) do
+               if layer.parent == layer.sprite then
+                       originalLayerVisible[layer.name] = layer.isVisible
+                       layer.isVisible = false
+                       
+                       layerLookup[layer.name] = layer
+               end
+       end
+       
+       local imageDetails = {}
+       
+       for i, slice in ipairs(sprite.slices) do
+               local id = slice.name
+       
+               if layerLookup[id] then
+                       local layer = layerLookup[id]
+                       local filename = app.fs.joinPath(imagePath, id..".png")
+       
+                       layer.isVisible = true
+                       app.command.SaveFileCopyAs{ ui=false, filename=filename, slice=id }
+                       layer.isVisible = false
+       
+                       table.insert(imageDetails, id)
+               end
+       end
+       
+       local metricsDetails = {}
+       
+       for i, slice in ipairs(sprite.slices) do
+               local id = slice.name
+               local sliceMetrics = {}
+       
+               sliceMetrics["origin"] = { slice.bounds.x, slice.bounds.y}
+               sliceMetrics["size"] = { slice.bounds.width, slice.bounds.height}
+               sliceMetrics["center"] = { (slice.bounds.x + slice.bounds.width / 2), (slice.bounds.y + slice.bounds.height / 2)}
+       
+               metricsDetails[id] = sliceMetrics
+       end
+       
+       --[[if false then
+               local metricsFilename = app.fs.joinPath(metricsPath, name..".ui.metrics.json")
+               local metricsFile = io.open(metricsFilename, "w")
+               local metricsJson = json.encode(metricsDetails)
+               metricsFile:write(metricsJson)
+               io.close(metricsFile)
+       end]]
+       
+       if true then
+               --local metricsFilename = app.fs.joinPath(metricsPath, data.metrics_filename)
+               local metricsFile = io.open(data.metrics_filename, "w")
+       
+               metricsFile:write(name.."ImagesTable = {\n")
+               for k,v in ipairs(imageDetails) do
+                       metricsFile:write("\t\"" .. v .. "\",\n")
+               end
+               metricsFile:write("}\n")
+               metricsFile:write("\n")
+       
+               metricsFile:write(name.."MetricsTable = {\n")
+               for k,v in pairs(metricsDetails) do
+                       metricsFile:write("\n")
+                       metricsFile:write("\t[\"" .. k .. ".origin\"] = { x=" .. v["origin"][1] .. ", y=" .. v["origin"][2] .. " },\n")
+                       metricsFile:write("\t[\"" .. k .. ".size\"] = { w=" .. v["size"][1] .. ", h=" .. v["size"][2] .. " },\n")
+                       metricsFile:write("\t[\"" .. k .. ".center\"] = { x=" .. v["center"][1] .. ", y=" .. v["center"][2] .. " },\n")
+               end
+               metricsFile:write("\n")
+               metricsFile:write("}\n")
+               metricsFile:write("\n")
+               metricsFile:write("registerMetrics(\""..name.."\", "..name.."ImagesTable, "..name.."MetricsTable)\n")
+       
+               io.close(metricsFile)
+       end
+       
+       for k, v in pairs(originalLayerVisible) do
+               local layer = layerLookup[k]
+               layer.isVisible = v
+       end
+
+       sprite.properties(pluginKey).previousExportFilename = data.metrics_filename
+end
diff --git a/export-p8-font-metrics.lua b/export-p8-font-metrics.lua
new file mode 100644 (file)
index 0000000..b1003ee
--- /dev/null
@@ -0,0 +1,59 @@
+local pluginKey = "bts/p8-font-metrics"
+
+local sprite = app.sprite
+
+local name = app.fs.fileTitle(sprite.filename)
+
+local previousExportFilename = sprite.properties(pluginKey).previousExportFilename
+
+if previousExportFilename == nil then
+       local defaultPath = app.fs.filePath(sprite.filename)
+       local defaultFilename = name..".lua"
+       previousExportFilename = app.fs.joinPath(defaultPath, defaultFilename)
+end
+
+local dlg = Dialog()
+dlg:file{ id="metrics_filename", label="Metrics output:", filename=previousExportFilename, filetypes=".lua", save=true }
+dlg:button{ id="confirm", text="Confirm" }
+dlg:button{ id="cancel", text="Cancel" }
+dlg:show()
+
+local data = dlg.data
+if data.confirm then
+       name = app.fs.fileTitle(data.metrics_filename)
+
+       local basePath = app.fs.filePath(data.metrics_filename)
+       local imagePath = app.fs.joinPath(basePath, name)
+       
+       local characterDetails = {}
+
+       for cy=0,15 do
+               for cx=0,15 do
+                       local x,y=cx*8,cy*8
+                       local width = 8
+                       for i, slice in ipairs(sprite.slices) do
+                               if slice.bounds.x == x and slice.bounds.y == y then
+                                       width = slice.bounds.width+1
+                                       break
+                               end
+                       end
+                       table.insert(characterDetails, width)
+               end
+       end
+       
+       if true then
+               --local metricsFilename = app.fs.joinPath(metricsPath, data.metrics_filename)
+               local metricsFile = io.open(data.metrics_filename, "w")
+       
+               metricsFile:write(name.."Widths = split[[")
+               for k,v in ipairs(characterDetails) do
+                       metricsFile:write(v .. ",")
+               end
+               metricsFile:write("]]\n")
+               metricsFile:write("\n")
+
+               io.close(metricsFile)
+       end
+
+       sprite.properties(pluginKey).previousExportFilename = data.metrics_filename
+end
diff --git a/export-puzzlescript.lua b/export-puzzlescript.lua
new file mode 100644 (file)
index 0000000..31ef326
--- /dev/null
@@ -0,0 +1,618 @@
+local plugin_key = "bts/puzzlescript"
+
+local sprite = app.sprite
+local pc = app.pixelColor
+
+
+
+function hexColor(c)
+       return string.format("#%02x%02x%02x", c.red, c.green, c.blue)
+end
+
+function findOrInsertIdx(table, value)
+       for i=1,#table do
+               if table[i] == value then
+                       return i
+               end
+       end
+       table[#table + 1] = value
+       return #table
+end
+
+function findOrAdd(table, idx, default)
+       if table[idx] == nil then
+               table[idx] = default or {}
+       end
+       return table[idx]
+end
+
+function get_layers_flat(output, layer_container)
+       for _, layer in ipairs(layer_container.layers) do
+               table.insert(output, layer)
+               if layer.isGroup then
+                       get_layers_flat(output, layer)
+               end
+       end
+
+       return output
+end
+
+
+
+local PuzzleScriptSection = {}
+
+function PuzzleScriptSection:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.items = {}
+       return o
+end
+
+function PuzzleScriptSection:add_item(item)
+       self.items[#self.items + 1] = item
+end
+
+
+
+local PuzzleScriptRawItem = {}
+
+function PuzzleScriptRawItem:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.lines = {}
+       return o
+end
+
+function PuzzleScriptRawItem:add_line(line)
+       self.lines[#self.lines + 1] = line
+end
+
+function PuzzleScriptRawItem:export(script_file)
+       for _, section_item_line in pairs(self.lines) do
+               script_file:write(section_item_line)
+               script_file:write("\n")
+       end
+end
+
+
+
+local PuzzleScriptObjectItem = {}
+
+function PuzzleScriptObjectItem:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.colors = {}
+       o.pixels = {}
+       return o
+end
+
+function PuzzleScriptObjectItem:set_color(color)
+       if color.alpha == 255 then
+               self.colors = { hexColor(color) }
+       else
+               self.colors = { "transparent" }
+       end
+       self.pixels = nil
+end
+
+function PuzzleScriptObjectItem:set_pixel(x, y, color)
+       local pixel_idx = y * 5 + x + 1
+       local pixel_value = "."
+       if color.alpha == 255 then
+               pixel_value = findOrInsertIdx(self.colors, hexColor(color)) - 1
+       end
+
+       self.pixels[pixel_idx] = pixel_value
+end
+
+function PuzzleScriptObjectItem:export(script_file)
+       script_file:write(self.id)
+       script_file:write("\n")
+
+       script_file:write(table.concat(self.colors, " "))
+       script_file:write("\n")
+
+       if self.pixels then
+               for y = 0, 4 do
+                       for x = 0, 4 do
+                               script_file:write(self.pixels[y * 5 + x + 1])
+                       end
+                       script_file:write("\n")
+               end
+       end
+end
+
+
+
+local PuzzleScriptLayerItem = {}
+
+function PuzzleScriptLayerItem:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.objects = {}
+       return o
+end
+
+function PuzzleScriptLayerItem:add_object(object)
+       self.objects[#self.objects + 1] = object
+end
+
+function PuzzleScriptLayerItem:export(script_file)
+       local object_ids = { self.id }
+       for i, obj in ipairs(self.objects) do
+               object_ids[i] = obj.id
+       end
+       script_file:write(table.concat(object_ids, ", "))
+       script_file:write("\n")
+end
+
+
+
+local PuzzleScriptLegendItem = {}
+
+function PuzzleScriptLegendItem:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+--     o.objects = {}
+       return o
+end
+
+function PuzzleScriptLegendItem:obj_match(objects)
+       if objects == nil and self.objects == nil then return true end
+       if objects == nil or self.objects == nil then return false end
+       if #objects ~= #self.objects then return false end
+       for i = 1, #self.objects do
+               if objects[i] ~= self.objects[i] then return false end
+       end
+       return true     
+end
+
+function PuzzleScriptLegendItem:set_objects(objects)
+       self.objects = {}
+       for _, obj in pairs(objects) do
+               table.insert(self.objects, obj)
+       end
+end
+
+function PuzzleScriptLegendItem:export(script_file)
+       local object_ids = {}
+
+       if self.objects then
+               for i, obj in ipairs(self.objects) do
+                       object_ids[i] = obj.id
+               end
+       else
+               object_ids = { "Background" } -- FIXME
+       end
+
+       script_file:write(self.id)
+       script_file:write(" = ")
+       script_file:write(table.concat(object_ids, " and "))
+
+       script_file:write("\n")
+end
+
+
+
+local PuzzleScriptLegendMapping = {}
+
+function PuzzleScriptLegendMapping:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       return o
+end
+
+function PuzzleScriptLegendMapping:add_objects(objects)
+       self.objects = self.objects or {}
+       for _, obj in pairs(objects) do
+               table.insert(self.objects, obj)
+       end
+end
+
+function PuzzleScriptLegendMapping:export(script_file)
+       local object_ids = {}
+
+       if self.objects then
+               for i, obj in ipairs(self.objects) do
+                       object_ids[i] = obj.id
+               end
+       end
+
+       -- Failsafe for matching name and single item
+       if #object_ids == 1 and object_ids[1] == self.id then return end
+
+       script_file:write(self.id)
+       script_file:write(" = ")
+       script_file:write(table.concat(object_ids, " or "))
+
+       script_file:write("\n")
+end
+
+
+
+
+local PuzzleScriptLevelItem = {}
+
+function PuzzleScriptLevelItem:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.tiles = {}
+       return o
+end
+
+function PuzzleScriptLevelItem:set_tile(tile_x, tile_y, value)
+       local tile_idx = tile_y * self.width + tile_x
+       if self.tiles[tile_idx] == nil then
+               self.tiles[tile_idx] = {}
+       end
+
+       table.insert(self.tiles[tile_idx], value)
+end
+
+function PuzzleScriptLevelItem:map_to_legend(legend_section, legend_icons)
+       self.tile_legends = {}
+
+       for tile_y = 0, self.height - 1 do
+               for tile_x = 0, self.width - 1 do
+                       local tile_idx = tile_y * self.width + tile_x
+
+                       local legend_item = nil
+                       for _, test_legend_item in pairs(legend_section.items) do
+                               if test_legend_item:obj_match(self.tiles[tile_idx]) then
+                                       legend_item = test_legend_item
+                                       break
+                               end
+                       end
+
+                       if legend_item == nil then
+                               legend_item = PuzzleScriptLegendItem:new()
+                               assert(#legend_icons > 0)
+                               legend_item.id = legend_icons[1]
+                               table.remove(legend_icons, 1)
+                               legend_item:set_objects(self.tiles[tile_idx])
+                               legend_section:add_item(legend_item)
+                       end
+
+                       self.tile_legends[tile_idx] = legend_item
+               end
+       end
+end
+
+function PuzzleScriptLevelItem:export(script_file)
+       for tile_y = 0, self.height - 1 do
+               for tile_x = 0, self.width - 1 do
+                       local tile_idx = tile_y * self.width + tile_x
+                       script_file:write(self.tile_legends[tile_idx].id)
+               end
+               script_file:write("\n")
+       end
+end
+
+
+
+local PuzzleScriptFile = {}
+
+function PuzzleScriptFile:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+       o.sections = {}
+       return o
+end
+
+function PuzzleScriptFile:get_section(id)
+       id = string.upper(id)
+       return findOrAdd(self.sections, id, PuzzleScriptSection:new{})
+end
+
+function PuzzleScriptFile:read(filename)
+       local script_file = io.open(filename, "r")
+
+       if script_file then
+               local current_section = self:get_section("PRELUDE")
+               local current_item = nil
+               while true do
+                       local line = script_file:read()
+                       if line == nil then break end
+                       if string.match(line, "^=+$") then
+                               local current_section_name = script_file:read()
+                               current_section = self:get_section(current_section_name)
+                               script_file:read() -- Skip the next line as it's just the closing ===
+
+                               current_item = nil
+                       elseif string.match(line, "^[ \t\r\n]*$") then
+                               current_item = nil
+                       else
+                               if current_item == nil then
+                                       current_item = PuzzleScriptRawItem:new{}
+                                       current_section:add_item(current_item)
+                               end
+
+                               current_item:add_line(line)
+                       end
+               end
+
+               io.close(script_file)
+       end
+end
+
+function PuzzleScriptFile:export(filename)
+       local script_file = io.open(filename, "w")
+
+       local ordered_section_names = {
+               "PRELUDE",
+               "OBJECTS",
+               "LEGEND",
+               "SOUNDS",
+               "COLLISIONLAYERS",
+               "RULES",
+               "WINCONDITIONS",
+               "LEVELS"
+       }
+
+       for _, section_name in pairs(ordered_section_names) do
+               local section = self:get_section(section_name)
+               if section ~= nil then
+                       if section_name ~= "PRELUDE" then
+                               local separator = string.rep("=", string.len(section_name))
+                               script_file:write(separator)
+                               script_file:write("\n")
+                               script_file:write(section_name)
+                               script_file:write("\n")
+                               script_file:write(separator)
+                               script_file:write("\n\n")
+                       end
+
+                       for _, section_item in pairs(section.items) do
+                               section_item:export(script_file)
+
+                               if section_name ~= "COLLISIONLAYERS" and section_name ~= "LEGEND" then -- fixme, table??
+                                       script_file:write("\n")
+                               end
+                       end
+
+                       if section_name == "COLLISIONLAYERS" or section_name == "LEGEND" then -- fixme, table??
+                               script_file:write("\n")
+                       end
+               end
+       end
+       
+       io.close(script_file)
+end
+
+
+
+function main()
+       local name = app.fs.fileTitle(sprite.filename)
+
+       local previous_export_filename = sprite.properties(plugin_key).previous_export_filename
+       local previous_merge_filename = sprite.properties(plugin_key).previous_merge_filename
+       local previous_export_lofi = sprite.properties(plugin_key).previous_export_lofi
+
+       if previous_export_filename == nil then
+               local default_path = app.fs.filePath(sprite.filename)
+               local default_filename = name..".txt"
+               previous_export_filename = app.fs.joinPath(default_path, default_filename)
+       end
+
+       local dlg = Dialog()
+       dlg:file{ id="script_filename", label="Output:", filename=previous_export_filename, filetypes=".txt", save=true }
+       dlg:file{ id="merge_filename", label="Merge script (optional):", filename=previous_merge_filename, filetypes=".txt" }
+       dlg:check{ id="lofi", label="Prototype export (layer colours)", selected=previous_export_lofi }
+       dlg:button{ id="confirm", text="Confirm" }
+       dlg:button{ id="cancel", text="Cancel" }
+       dlg:show()
+
+       local data = dlg.data
+       if data.confirm == false then
+               return
+       end
+
+       local layers = get_layers_flat({}, sprite)
+
+       local pz_file = PuzzleScriptFile:new{}
+
+
+       local object_section = pz_file:get_section("OBJECTS")
+
+       local layer_objects = {}
+
+       for _, layer in ipairs(layers) do
+               if layer.isVisible then
+                       if layer.isImage == true and (layer.isTilemap == false or data.lofi) then
+                               local object_item = PuzzleScriptObjectItem:new()
+                               object_item.id = layer.name
+                               object_section:add_item(object_item)
+                               object_item:set_color(layer.color)
+                       elseif layer.isTilemap == true then
+                               local tileset = layer.tileset
+
+                               local used_tiles = {}
+                               for _, frame in ipairs(sprite.frames) do
+                                       local cel = layer:cel(frame)
+                                       if cel ~= nil then
+                                               for pixel in cel.image:pixels() do
+                                                       p = pc.tileI(pixel())
+                                                       if p > 0 then
+                                                               used_tiles[p] = true
+                                                       end
+                                               end
+                                       end
+                               end
+
+                               local object_names = {}
+                               for object_name in string.gmatch(layer.data, "[^,]+") do
+                                       if object_name ~= "" then
+                                               object_names[#object_names + 1] = object_name
+                                               used_tiles[#object_names] = true -- Always export named tiles
+                                       end
+                               end
+
+                               for tile_idx, _ in pairs(used_tiles) do
+                                       local object_item = PuzzleScriptObjectItem:new()
+                                       object_section:add_item(object_item)
+
+                                       if object_names[tile_idx] then
+                                               object_item.id = object_names[tile_idx]
+                                       else
+                                               object_item.id = layer.name..tile_idx
+                                       end
+
+                                       object_item.layer = layer
+
+                                       local tile = tileset:tile(tile_idx)
+
+                                       if tile then
+                                               local layer_tiles = findOrAdd(layer_objects, layer)
+                                               layer_tiles[tile_idx] = object_item
+
+                                               local colors = {}
+                                               local pixels = {}
+                                               for pixel in tile.image:pixels() do
+                                                       object_item:set_pixel(pixel.x, pixel.y, Color(pixel()))
+                                               end
+                                       else
+                                               assert(false, "Missing tile: " .. layer.name .. "  " .. tile_idx)
+                                       end
+                               end
+                       end
+               end
+       end
+
+       local levels_section = pz_file:get_section("LEVELS")
+
+       local tiles_w = math.floor(sprite.width / 5)
+       local tiles_h = math.floor(sprite.height / 5)
+
+       for _, frame in ipairs(sprite.frames) do
+               local level_item = PuzzleScriptLevelItem:new()
+               levels_section:add_item(level_item)
+               level_item.width = tiles_w
+               level_item.height = tiles_h
+
+               for tile_y = 0, tiles_h - 1 do
+                       for tile_x = 0, tiles_w - 1 do
+                               for _, layer in ipairs(layers) do
+                                       if layer.isVisible and layer.isTilemap == true then
+                                               local cel = layer:cel(frame)
+                                               if cel ~= nil then
+                                                       local image = cel.image
+
+                                                       local offset_x = math.floor(cel.bounds.x / 5)
+                                                       local offset_y = math.floor(cel.bounds.y / 5)
+                                                       local cel_width = math.floor(cel.bounds.width / 5)
+                                                       local cel_height = math.floor(cel.bounds.height / 5)
+
+                                                       local actual_tile_x = tile_x - offset_x
+                                                       local actual_tile_y = tile_y - offset_y
+                                                       if actual_tile_x >= 0 and actual_tile_y >= 0 and actual_tile_x < cel_width and actual_tile_y < cel_height then
+                                                               local pixel_value = image:getPixel(actual_tile_x, actual_tile_y)
+                                                               local tile_idx = pc.tileI(pixel_value)
+                                                               if tile_idx > 0 then
+                                                                       assert(layer_objects[layer][tile_idx], "Looking for: " .. layer.name)
+                                                                       level_item:set_tile(tile_x, tile_y, layer_objects[layer][tile_idx])
+                                                               end
+                                                       end
+                                               end
+                                       end
+                               end
+                       end
+               end
+       end
+
+       local legend_section = pz_file:get_section("LEGEND")
+
+       local background_legend = PuzzleScriptLegendItem:new()
+       background_legend.id = "."
+       legend_section:add_item(background_legend)
+
+       local legend_icons = {}
+       for icon in string.gmatch("!\"#$%&'*+,-/\\0123456789:;?@_`abcdefghijklmnopqrstuvwzyz~", ".") do
+               legend_icons[#legend_icons + 1] = icon
+       end
+
+       for _, level in pairs(levels_section.items) do
+               level:map_to_legend(legend_section, legend_icons)
+       end
+
+
+
+
+       local layers_section = pz_file:get_section("COLLISIONLAYERS")
+       for _, layer in ipairs(sprite.layers) do
+               local layer_item = PuzzleScriptLayerItem:new()
+               layer_item.id = layer.name
+               layers_section:add_item(layer_item)
+       end
+
+
+
+       for layer, objects in pairs(layer_objects) do
+               local legend_map = PuzzleScriptLegendMapping:new()
+               legend_map.id = layer.name
+               legend_map:add_objects(objects)
+               legend_section:add_item(legend_map)
+       end
+
+       for _, layer in ipairs(layers) do
+               if layer.isGroup and #layer.layers > 0 then
+                       local legend_map = PuzzleScriptLegendMapping:new()
+                       legend_map.id = layer.name
+                       for _, sub_layer in ipairs(layer.layers) do
+                               legend_map:add_objects({ {id=sub_layer.name} })
+                       end
+                       legend_section:add_item(legend_map)
+               end
+       end
+
+--     for _, objects in pairs(layer_objects) do
+--             for _, object in pairs(objects) do
+--                     local layer_item = PuzzleScriptLayerItem:new()
+--                     layers_section:add_item(layer_item)
+--                     layer_item.id = object.layer.name
+--             end
+--     end
+
+
+--     for _, layer in ipairs(layers) do
+--             if layer.isVisible and layer.parent == sprite then
+
+
+
+--PuzzleScriptLegendMapping:new()
+
+--                     local layer_item = PuzzleScriptLayerItem:new()
+--                     layers_section:add_item(layer_item)
+--                     layer_item.id = layer.name
+--             end
+--     end
+
+
+--legend stuff again after...
+
+
+
+       -- Merge after making everything else
+       pz_file:read(data.merge_filename)
+
+       pz_file:export(data.script_filename)
+
+       local export_filename_changed = sprite.properties(plugin_key).previous_export_filename ~= data.script_filename
+       local merge_filename_changed = sprite.properties(plugin_key).previous_merge_filename ~= data.merge_filename
+       local lofi_changed = sprite.properties(plugin_key).previous_export_lofi ~= data.lofi
+
+       if export_filename_changed or merge_filename_changed or lofi_changed then
+               sprite.properties(plugin_key).previous_export_filename = data.script_filename
+               sprite.properties(plugin_key).previous_merge_filename = data.merge_filename
+               sprite.properties(plugin_key).previous_export_lofi = data.lofi
+       end
+end
+
+main()