-
Notifications
You must be signed in to change notification settings - Fork 21
Custom Tools
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.
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 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
.
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 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
This section is a work in progress!
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
This section is a work in progress!
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
This section is a work in progress!