Your first plugin
The template plugin, file by file.
Every new plugin starts from the template: a blank plugin with the callbacks wired up and its settings saved for you. Unzip it into scripts, rename the my-plugin folder to your plugin's name, add that name to scripts\plugins.txt and start editing app.lua.
my-plugin folder.scripts/
my-plugin/
main.lua
app.lua
config/
config.lua
config_api.luamain.lua
The entry point. It runs once when the plugin starts: it loads the app, initializes it, and defines the callbacks the game calls, each handing off to the app. Add a callback here when you need one.
local app = require("app")
app:init()
function on_stop(reason) app:shutdown(reason) end
function on_tick() app:tick() end
function on_render() app:render() end
function on_key_pressed(key) return app:on_key_pressed(key) endapp.lua
The plugin itself. App is a table with methods: App.new() makes the one app, and every function defined with : gets it as self, so state kept in self lasts between ticks and frames.
local config_api = require("config.config_api")
local App = {}
App.__index = App
function App.new()
return setmetatable({}, App)
end
function App:init()
config_api.init()
end
function App:shutdown(reason)
end
function App:tick()
config_api.save()
end
function App:render()
end
function App:on_key_pressed(key)
if game_ui.text_box_has_focus() then
return false
end
return false
end
return App.new()initruns once, when the plugin starts (and again after every reload). It loads the settings.tickruns every game tick. Game logic goes here. It also saves the settings when they changed.renderruns every frame, and is the only placeui,gfxandgfx3dwork. Keep it light and do heavy work intickor a task.shutdownruns when the plugin stops. Undo any game settings the plugin changed.on_key_pressedgets every key press. Returntrueto keep the key from the game. The first check lets keys through while the player is typing in chat.
config/config.lua
The settings and their defaults. In the template it's empty:
return function()
return {}
endAdd a setting by adding it here with its default value. Only settings listed here are saved and loaded; see Settings for how saved values are merged.
return function()
return {
enabled = true,
range = 1200.0,
color = Color.hex("#4FC3F7C0"),
}
endconfig/config_api.lua
Loads, returns and saves the settings, on top of the config module. It usually needs no changes.
local defaults = require("config.config")
local config_api = {}
local live
function config_api.init()
live = config.init(defaults())
end
function config_api.save()
config.save()
end
function config_api.cfg()
return live
end
return config_apiAnywhere in the plugin, config_api.cfg() is the settings table: read a field to get a setting and assign one to change it. Changes are saved by themselves.
Adding a window
Draw from App:render. A window with a close button tied to a setting needs two settings in config/config.lua:
return function()
return {
show_window = true,
range = 1200.0,
}
endThen, in app.lua:
local cfg = config_api.cfg
function App:render()
if not cfg().show_window then return end
ui.set_next_window_size(Vec2(360, 200), ui.Cond.FirstUseEver)
local open, visible = ui.begin_window(script.name, true)
if visible then
ui.text("Hello from " .. script.name)
local changed
changed, cfg().range = ui.slider_float("Range", cfg().range, 100, 1500, "%.0f")
end
ui.end_window()
if open == false then
cfg().show_window = false
end
endSave the file and press reload on the plugin's row to see it. Everything a plugin prints with print shows up in the Lua Console.