Skip to content
Vexatos edited this page Jun 27, 2023 · 21 revisions

Lönn is written in Lua using the LÖVE engine. Consequently, all Lönn plugins are written in Lua (specifically, Lua 5.1). Within a plugin, you have access to the Lua standard library, the functions provided by LÖVE and Selene, as well as a number of utility functions provided by Lönn itself in the utils package. When developing plugins, it is always a good idea to refer to the Lua reference manual when needed.


Lönn plugins are structured identically to any Lua library. A Lönn plugin is a file returning a table with all relevant fields and functions that Lönn can access.

local myobject = {}

return myobject

Lönn expects to find specific data in such a table, depending on the type of plugin that it is. All Lönn plugins must be placed in the Loenn subdirectory of your mod. Inside, the subdirectory determines the type of plugin. Valid subdirectories are:

  • entities
  • triggers
  • effects
  • lang
  • libraries
  • tools
  • scenes
  • ui/windows
  • ui/forms/fields
  • ui/themes

Among these, the lang directory is special as it contains your language files rather than Lönn plugins. Lönn will use the language file to make keys specified in your Lönn plugins more human-readable. Defining the placement names in the language file is required for entity and trigger placements to appear with a sensible name, and makes sure they will appear properly on the Custom Entity List.

Within each subdirectory, you may create further subdirectories with any names you want, should you wish to organize your plugins.

Making a Trigger

The most basic trigger contains two things:

local birdPath = {}

birdPath.name = "birdPathTrigger"
birdPath.placements = {
    name = "bird_path"
}

return birdPath

The name refers to the name of the entity or trigger as defined in the mod. The placements table is what Lönn uses to create entries in the entity or trigger list in the placement tool. Each line in the list is considered its own placement, and a plugin may add more than one placement. If only one placement is added, the name in the placement refers to the key Lönn looks up in the language file to access the human-readable version of this placement's name which will be displayed in the placement list. If there is no matching language file key, the placement will be displayed with a fallback name.

If you want to have more than one placement for an entity or trigger, you make placements a table of tables, where each inner table has the same structure as the placements table in the example above.

Another thing that a placement usually contains is data, which specifies the default attributes that the entity or trigger is placed with. It also specifies the names of the attributes (as the keys of the data table). These keys will also be looked up in the language file, but should not language entry be found, a best guess will be made to humanize the attribute name.

local cameraOffset = {}

cameraOffset.name = "cameraOffsetTrigger"
cameraOffset.placements = {
    name = "camera_offset",
    data = {
        cameraX = 0.0,
        cameraY = 0.0
    }
}

return cameraOffset

Data attributes can be customized to a great extent. Other fields are used to further change the behaviour of triggers, their attributes, nodes, the way they render, and more. For details, refer to the trigger documentation.

The best way to learn is to learn from examples! Refer to existing trigger plugins for what you can do.

Making an Entity

An entity plugin is structurally identical to a trigger plugin, except that entities always have to specify how they are displayed, while triggers render as a rectangle by default.

local theoCrystalPedestal = {}

theoCrystalPedestal.name = "theoCrystalPedestal"
theoCrystalPedestal.depth = 8998
theoCrystalPedestal.texture = "characters/theoCrystal/pedestal"
theoCrystalPedestal.justification = {0.5, 1.0}
theoCrystalPedestal.placements = {
    name = "pedestal",
}

return theoCrystalPedestal

The purpose of name and placements is identical to what they do in triggers, including the language file usage. depth specifies the render depth, a number used to determine which entities render above or below others. texture specifies the path to the entity texture relative to the Atlases/Gameplay directory. justification is a pair of numbers between 0 and 1 specifying where the centre of the entity lies. This is used to visually offset the texture so that it can look identical to what it looks like in-game.

Note that any field specified in a plugin as a value may also be defined as a function returning said field, allowing for greater customization. In the example below, the texture field is not a texture path but a function returning a texture path depending on the entity's attributes. The exact function signatures may be found in the entity documentation. This example also shows how to create multiple placements for a single entity.

local refill = {}

refill.name = "refill"
refill.depth = -100
refill.placements = {
    {
        name = "one_dash",
        data = {
            oneUse = false,
            twoDash = false
        }
    },
    {
        name = "two_dashes",
        data = {
            oneUse = false,
            twoDash = true
        }
    }
}

function refill.texture(room, entity)
    return entity.twoDash and "objects/refillTwo/idle00" or "objects/refill/idle00"
end

return refill

Other fields are used to further change the behaviour of entities, their attributes, nodes, the way they render, and more. For details, refer to the entity documentation.

The best way to learn is to learn from examples! Refer to existing entity plugins for what you can do.