Skip to content
microlith57 edited this page Jun 29, 2023 · 7 revisions

Tools are the items in the first section of the menu on the right-hand side of the interface; they are the primary way to interact with stuff in a map. It is possible to add custom tools, and they can do a wide variety of things — in effect, they act as input devices that are only active when selected in the menu.

The fundamental structure of a tool is as follows:

-- this require is optional but useful
local toolUtils = require("tool_utils")

-- the following is mandatory
local tool = {}

tool._type = "tool"

tool.name = "example_tool"
tool.group = "example"

return tool

The name of the tool is used to get its language keys (in this case tools.name.example_tool and tools.description.example_tool), and is used as-is for sorting. The group is used internally as the primary sort key; hence all tools with brush as their group are next to each other. Lönn's built in tools use the brush and placement group names.

Layers, Modes, and Materials

You do not actually have to implement any of these, but it is a good idea to at least implement materials, so that Lönn has something to fill the list with, and you aren't left with the previously selected tool's list.

Layers

Layers are used to distinguish between foreground vs. background tiles, entities vs. triggers vs. decals, and so on. If your tool uses layers, you can add:

tool.layer = "example_layer_a"
tool.validLayers = {
  "example_layer_a", "example_layer_b"
}

These are localised using language keys like layers.name.example_layer_a. Lönn will then automatically change the value of tool.layer when different layers are selected in the UI.

If you need some more advanced behaviour, you can optionally define functions to get and set the allowed layers:

function tool.getLayers()
    return {"example_layer_a", "example_layer_b"}
end

function tool.setLayer(layer, oldLayer)
    handler.layer = layer

    -- by default, lönn automatically switches material when switching layer, but the advanced function overrides this
    local materialValue = toolUtils.getPersistenceMaterial(handler, layer)
    if materialValue then
        -- for simplicity, the oldMaterial argument is left out here
        tool.setMaterial(materialValue)
    end

    return true
end

-- this one doesn't seem as useful as the first two
function tool.getLayer()
    return tool.layer
end

Note that these are individually optional, so you can define none, some, or all of them. If you use all of the advanced functions, Lönn will ignore tool.layer and tool.validLayers.

Modes

These work very similarly to layers, with the main distinctions being that they are further down in the interface, and that the names involved are different:

tool.mode = "example_mode_a"
tool.modes = {
  "example_mode_a", "example_mode_b"
}

The language keys for modes are dependent on the tool name also, like tools.example_tool.modes.name.example_mode_a and tools.example_tool.modes.description.example_mode_a.

The advanced functions for modes work in the same way as for layers, but are called tool.getModes(), tool.setMode(mode, oldMode), and tool.getMode().

Materials

Materials are different to layers and modes in that you have to use the advanced functions for getting and setting materials — this is because it is expected that the materials available for a tool will depend on the map or editor state in some way. Also, there are no language keys involved; the material name is displayed directly in the UI. For example:

tool.material = "Material Display Name"

-- mandatory
function tool.getMaterials(layer)
    return {"Material Display Name", "Another Material"}
end

-- mandatory
function tool.setMaterial(material, oldMaterial)
    tool.material = material
end

-- optional
function tool.getMaterial()
    return tool.material
end

Attaching Data to Materials

This section is a work in progress!

Making your tool do something

Because tools are actually input devices under the hood, they can listen for any supported event, so they can do a lot of things. However, most people expect tools to do things when they click or drag on the map canvas, so those are the most important events for most tools to listen to.

You can listen for an event, for example mouseclicked, by creating a function with that name:

function tool.mouseclicked(x, y, button, istouch, pressed)
    -- it's a good idea to use these, rather than hardcoding which buttons do what
    local actionButton = configs.editor.toolActionButton
    local cloneButton = configs.editor.objectCloneButton

    -- you can do anything here
end

Click and Drag

This section is a work in progress!

Graphics

You can draw graphics by listening to the draw event, which you can do by making a tool.draw function. For example:

-- adapted from https://github.com/JaThePlayer/LoennScripts/blob/main/Loenn/tools/scripts.lua under the MIT license
-- copyright (c) 2021 JaThePlayer

local state = require("loaded_state")
local viewportHandler = require("viewport_handler")
local drawing = require("utils.drawing")

function tool.draw()
    local room = state.getSelectedRoom()

    if room then
        local px, py = viewportHandler.getRoomCoordinates(room)

        viewportHandler.drawRelativeTo(room.x, room.y, function()
            -- draw a box with a dot in it at the cursor position
            love.graphics.rectangle("line", px - 2.5, py - 2.5, 5, 5)
            love.graphics.rectangle("line", px, py, .1, .1)
        end)
    end
end

Brush-Like Tools

This section is a work in progress!