pxl8/tools/aseprite/tile-props/main.lua

304 lines
7.2 KiB
Lua

-- pxl8 tile properties dialog menu
-- provides a ui for editing custom properties on tilemap tiles
local DEBUG = false
local function log(msg)
if DEBUG then
print("[tile-properties] " .. msg)
end
end
local function get_selected_tile()
log("get_selected_tile() called")
if not app then
log("ERROR: app is nil!")
return nil
end
local sprite = app.sprite
log("sprite: " .. tostring(sprite))
if not sprite then
log("No sprite selected")
return nil
end
local layer = app.layer
log("layer: " .. tostring(layer))
if not layer or not layer.isTilemap then
log("Layer is not a tilemap")
return nil
end
local tileset = layer.tileset
log("tileset: " .. tostring(tileset))
if not tileset then
log("No tileset in layer")
return nil
end
local tileIndex = app.fgTile
log("tileIndex: " .. tostring(tileIndex))
if not tileIndex or tileIndex < 0 then
log("Invalid tile index")
return nil
end
log("Selected tile: tileset=" .. tostring(tileset) .. ", index=" .. tostring(tileIndex))
return tileset, tileIndex
end
local function get_tile_properties(tileset, tile_index)
log("get_tile_properties() called for tile index: " .. tostring(tile_index))
local tile = tileset:tile(tile_index)
if not tile then
log("Could not get tile object")
return {}
end
if not tile.properties then
log("Tile has no properties")
return {}
end
local props = {}
for key, value in pairs(tile.properties) do
local propType = "string"
if type(value) == "boolean" then
propType = "boolean"
elseif type(value) == "number" then
propType = "number"
end
table.insert(props, {
key = key,
type = propType,
value = value
})
end
log("Found " .. #props .. " properties")
return props
end
local function set_tile_properties(tileset, tile_index, props)
log("set_tile_properties() called for tile index: " .. tostring(tile_index))
local tile = tileset:tile(tile_index)
if not tile then
log("Could not get tile object")
return
end
tile.properties = {}
local count = 0
for _, prop in ipairs(props) do
if prop.key and prop.key ~= "" then
local value = prop.value
if prop.type == "boolean" then
value = (value == true or value == "true")
elseif prop.type == "number" then
value = tonumber(value) or 0
end
tile.properties[prop.key] = value
count = count + 1
end
end
log("Set " .. count .. " properties")
end
local function show_property_editor()
log("show_property_editor() called")
local tileset, tile_index = get_selected_tile()
if not tileset then
log("No tileset selected, showing alert")
app.alert("Please select a tile in the tileset")
return
end
log("Getting properties for tile")
local properties = get_tile_properties(tileset, tile_index)
local function build_dialog()
local dlg = Dialog("Tile Properties - Tile #" .. tile_index)
for i, prop in ipairs(properties) do
local id_prefix = "prop_" .. i .. "_"
local prop_value = prop
dlg:separator{ id = id_prefix .. "sep" }
dlg:newrow()
dlg:entry{
id = id_prefix .. "key",
label = "Name:",
text = prop_value.key
}
dlg:newrow()
dlg:combobox{
id = id_prefix .. "type",
label = "Type:",
option = prop_value.type,
options = { "boolean", "number", "string" },
onchange = function()
local new_type = dlg.data[id_prefix .. "type"]
if new_type ~= prop_value.type then
if new_type == "boolean" then
properties[i].value = false
elseif new_type == "number" then
properties[i].value = 0
else
properties[i].value = ""
end
properties[i].type = new_type
rebuild_from_dialog(dlg)
end
end
}
dlg:newrow()
if prop_value.type == "boolean" then
dlg:check{
id = id_prefix .. "value",
label = "Value:",
selected = prop_value.value
}
elseif prop_value.type == "number" then
dlg:number{
id = id_prefix .. "value",
label = "Value:",
text = tostring(prop_value.value),
decimals = 0
}
else
dlg:entry{
id = id_prefix .. "value",
label = "Value:",
text = tostring(prop_value.value)
}
end
dlg:button{
id = id_prefix .. "delete",
text = "Delete",
onclick = function()
sync_from_dialog(dlg)
table.remove(properties, i)
rebuild_from_dialog(dlg)
end
}
dlg:newrow()
end
dlg:separator()
dlg:button{
id = "add_btn",
text = "Add Property",
onclick = function()
sync_from_dialog(dlg)
table.insert(properties, {
key = "",
type = "string",
value = ""
})
rebuild_from_dialog(dlg)
end
}
dlg:button{
text = "Apply Changes",
onclick = function()
sync_from_dialog(dlg)
local new_props = {}
for _, prop in ipairs(properties) do
if prop.key and prop.key ~= "" then
table.insert(new_props, prop)
end
end
set_tile_properties(tileset, tile_index, new_props)
end
}
dlg:separator()
dlg:button{
text = "OK",
focus = true,
onclick = function()
sync_from_dialog(dlg)
local new_props = {}
for _, prop in ipairs(properties) do
if prop.key and prop.key ~= "" then
table.insert(new_props, prop)
end
end
set_tile_properties(tileset, tile_index, new_props)
dlg:close()
end
}
dlg:button{ text = "Cancel" }
return dlg
end
function sync_from_dialog(dlg)
log("sync_from_dialog() called")
for i = 1, #properties do
local id_prefix = "prop_" .. i .. "_"
local data = dlg.data
log(" Property " .. i .. ":")
if data[id_prefix .. "key"] then
properties[i].key = data[id_prefix .. "key"]
log(" key = " .. tostring(properties[i].key))
end
if data[id_prefix .. "type"] then
properties[i].type = data[id_prefix .. "type"]
log(" type = " .. tostring(properties[i].type))
end
if data[id_prefix .. "value"] ~= nil then
properties[i].value = data[id_prefix .. "value"]
log(" value = " .. tostring(properties[i].value) .. " (type: " .. type(properties[i].value) .. ")")
else
log(" WARNING: value is nil in dialog data!")
end
end
end
function rebuild_from_dialog(old_dlg)
old_dlg:close()
local new_dlg = build_dialog()
new_dlg:show()
end
local dlg = build_dialog()
dlg:show()
end
function init(plugin)
if not plugin then
print("[tile-properties] ERROR: plugin is nil!")
return
end
plugin:newCommand{
id = "TilePropertiesEditor",
title = "Tile Properties",
group = "sprite_properties",
onenabled = function()
local tileset, tile_index = get_selected_tile()
return tileset ~= nil and tile_index ~= nil
end,
onclick = show_property_editor
}
end
function exit(plugin)
end