-- pxl8 tile properties editor -- provides a ui for editing custom properties of tilemap tiles local DEBUG = false local function log(msg) if DEBUG then print("[tile-properties] " .. msg) end end local function getSelectedTile() log("getSelectedTile() 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 getTileProperties(tileset, tileIndex) log("getTileProperties() called for tile index: " .. tostring(tileIndex)) local tile = tileset:tile(tileIndex) 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 setTileProperties(tileset, tileIndex, props) log("setTileProperties() called for tile index: " .. tostring(tileIndex)) local tile = tileset:tile(tileIndex) 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 showPropertyEditor(existingProps) log("showPropertyEditor() called") local tileset, tileIndex = getSelectedTile() 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 = existingProps or getTileProperties(tileset, tileIndex) local dlg = Dialog("Tile Properties - Tile #" .. tileIndex) dlg:label{ text="Properties:" } for i, prop in ipairs(properties) do dlg:separator() dlg:entry{ id = "key_" .. i, label = "Name:", text = prop.key } dlg:combobox{ id = "type_" .. i, label = "Type:", option = prop.type, options = { "boolean", "number", "string" }, onchange = function() -- Save all current field values and apply type conversions for j = 1, #properties do properties[j].key = dlg.data["key_" .. j] or properties[j].key local newType = dlg.data["type_" .. j] or properties[j].type local oldType = properties[j].type -- Apply default values when type changes if newType ~= oldType then if newType == "boolean" then properties[j].value = false elseif newType == "number" then properties[j].value = 0 else -- string properties[j].value = "" end else properties[j].value = dlg.data["value_" .. j] or properties[j].value end properties[j].type = newType end dlg:close() showPropertyEditor(properties) end } if prop.type == "boolean" then dlg:check{ id = "value_" .. i, text = "", selected = prop.value } elseif prop.type == "number" then dlg:number{ id = "value_" .. i, label = "Value:", text = tostring(prop.value), decimals = 0 } else dlg:entry{ id = "value_" .. i, label = "Value:", text = tostring(prop.value) } end dlg:button{ id = "delete_" .. i, text = "Delete", onclick = function() -- Save current field values before deleting for j = 1, #properties do properties[j].key = dlg.data["key_" .. j] or properties[j].key properties[j].type = dlg.data["type_" .. j] or properties[j].type properties[j].value = dlg.data["value_" .. j] or properties[j].value end table.remove(properties, i) dlg:close() showPropertyEditor(properties) end } end dlg:separator() dlg:button{ text = "Add Property", onclick = function() -- Save current field values before adding new property for i = 1, #properties do properties[i].key = dlg.data["key_" .. i] or properties[i].key properties[i].type = dlg.data["type_" .. i] or properties[i].type properties[i].value = dlg.data["value_" .. i] or properties[i].value end table.insert(properties, { key = "", type = "string", value = "" }) dlg:close() showPropertyEditor(properties) end } dlg:separator() dlg:button{ text = "Apply", onclick = function() local newProps = {} for i = 1, #properties do local key = dlg.data["key_" .. i] local propType = dlg.data["type_" .. i] local value = dlg.data["value_" .. i] if key and key ~= "" then table.insert(newProps, { key = key, type = propType, value = value }) end end setTileProperties(tileset, tileIndex, newProps) dlg:close() end } dlg:button{ text = "Cancel" } dlg:show() end function init(plugin) log("=== PLUGIN INIT START ===") if not plugin then print("[tile-properties] ERROR: plugin is nil!") return end -- Register in Sprite menu plugin:newCommand{ id = "TilePropertiesEditor", title = "Tile Properties", group = "sprite_properties", onenabled = function() local tileset, tileIndex = getSelectedTile() return tileset ~= nil and tileIndex ~= nil end, onclick = showPropertyEditor } log("Command registered in Sprite menu") log("=== PLUGIN INIT COMPLETE ===") end function exit(plugin) log("=== PLUGIN EXIT ===") end